Browse Source

Merge "qcacmn: WEP bit set for RAW Mode Workaround"

Linux Build Service Account 7 years ago
parent
commit
f6a858edbf

+ 2 - 0
dp/inc/cdp_txrx_cmn_struct.h

@@ -549,6 +549,7 @@ enum cdp_pdev_param_type {
  * @CDP_ENABLE_PROXYSTA: proxy sta
  * @CDP_UPDATE_TDLS_FLAGS: tdls link flags
  * @CDP_ENABLE_AP_BRIDGE: set ap_bridging enable/disable
+ * @CDP_ENABLE_CIPHER : set cipher type based on security
  */
 enum cdp_vdev_param_type {
 	CDP_ENABLE_NAWDS,
@@ -558,6 +559,7 @@ enum cdp_vdev_param_type {
 	CDP_UPDATE_TDLS_FLAGS,
 	CDP_CFG_WDS_AGING_TIMER,
 	CDP_ENABLE_AP_BRIDGE,
+	CDP_ENABLE_CIPHER
 };
 
 #define TXRX_FW_STATS_TXSTATS                     1

+ 4 - 0
dp/wifi3.0/dp_main.c

@@ -2726,6 +2726,7 @@ static struct cdp_vdev *dp_vdev_attach_wifi3(struct cdp_pdev *txrx_pdev,
 	vdev->delete.pending = 0;
 	vdev->safemode = 0;
 	vdev->drop_unenc = 1;
+	vdev->sec_type = cdp_sec_type_none;
 #ifdef notyet
 	vdev->filters_num = 0;
 #endif
@@ -4817,6 +4818,9 @@ static void dp_set_vdev_param(struct cdp_vdev *vdev_handle,
 		else
 			vdev->ap_bridge_enabled = false;
 		break;
+	case CDP_ENABLE_CIPHER:
+		vdev->sec_type = val;
+		break;
 	default:
 		break;
 	}

+ 19 - 3
dp/wifi3.0/dp_tx.c

@@ -55,6 +55,22 @@
 /* invalid peer id for reinject*/
 #define DP_INVALID_PEER 0XFFFE
 
+/*mapping between hal encrypt type and cdp_sec_type*/
+#define MAX_CDP_SEC_TYPE 12
+static const uint8_t sec_type_map[MAX_CDP_SEC_TYPE] = {
+					HAL_TX_ENCRYPT_TYPE_NO_CIPHER,
+					HAL_TX_ENCRYPT_TYPE_WEP_128,
+					HAL_TX_ENCRYPT_TYPE_WEP_104,
+					HAL_TX_ENCRYPT_TYPE_WEP_40,
+					HAL_TX_ENCRYPT_TYPE_TKIP_WITH_MIC,
+					HAL_TX_ENCRYPT_TYPE_TKIP_NO_MIC,
+					HAL_TX_ENCRYPT_TYPE_AES_CCMP_128,
+					HAL_TX_ENCRYPT_TYPE_WAPI,
+					HAL_TX_ENCRYPT_TYPE_AES_CCMP_256,
+					HAL_TX_ENCRYPT_TYPE_AES_GCMP_128,
+					HAL_TX_ENCRYPT_TYPE_AES_GCMP_256,
+					HAL_TX_ENCRYPT_TYPE_WAPI_GCM_SM4};
+
 /**
  * dp_tx_get_queue() - Returns Tx queue IDs to be used for this Tx frame
  * @vdev: DP Virtual device handle
@@ -716,10 +732,8 @@ static qdf_nbuf_t dp_tx_prepare_raw(struct dp_vdev *vdev, qdf_nbuf_t nbuf,
 	DP_STATS_INC_PKT(vdev, tx_i.raw.raw_pkt, 1, qdf_nbuf_len(nbuf));
 
 	/* SWAR for HW: Enable WEP bit in the AMSDU frames for RAW mode */
-	if ((qos_wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
-			&& (qos_wh->i_qos[0] & IEEE80211_QOS_AMSDU)) {
+	if (qos_wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
 		qos_wh->i_fc[1] |= IEEE80211_FC1_WEP;
-	}
 
 	if (QDF_STATUS_SUCCESS != qdf_nbuf_map(vdev->osdev, nbuf,
 				QDF_DMA_TO_DEVICE)) {
@@ -800,6 +814,8 @@ static QDF_STATUS dp_tx_hw_enqueue(struct dp_soc *soc, struct dp_vdev *vdev,
 	hal_tx_desc_set_encap_type(hal_tx_desc_cached, tx_desc->tx_encap_type);
 	hal_tx_desc_set_dscp_tid_table_id(hal_tx_desc_cached,
 			vdev->dscp_tid_map_id);
+	hal_tx_desc_set_encrypt_type(hal_tx_desc_cached,
+			sec_type_map[vdev->sec_type]);
 
 	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
 			"%s length:%d , type = %d, dma_addr %llx, offset %d desc id %u",

+ 2 - 0
dp/wifi3.0/dp_types.h

@@ -1192,6 +1192,8 @@ struct dp_vdev {
 	/* AP BRIDGE enabled */
 	uint32_t ap_bridge_enabled;
 
+	enum cdp_sec_type  sec_type;
+
 };
 
 

+ 2 - 0
hif/inc/hif.h

@@ -52,6 +52,8 @@ extern "C" {
 typedef void __iomem *A_target_id_t;
 typedef void *hif_handle_t;
 
+#define HIF_DBG_PRINT_RATE 1000
+
 #define HIF_TYPE_AR6002   2
 #define HIF_TYPE_AR6003   3
 #define HIF_TYPE_AR6004   5

+ 4 - 2
hif/src/sdio/hif_sdio_dev.c

@@ -190,8 +190,10 @@ HTC_PACKET *hif_dev_alloc_rx_buffer(struct hif_sdio_device *pdev)
 	headsize = sizeof(HTC_PACKET);
 	netbuf = qdf_nbuf_alloc(NULL, bufsize + headsize, 0, 4, false);
 	if (netbuf == NULL) {
-		AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
-				("(%s)Allocate netbuf failed\n", __func__));
+		QDF_TRACE_RATE_LIMITED(HIF_DBG_PRINT_RATE, QDF_MODULE_ID_HIF,
+				       QDF_TRACE_LEVEL_ERROR,
+				       "(%s)Allocate netbuf failed\n",
+				       __func__);
 		return NULL;
 	}
 	packet = (HTC_PACKET *) qdf_nbuf_data(netbuf);

+ 14 - 1
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

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

+ 2 - 1
qdf/linux/src/qdf_nbuf.c

@@ -400,7 +400,8 @@ struct sk_buff *__qdf_nbuf_alloc(qdf_device_t osdev, size_t size, int reserve,
 	skb = pld_nbuf_pre_alloc(size);
 
 	if (!skb) {
-		pr_info("ERROR:NBUF alloc failed\n");
+		pr_err_ratelimited("ERROR:NBUF alloc failed, size = %zu\n",
+				   size);
 		__qdf_nbuf_start_replenish_timer();
 		return NULL;
 	} else {

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

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

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

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

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

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