Преглед изворни кода

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
Frank Liu пре 7 година
родитељ
комит
7fc7beb463

+ 148 - 1
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;
+}

+ 20 - 0
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

+ 2 - 0
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);

+ 20 - 0
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

+ 10 - 0
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

+ 40 - 0
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;
+}