From 283a2f4221cdef1b2688f7867adca880b0265137 Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Fri, 21 Jul 2017 18:02:02 +0800 Subject: [PATCH] qcacmn: Dynamic antenna switch in TDLS operation Support dynamic antenna switch in TDLS: 1. If TDLS NSS is configured to be 1x1, TDLS connections not teardown; 2. When antenna mode switched from 2x2 to 1x1, TDLS connections teared down and EAGAIN returned; When antenna mode switched from 1x1 to 2x2, TDLS connections is still teardown, but success in one trial. Change-Id: I1877002122a96dc8f40c796f8a1b938199d3b67a CRs-Fixed: 2080461 --- os_if/linux/tdls/inc/wlan_cfg80211_tdls.h | 15 +- os_if/linux/tdls/src/wlan_cfg80211_tdls.c | 45 ++++++ umac/tdls/core/src/wlan_tdls_cmds_process.c | 149 +++++++++++++++++- umac/tdls/core/src/wlan_tdls_cmds_process.h | 20 +++ umac/tdls/core/src/wlan_tdls_main.c | 2 + .../dispatcher/inc/wlan_tdls_public_structs.h | 20 +++ umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h | 10 ++ umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c | 40 +++++ 8 files changed, 299 insertions(+), 2 deletions(-) diff --git a/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h b/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h index 7c313cf3d494..42adf4fec767 100644 --- a/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h +++ b/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h @@ -44,11 +44,13 @@ * @tdls_mgmt_comp: Completion to send tdls mgmt packets * @tdls_link_establish_req_comp: Completion to establish link, sync to * send establish params to firmware, not used today. - * @tdls_teardown_comp: tdls teardown completion event + * @tdls_teardown_comp: Completion to teardown tdls peer * @tdls_user_cmd_comp: tdls user command completion event + * @tdls_antenna_switch_comp: Completion to switch antenna * @tdls_add_peer_status: Peer status after add peer * @mgmt_tx_completion_status: Tdls mgmt frames TX completion status code * @tdls_user_cmd_len: tdls user command written buffer length + * @tdls_antenna_switch_status: return status after antenna switch */ struct osif_tdls_vdev { struct completion tdls_add_peer_comp; @@ -57,9 +59,11 @@ struct osif_tdls_vdev { struct completion tdls_link_establish_req_comp; struct completion tdls_teardown_comp; struct completion tdls_user_cmd_comp; + struct completion tdls_antenna_switch_comp; QDF_STATUS tdls_add_peer_status; uint32_t mgmt_tx_completion_status; uint32_t tdls_user_cmd_len; + int tdls_antenna_switch_status; }; /** @@ -183,6 +187,15 @@ int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_pdev *pdev, uint16_t status_code, uint32_t peer_capability, const uint8_t *buf, size_t len); +/** + * wlan_tdls_antenna_switch() - process tdls antenna switch + * @vdev: vdev object + * @mode: antenna mode + * + * Return: 0 on success; -EAGAIN to retry + */ +int wlan_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, uint32_t mode); + /** * wlan_cfg80211_tdls_event_callback() - callback for tdls module * @userdata: user data diff --git a/os_if/linux/tdls/src/wlan_cfg80211_tdls.c b/os_if/linux/tdls/src/wlan_cfg80211_tdls.c index 03614e6874d1..7ff9690610fa 100644 --- a/os_if/linux/tdls/src/wlan_cfg80211_tdls.c +++ b/os_if/linux/tdls/src/wlan_cfg80211_tdls.c @@ -68,6 +68,7 @@ QDF_STATUS wlan_cfg80211_tdls_priv_init(struct vdev_osif_priv *osif_priv) init_completion(&tdls_priv->tdls_link_establish_req_comp); init_completion(&tdls_priv->tdls_teardown_comp); init_completion(&tdls_priv->tdls_user_cmd_comp); + init_completion(&tdls_priv->tdls_antenna_switch_comp); osif_priv->osif_tdls = tdls_priv; @@ -814,6 +815,47 @@ error_mgmt_req: return status; } +int wlan_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, uint32_t mode) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int ret; + unsigned long rc; + + if (!vdev) { + cfg80211_err("vdev is NULL"); + return -EAGAIN; + } + wlan_objmgr_vdev_get_ref(vdev, WLAN_OSIF_ID); + + osif_priv = wlan_vdev_get_ospriv(vdev); + tdls_priv = osif_priv->osif_tdls; + + reinit_completion(&tdls_priv->tdls_antenna_switch_comp); + ret = ucfg_tdls_antenna_switch(vdev, mode); + if (QDF_IS_STATUS_ERROR(ret)) { + cfg80211_err("ucfg_tdls_antenna_switch failed err %d", ret); + ret = -EAGAIN; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_antenna_switch_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_ANTENNA_SWITCH)); + if (!rc) { + cfg80211_err("timeout for tdls antenna switch %ld", rc); + ret = -EAGAIN; + goto error; + } + + ret = tdls_priv->tdls_antenna_switch_status; + cfg80211_debug("tdls antenna switch status:%d", ret); +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + + return ret; +} + static void wlan_cfg80211_tdls_indicate_discovery(struct tdls_osif_indication *ind) { @@ -896,6 +938,9 @@ void wlan_cfg80211_tdls_event_callback(void *user_data, complete(&tdls_priv->tdls_user_cmd_comp); break; + case TDLS_EVENT_ANTENNA_SWITCH: + tdls_priv->tdls_antenna_switch_status = ind->status; + complete(&tdls_priv->tdls_antenna_switch_comp); default: break; } diff --git a/umac/tdls/core/src/wlan_tdls_cmds_process.c b/umac/tdls/core/src/wlan_tdls_cmds_process.c index 6780f0a2c237..e934f4f49a0e 100644 --- a/umac/tdls/core/src/wlan_tdls_cmds_process.c +++ b/umac/tdls/core/src/wlan_tdls_cmds_process.c @@ -29,6 +29,7 @@ #include "wlan_tdls_mgmt.h" #include "wlan_tdls_cmds_process.h" #include "wlan_tdls_tgt_api.h" +#include "wlan_policy_mgr_api.h" static uint16_t tdls_get_connected_peer(struct tdls_soc_priv_obj *soc_obj) { @@ -567,7 +568,7 @@ void tdls_reset_nss(struct tdls_soc_priv_obj *tdls_soc, return; if (TDLS_TEARDOWN != action_code || - tdls_soc->tdls_nss_switch_in_progress) + !tdls_soc->tdls_nss_switch_in_progress) return; if (tdls_soc->tdls_teardown_peers_cnt != 0) @@ -589,6 +590,8 @@ void tdls_reset_nss(struct tdls_soc_priv_obj *tdls_soc, tdls_notice("teardown done & NSS switch in progress"); tdls_soc->tdls_nss_teardown_complete = true; } + tdls_soc->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_UNKNOWN; } } @@ -2182,3 +2185,147 @@ int tdls_set_responder(struct tdls_set_responder_req *set_req) return status; } +static int tdls_teardown_links(struct tdls_soc_priv_obj *soc_obj, uint32_t mode) +{ + uint8_t staidx; + struct tdls_peer *curr_peer; + struct tdls_conn_info *conn_rec; + int ret = 0; + + conn_rec = soc_obj->tdls_conn_info; + for (staidx = 0; staidx < soc_obj->max_num_tdls_sta; staidx++) { + if (conn_rec[staidx].sta_id == 0) + continue; + + curr_peer = tdls_find_all_peer(soc_obj, + conn_rec[staidx].peer_mac.bytes); + if (!curr_peer) + continue; + + /* if supported only 1x1, skip it */ + if (curr_peer->spatial_streams == HW_MODE_SS_1x1) + continue; + + tdls_debug("Indicate TDLS teardown (staId %d)", + curr_peer->sta_id); + tdls_indicate_teardown(curr_peer->vdev_priv, curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + + soc_obj->tdls_teardown_peers_cnt++; + } + + if (soc_obj->tdls_teardown_peers_cnt >= 1) { + soc_obj->tdls_nss_switch_in_progress = true; + tdls_debug("TDLS peers to be torn down = %d", + soc_obj->tdls_teardown_peers_cnt); + + /* set the antenna switch transition mode */ + if (mode == HW_MODE_SS_1x1) { + soc_obj->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_2x2_to_1x1; + ret = -EAGAIN; + } else { + soc_obj->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_1x1_to_2x2; + ret = 0; + } + tdls_debug("TDLS teardown for antenna switch operation starts"); + } + + return ret; +} + +QDF_STATUS tdls_process_antenna_switch(struct tdls_antenna_switch_request *req) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_vdev *vdev = NULL; + uint32_t vdev_nss; + int ant_switch_state = 0; + uint32_t vdev_id; + enum QDF_OPMODE opmode; + uint8_t channel; + struct tdls_osif_indication ind; + + if (!req || !req->vdev) { + tdls_err("req: %p", req); + status = QDF_STATUS_E_INVAL; + goto error; + } + + vdev = req->vdev; + status = tdls_get_vdev_objects(vdev, &vdev_obj, &soc_obj); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev_obj & soc_obj"); + goto error; + } + + if (soc_obj->connected_peer_count == 0) + goto ant_sw_done; + + if (soc_obj->tdls_nss_switch_in_progress) { + if (!soc_obj->tdls_nss_teardown_complete) { + tdls_err("TDLS antenna switch is in progress"); + goto error; + } else { + goto ant_sw_done; + } + } + + vdev_id = wlan_vdev_get_id(vdev); + opmode = wlan_vdev_mlme_get_opmode(vdev); + channel = policy_mgr_get_channel(soc_obj->soc, opmode, &vdev_id); + + /* Check supported nss for TDLS, if is 1x1, no need to teardown links */ + if (WLAN_REG_IS_24GHZ_CH(channel)) + vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_2g; + else + vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_5g; + + if (vdev_nss == HW_MODE_SS_1x1) { + tdls_debug("Supported NSS is 1x1, no need to teardown TDLS links"); + goto ant_sw_done; + } + + if (tdls_teardown_links(soc_obj, req->mode) == 0) + goto ant_sw_done; + +error: + ant_switch_state = -EAGAIN; +ant_sw_done: + if (soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = ant_switch_state; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ANTENNA_SWITCH, &ind); + } + + if (soc_obj->tdls_nss_switch_in_progress && + soc_obj->tdls_nss_teardown_complete) { + soc_obj->tdls_nss_switch_in_progress = false; + soc_obj->tdls_nss_teardown_complete = false; + } + tdls_debug("tdls_nss_switch_in_progress: %d tdls_nss_teardown_complete: %d", + soc_obj->tdls_nss_switch_in_progress, + soc_obj->tdls_nss_teardown_complete); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + return status; +} + +QDF_STATUS tdls_antenna_switch_flush_callback(struct scheduler_msg *msg) +{ + struct tdls_antenna_switch_request *req; + + if (!msg || !msg->bodyptr) { + tdls_err("msg: 0x%pK, bodyptr: 0x%pK", msg, msg->bodyptr); + return QDF_STATUS_E_NULL_VALUE; + } + req = msg->bodyptr; + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return QDF_STATUS_SUCCESS; +} diff --git a/umac/tdls/core/src/wlan_tdls_cmds_process.h b/umac/tdls/core/src/wlan_tdls_cmds_process.h index 561a40d78b34..10e20cfa1b5e 100644 --- a/umac/tdls/core/src/wlan_tdls_cmds_process.h +++ b/umac/tdls/core/src/wlan_tdls_cmds_process.h @@ -249,6 +249,26 @@ QDF_STATUS tdls_process_remove_force_peer(struct tdls_oper_request *req); */ QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req); +/** + * tdls_process_antenna_switch() - handle TDLS antenna switch + * @req: TDLS antenna switch request + * + * Rely on callback to indicate the antenna switch state to caller. + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed. + */ +QDF_STATUS tdls_process_antenna_switch(struct tdls_antenna_switch_request *req); + +/** + * tdls_antenna_switch_flush_callback() - flush TDLS antenna switch request + * @msg: scheduler message contains tdls antenna switch event + * + * This function call is invoked when scheduler thread is going down + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_antenna_switch_flush_callback(struct scheduler_msg *msg); + /** * tdls_pe_del_peer() - send TDLS delete peer request to PE * @req: TDLS delete peer request diff --git a/umac/tdls/core/src/wlan_tdls_main.c b/umac/tdls/core/src/wlan_tdls_main.c index a423dcd08a05..a269a95cc6ba 100644 --- a/umac/tdls/core/src/wlan_tdls_main.c +++ b/umac/tdls/core/src/wlan_tdls_main.c @@ -292,6 +292,8 @@ QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg) break; case TDLS_NOTIFY_RESET_ADAPTERS: tdls_notify_reset_adapter(msg->bodyptr); + case TDLS_CMD_ANTENNA_SWITCH: + tdls_process_antenna_switch(msg->bodyptr); break; case TDLS_CMD_GET_ALL_PEERS: tdls_get_all_peers_from_list(msg->bodyptr); diff --git a/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h b/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h index ae0a4077b247..457a3ded556b 100644 --- a/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h +++ b/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h @@ -68,6 +68,9 @@ /** Maximum waittime for TDLS teardown links **/ #define WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS 10000 +/** Maximum waittime for TDLS antenna switch **/ +#define WAIT_TIME_FOR_TDLS_ANTENNA_SWITCH 1000 + #define TDLS_TEARDOWN_PEER_UNREACHABLE 25 #define TDLS_TEARDOWN_PEER_UNSPEC_REASON 26 @@ -185,6 +188,7 @@ enum tdls_feature_mode { * @TDLS_CMD_TEARDOWN_LINKS: notify teardown * @TDLS_NOTIFY_RESET_ADAPTERS: notify adapater reset * @TDLS_CMD_GET_ALL_PEERS: get all the tdls peers from the list + * @TDLS_CMD_ANTENNA_SWITCH: dynamic tdls antenna switch */ enum tdls_command_type { TDLS_CMD_TX_ACTION = 1, @@ -206,6 +210,7 @@ enum tdls_command_type { TDLS_CMD_TEARDOWN_LINKS, TDLS_NOTIFY_RESET_ADAPTERS, TDLS_CMD_GET_ALL_PEERS, + TDLS_CMD_ANTENNA_SWITCH }; /** @@ -220,6 +225,7 @@ enum tdls_command_type { * @TDLS_EVENT_SETUP_REQ: setup request * @TDLS_EVENT_TEARDOWN_LINKS_DONE: teardown completion event * @TDLS_EVENT_USER_CMD: tdls user command + * @TDLS_EVENT_ANTENNA_SWITCH: antenna switch event */ enum tdls_event_type { TDLS_EVENT_VDEV_STATE_CHANGE = 0, @@ -232,6 +238,7 @@ enum tdls_event_type { TDLS_EVENT_SETUP_REQ, TDLS_EVENT_TEARDOWN_LINKS_DONE, TDLS_EVENT_USER_CMD, + TDLS_EVENT_ANTENNA_SWITCH, }; /** @@ -417,6 +424,8 @@ enum tdls_feature_bit { * @tdls_pre_off_chan_bw: tdls off channel bandwidth * @tdls_peer_kickout_threshold: sta kickout threshold for tdls peer * @delayed_trig_framint: delayed trigger frame interval + * @tdls_vdev_nss_2g: tdls NSS setting for 2G band + * @tdls_vdev_nss_5g: tdls NSS setting for 5G band */ struct tdls_user_config { uint32_t tdls_tx_states_period; @@ -437,6 +446,8 @@ struct tdls_user_config { uint32_t tdls_pre_off_chan_bw; uint32_t tdls_peer_kickout_threshold; uint32_t delayed_trig_framint; + uint8_t tdls_vdev_nss_2g; + uint8_t tdls_vdev_nss_5g; }; /** @@ -1066,4 +1077,13 @@ struct tdls_del_all_tdls_peers { struct qdf_mac_addr bssid; }; +/** + * struct tdls_antenna_switch_request - TDLS antenna switch request + * @vdev: vdev object + * @mode: antenna mode, 1x1 or 2x2 + */ +struct tdls_antenna_switch_request { + struct wlan_objmgr_vdev *vdev; + uint32_t mode; +}; #endif diff --git a/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h b/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h index f23bf52aebee..ffe3404dc6b6 100644 --- a/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h +++ b/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h @@ -212,4 +212,14 @@ void ucfg_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, void ucfg_tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, struct qdf_mac_addr *mac_addr); +/** + * ucfg_tdls_antenna_switch() - tdls antenna switch + * @vdev: tdls vdev object + * @mode: antenna mode + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, + uint32_t mode); + #endif diff --git a/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c b/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c index 36c6318156f5..7ed904bd3ae0 100644 --- a/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c +++ b/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c @@ -742,3 +742,43 @@ void ucfg_tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, } +QDF_STATUS ucfg_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, + uint32_t mode) +{ + QDF_STATUS status; + struct tdls_antenna_switch_request *req; + struct scheduler_msg msg = {0, }; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto error; + } + + req->vdev = vdev; + req->mode = mode; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_antenna_switch_flush_callback; + msg.type = TDLS_CMD_ANTENNA_SWITCH; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post antenna switch msg fail"); + goto dec_ref; + } + + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +error: + qdf_mem_free(req); + return status; +}