Browse Source

qcacmn: TDLS component: core peer operations

TDLS peer operations: add, delete and update TDLS peer

Change-Id: I7dba1a4b2b3b8597935db811da68ee5ba630bf4d
CRs-Fixed: 2011330
Frank Liu 8 years ago
parent
commit
d4b12dbff6

+ 1148 - 0
umac/tdls/core/src/wlan_tdls_cmds_process.c

@@ -21,3 +21,1151 @@
  *
  * TDLS north bound commands implementation
  */
+#include <wlan_serialization_api.h>
+#include "wlan_tdls_main.h"
+#include "wlan_tdls_peer.h"
+#include "wlan_tdls_cmds_process.h"
+
+static uint16_t tdls_get_connected_peer(struct tdls_soc_priv_obj *soc_obj)
+{
+	return soc_obj->connected_peer_count;
+}
+
+/**
+ * tdls_decrement_peer_count() - decrement connected TDLS peer counter
+ * @soc_obj: TDLS soc object
+ *
+ * Return: None.
+ */
+static void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj)
+{
+	if (soc_obj->connected_peer_count)
+		soc_obj->connected_peer_count--;
+
+	tdls_debug("Connected peer count %d", soc_obj->connected_peer_count);
+}
+
+/**
+ * tdls_validate_current_mode() - check current TDL mode
+ * @soc_obj: TDLS soc object
+ *
+ * Return: QDF_STATUS_SUCCESS if TDLS enabled, other for disabled
+ */
+static QDF_STATUS tdls_validate_current_mode(struct tdls_soc_priv_obj *soc_obj)
+{
+	if (soc_obj->tdls_current_mode == TDLS_SUPPORT_DISABLED ||
+	    soc_obj->tdls_current_mode == TDLS_SUPPORT_SUSPENDED) {
+		tdls_err("TDLS mode disabled OR not enabled, current mode %d",
+			 soc_obj->tdls_current_mode);
+		return QDF_STATUS_E_NOSUPPORT;
+	}
+	return QDF_STATUS_SUCCESS;
+}
+
+static char *tdls_get_ser_cmd_str(enum  wlan_serialization_cmd_type type)
+{
+	switch (type) {
+	case WLAN_SER_CMD_TDLS_ADD_PEER:
+		return "TDLS_ADD_PEER_CMD";
+	case WLAN_SER_CMD_TDLS_DEL_PEER:
+		return "TDLS_DEL_PEER_CMD";
+	case WLAN_SER_CMD_TDLS_SEND_MGMT:
+		return "TDLS_SEND_MGMT_CMD";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static void
+tdls_release_serialization_command(struct wlan_objmgr_vdev *vdev,
+				   enum wlan_serialization_cmd_type type)
+{
+	struct wlan_serialization_queued_cmd_info cmd = {0};
+
+	cmd.cmd_type = type;
+	cmd.cmd_id = 0;
+	cmd.vdev = vdev;
+
+	tdls_debug("release %s", tdls_get_ser_cmd_str(type));
+	/* Inform serialization for command completion */
+	wlan_serialization_remove_cmd(&cmd);
+}
+
+/**
+ * tdls_pe_add_peer() - send TDLS add peer request to PE
+ * @req: TDL add peer request
+ *
+ * Return: QDF_STATUS_SUCCESS for success; other values if failed
+ */
+static QDF_STATUS tdls_pe_add_peer(struct tdls_add_peer_request *req)
+{
+	struct tdls_add_sta_req *addstareq;
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_peer *peer;
+	struct tdls_soc_priv_obj *soc_obj;
+	struct scheduler_msg msg = {0,};
+	QDF_STATUS status;
+
+	addstareq = qdf_mem_malloc(sizeof(*addstareq));
+	if (!addstareq) {
+		tdls_err("allocate failed");
+		return QDF_STATUS_E_NOMEM;
+	}
+	vdev = req->vdev;
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	if (!soc_obj) {
+		tdls_err("NULL tdls soc object");
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+
+	addstareq->tdls_oper = TDLS_OPER_ADD;
+	addstareq->transaction_id = 0;
+
+	wlan_vdev_obj_lock(vdev);
+	addstareq->session_id = wlan_vdev_get_id(vdev);
+	peer = wlan_vdev_get_bsspeer(vdev);
+	if (!peer) {
+		wlan_vdev_obj_unlock(vdev);
+		tdls_err("bss peer is NULL");
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	wlan_vdev_obj_unlock(vdev);
+	status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("can't get bss peer");
+		goto error;
+	}
+	wlan_peer_obj_lock(peer);
+	qdf_mem_copy(addstareq->bssid.bytes,
+		     wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE);
+	wlan_peer_obj_unlock(peer);
+	wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID);
+	qdf_mem_copy(addstareq->peermac.bytes, req->add_peer_req.peer_addr,
+		     QDF_MAC_ADDR_SIZE);
+
+	tdls_debug("for " QDF_MAC_ADDRESS_STR,
+		   QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes));
+	msg.type = soc_obj->tdls_add_sta_req;
+	msg.bodyptr = addstareq;
+	status = scheduler_post_msg(QDF_MODULE_ID_PE, &msg);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("fail to post pe msg to add peer");
+		goto error;
+	}
+	return status;
+error:
+	qdf_mem_free(addstareq);
+	return status;
+}
+
+/**
+ * tdls_pe_del_peer() - send TDLS delete peer request to PE
+ * @req: TDLS delete peer request
+ *
+ * Return: QDF_STATUS_SUCCESS for success; other values if failed
+ */
+QDF_STATUS tdls_pe_del_peer(struct tdls_del_peer_request *req)
+{
+	struct tdls_del_sta_req *delstareq;
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_peer *peer;
+	struct tdls_soc_priv_obj *soc_obj;
+	struct scheduler_msg msg = {0,};
+	QDF_STATUS status;
+
+	delstareq = qdf_mem_malloc(sizeof(*delstareq));
+	if (!delstareq) {
+		tdls_err("allocate failed");
+		return QDF_STATUS_E_NOMEM;
+	}
+	vdev = req->vdev;
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	if (!soc_obj) {
+		tdls_err("NULL tdls soc object");
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+
+	delstareq->transaction_id = 0;
+
+	wlan_vdev_obj_lock(vdev);
+	delstareq->session_id = wlan_vdev_get_id(vdev);
+	peer = wlan_vdev_get_bsspeer(vdev);
+	if (!peer) {
+		wlan_vdev_obj_unlock(vdev);
+		tdls_err("bss peer is NULL");
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	wlan_vdev_obj_unlock(vdev);
+	status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("can't get bss peer");
+		goto error;
+	}
+	wlan_peer_obj_lock(peer);
+	qdf_mem_copy(delstareq->bssid.bytes,
+		     wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE);
+	wlan_peer_obj_unlock(peer);
+	wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID);
+	qdf_mem_copy(delstareq->peermac.bytes, req->del_peer_req.peer_addr,
+		     QDF_MAC_ADDR_SIZE);
+
+	tdls_debug("for " QDF_MAC_ADDRESS_STR,
+		   QDF_MAC_ADDR_ARRAY(delstareq->peermac.bytes));
+	msg.type = soc_obj->tdls_del_sta_req;
+	msg.bodyptr = delstareq;
+	status = scheduler_post_msg(QDF_MODULE_ID_PE, &msg);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("fail to post pe msg to del peer");
+		goto error;
+	}
+	return status;
+error:
+	qdf_mem_free(delstareq);
+	return status;
+}
+
+/**
+ * tdls_pe_update_peer() - send TDLS update peer request to PE
+ * @req: TDLS update peer request
+ *
+ * Return: QDF_STATUS_SUCCESS for success; other values if failed
+ */
+static QDF_STATUS tdls_pe_update_peer(struct tdls_update_peer_request *req)
+{
+	struct tdls_add_sta_req *addstareq;
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_peer *peer;
+	struct tdls_soc_priv_obj *soc_obj;
+	struct scheduler_msg msg = {0,};
+	struct tdls_update_peer_params *update_peer;
+	QDF_STATUS status;
+
+	addstareq = qdf_mem_malloc(sizeof(*addstareq));
+	if (!addstareq) {
+		tdls_err("allocate failed");
+		return QDF_STATUS_E_NOMEM;
+	}
+	vdev = req->vdev;
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	if (!soc_obj) {
+		tdls_err("NULL tdls soc object");
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	update_peer = &req->update_peer_req;
+
+	addstareq->tdls_oper = TDLS_OPER_UPDATE;
+	addstareq->transaction_id = 0;
+
+	wlan_vdev_obj_lock(vdev);
+	addstareq->session_id = wlan_vdev_get_id(vdev);
+	peer = wlan_vdev_get_bsspeer(vdev);
+	if (!peer) {
+		wlan_vdev_obj_unlock(vdev);
+		tdls_err("bss peer is NULL");
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	wlan_vdev_obj_unlock(vdev);
+	status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("can't get bss peer");
+		goto error;
+	}
+	wlan_peer_obj_lock(peer);
+	qdf_mem_copy(addstareq->bssid.bytes,
+		     wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE);
+	wlan_peer_obj_unlock(peer);
+	wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID);
+	qdf_mem_copy(addstareq->peermac.bytes, update_peer->peer_addr,
+		     QDF_MAC_ADDR_SIZE);
+	addstareq->capability = update_peer->capability;
+	addstareq->uapsd_queues = update_peer->uapsd_queues;
+	addstareq->max_sp = update_peer->max_sp;
+
+	qdf_mem_copy(addstareq->extn_capability,
+		     update_peer->extn_capability, WLAN_MAC_MAX_EXTN_CAP);
+	addstareq->htcap_present = update_peer->htcap_present;
+	qdf_mem_copy(&addstareq->ht_cap,
+		     &update_peer->ht_cap,
+		     sizeof(update_peer->ht_cap));
+	addstareq->vhtcap_present = update_peer->vhtcap_present;
+	qdf_mem_copy(&addstareq->vht_cap,
+		     &update_peer->vht_cap,
+		     sizeof(update_peer->vht_cap));
+	addstareq->supported_rates_length = update_peer->supported_rates_len;
+	qdf_mem_copy(&addstareq->supported_rates,
+		     update_peer->supported_rates,
+		     update_peer->supported_rates_len);
+	tdls_debug("for " QDF_MAC_ADDRESS_STR,
+		   QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes));
+
+	msg.type = soc_obj->tdls_add_sta_req;
+	msg.bodyptr = addstareq;
+	status = scheduler_post_msg(QDF_MODULE_ID_PE, &msg);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("fail to post pe msg to update peer");
+		goto error;
+	}
+	return status;
+error:
+	qdf_mem_free(addstareq);
+	return status;
+}
+
+static QDF_STATUS
+tdls_internal_add_peer_rsp(struct tdls_add_peer_request *req,
+			   QDF_STATUS status)
+{
+	struct tdls_soc_priv_obj *soc_obj;
+	struct wlan_objmgr_vdev *vdev;
+	struct tdls_osif_indication ind;
+	QDF_STATUS ret;
+
+	if (!req || !req->vdev) {
+		tdls_err("req: %p", req);
+		return QDF_STATUS_E_INVAL;
+	}
+	vdev = req->vdev;
+	ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID);
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		tdls_err("can't get vdev object");
+		return ret;
+	}
+
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	if (soc_obj && soc_obj->tdls_event_cb) {
+		ind.vdev = vdev;
+		ind.status = status;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_ADD_PEER, &ind);
+	}
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+tdls_internal_update_peer_rsp(struct tdls_update_peer_request *req,
+			      QDF_STATUS status)
+{
+	struct tdls_soc_priv_obj *soc_obj;
+	struct tdls_osif_indication ind;
+	struct wlan_objmgr_vdev *vdev;
+	QDF_STATUS ret;
+
+	if (!req || !req->vdev) {
+		tdls_err("req: %p", req);
+		return QDF_STATUS_E_INVAL;
+	}
+	vdev = req->vdev;
+	ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID);
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		tdls_err("can't get vdev object");
+		return ret;
+	}
+
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	if (soc_obj && soc_obj->tdls_event_cb) {
+		ind.vdev = vdev;
+		ind.status = status;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_ADD_PEER, &ind);
+	}
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS tdls_internal_del_peer_rsp(struct tdls_oper_request *req)
+{
+	struct tdls_soc_priv_obj *soc_obj;
+	struct tdls_osif_indication ind;
+	struct wlan_objmgr_vdev *vdev;
+	QDF_STATUS status;
+
+	if (!req || !req->vdev) {
+		tdls_err("req: %p", req);
+		return QDF_STATUS_E_INVAL;
+	}
+	vdev = req->vdev;
+	status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("can't get vdev object");
+		return status;
+	}
+
+	soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev);
+	if (soc_obj && soc_obj->tdls_event_cb) {
+		ind.vdev = req->vdev;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_DEL_PEER, &ind);
+	}
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS tdls_activate_add_peer(struct tdls_add_peer_request *req)
+{
+	QDF_STATUS status;
+	struct tdls_soc_priv_obj *soc_obj;
+	struct tdls_vdev_priv_obj *vdev_obj;
+	struct tdls_peer *peer;
+	uint16_t curr_tdls_peers;
+	const uint8_t *mac;
+	struct tdls_osif_indication ind;
+
+	if (!req->vdev) {
+		tdls_err("vdev null when add tdls peer");
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	mac = req->add_peer_req.peer_addr;
+	soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev);
+	vdev_obj = wlan_vdev_get_tdls_vdev_obj(req->vdev);
+
+	if (!soc_obj || !vdev_obj) {
+		tdls_err("soc_obj: %p, vdev_obj: %p", soc_obj, vdev_obj);
+		return QDF_STATUS_E_INVAL;
+	}
+	status = tdls_validate_current_mode(soc_obj);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto addrsp;
+
+	peer = tdls_get_peer(vdev_obj, mac);
+	if (!peer) {
+		tdls_err("peer: " QDF_MAC_ADDRESS_STR " not exist. invalid",
+			 QDF_MAC_ADDR_ARRAY(mac));
+		status = QDF_STATUS_E_INVAL;
+		goto addrsp;
+	}
+
+	/* in add station, we accept existing valid sta_id if there is */
+	if ((peer->link_status > TDLS_LINK_CONNECTING) ||
+	    (TDLS_STA_INDEX_CHECK((peer->sta_id)))) {
+		tdls_notice("link_status %d sta_id %d add peer ignored",
+			    peer->link_status, peer->sta_id);
+		status = QDF_STATUS_SUCCESS;
+		goto addrsp;
+	}
+
+	/* when others are on-going, we want to change link_status to idle */
+	if (tdls_is_progress(vdev_obj, mac, true)) {
+		tdls_notice(QDF_MAC_ADDRESS_STR " TDLS setuping. Req declined.",
+			    QDF_MAC_ADDR_ARRAY(mac));
+		status = QDF_STATUS_E_PERM;
+		goto setlink;
+	}
+
+	/* first to check if we reached to maximum supported TDLS peer. */
+	curr_tdls_peers = tdls_get_connected_peer(soc_obj);
+	if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) {
+		tdls_err(QDF_MAC_ADDRESS_STR
+			 " Request declined. Current %d, Max allowed %d.",
+			 QDF_MAC_ADDR_ARRAY(mac), curr_tdls_peers,
+			 soc_obj->max_num_tdls_sta);
+		status = QDF_STATUS_E_PERM;
+		goto setlink;
+	}
+
+	tdls_set_peer_link_status(peer,
+				  TDLS_LINK_CONNECTING, TDLS_LINK_SUCCESS);
+
+	status = tdls_pe_add_peer(req);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto setlink;
+
+	return QDF_STATUS_SUCCESS;
+
+setlink:
+	tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE,
+			     TDLS_LINK_UNSPECIFIED);
+addrsp:
+	if (soc_obj->tdls_event_cb) {
+		ind.status = status;
+		ind.vdev = req->vdev;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_ADD_PEER, &ind);
+	}
+
+	return QDF_STATUS_E_PERM;
+}
+
+static QDF_STATUS
+tdls_add_peer_serialize_callback(struct wlan_serialization_command *cmd,
+				 enum wlan_serialization_cb_reason reason)
+{
+	struct tdls_add_peer_request *req;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!cmd || !cmd->umac_cmd) {
+		tdls_err("cmd: %p, reason: %d", cmd, reason);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	req = cmd->umac_cmd;
+	tdls_debug("reason: %d, req %p", reason, req);
+
+	switch (reason) {
+	case WLAN_SER_CB_ACTIVATE_CMD:
+		/* command moved to active list
+		 */
+		status = tdls_activate_add_peer(req);
+		break;
+
+	case WLAN_SER_CB_CANCEL_CMD:
+		/* command removed from pending list.
+		 * notify os interface the status
+		 */
+		status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE);
+		break;
+
+	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
+		/* active command time out. */
+		status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE);
+		break;
+
+	case WLAN_SER_CB_RELEASE_MEM_CMD:
+		/* command successfully completed.
+		 * release memory & vdev reference count
+		 */
+		wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID);
+		qdf_mem_free(req);
+		break;
+
+	default:
+		/* Do nothing but logging */
+		QDF_ASSERT(0);
+		status = QDF_STATUS_E_INVAL;
+		break;
+	}
+
+	return status;
+}
+
+QDF_STATUS tdls_process_add_peer(struct tdls_add_peer_request *req)
+{
+	struct wlan_serialization_command cmd = {0,};
+	enum wlan_serialization_status ser_cmd_status;
+	struct wlan_objmgr_vdev *vdev;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!req || !req->vdev) {
+		tdls_err("req: %p", req);
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	vdev = req->vdev;
+	cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER;
+	cmd.cmd_id = 0;
+	cmd.cmd_cb = (wlan_serialization_cmd_callback)
+		tdls_add_peer_serialize_callback;
+	cmd.umac_cmd = req;
+	cmd.source = WLAN_UMAC_COMP_TDLS;
+	cmd.is_high_priority = false;
+	cmd.cmd_timeout_duration = WAIT_TIME_TDLS_ADD_STA;
+	cmd.vdev = vdev;
+
+	ser_cmd_status = wlan_serialization_request(&cmd);
+	tdls_debug("req: 0x%p wlan_serialization_request status:%d", req,
+		   ser_cmd_status);
+
+	switch (ser_cmd_status) {
+	case WLAN_SER_CMD_PENDING:
+		/* command moved to pending list. Do nothing */
+		break;
+	case WLAN_SER_CMD_ACTIVE:
+		/* command moved to active list. Do nothing */
+		break;
+	case WLAN_SER_CMD_DENIED_LIST_FULL:
+	case WLAN_SER_CMD_DENIED_RULES_FAILED:
+	case WLAN_SER_CMD_DENIED_UNSPECIFIED:
+		/* notify os interface about internal error*/
+		status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE);
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+		/* cmd can't be serviced.
+		 * release tdls_add_peer_request memory
+		 */
+		qdf_mem_free(req);
+		break;
+	default:
+		QDF_ASSERT(0);
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+
+	return status;
+error:
+	status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE);
+	qdf_mem_free(req);
+	return status;
+}
+
+static QDF_STATUS
+tdls_activate_update_peer(struct tdls_update_peer_request *req)
+{
+	QDF_STATUS status;
+	struct tdls_soc_priv_obj *soc_obj;
+	struct tdls_vdev_priv_obj *vdev_obj;
+	struct wlan_objmgr_vdev *vdev;
+	struct tdls_peer *curr_peer;
+	uint16_t curr_tdls_peers;
+	const uint8_t *mac;
+	struct tdls_update_peer_params *update_peer;
+	struct tdls_osif_indication ind;
+
+	if (!req->vdev) {
+		tdls_err("vdev object NULL when add TDLS peer");
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	mac = req->update_peer_req.peer_addr;
+	vdev = req->vdev;
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev);
+	if (!soc_obj || !vdev_obj) {
+		tdls_err("soc_obj: %p, vdev_obj: %p", soc_obj, vdev_obj);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	status = tdls_validate_current_mode(soc_obj);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto updatersp;
+
+	curr_peer = tdls_get_peer(vdev_obj, mac);
+	if (!curr_peer) {
+		tdls_err(QDF_MAC_ADDRESS_STR " not exist. return invalid",
+			 QDF_MAC_ADDR_ARRAY(mac));
+		status = QDF_STATUS_E_INVAL;
+		goto updatersp;
+	}
+
+	/* in change station, we accept only when sta_id is valid */
+	if (curr_peer->link_status > TDLS_LINK_CONNECTING ||
+	    !(TDLS_STA_INDEX_CHECK(curr_peer->sta_id))) {
+		tdls_err(QDF_MAC_ADDRESS_STR " link %d. sta %d. update peer %s",
+			 QDF_MAC_ADDR_ARRAY(mac), curr_peer->link_status,
+			 curr_peer->sta_id,
+			 (TDLS_STA_INDEX_CHECK(curr_peer->sta_id)) ? "ignored"
+			 : "declined");
+		status = (TDLS_STA_INDEX_CHECK(curr_peer->sta_id)) ?
+			QDF_STATUS_SUCCESS : QDF_STATUS_E_PERM;
+		goto updatersp;
+	}
+
+	/* when others are on-going, we want to change link_status to idle */
+	if (tdls_is_progress(vdev_obj, mac, true)) {
+		tdls_notice(QDF_MAC_ADDRESS_STR " TDLS setuping. Req declined.",
+			    QDF_MAC_ADDR_ARRAY(mac));
+		status = QDF_STATUS_E_PERM;
+		goto setlink;
+	}
+
+	curr_tdls_peers = tdls_get_connected_peer(soc_obj);
+	if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) {
+		tdls_err(QDF_MAC_ADDRESS_STR
+			 " Request declined. Current: %d, Max allowed: %d.",
+			 QDF_MAC_ADDR_ARRAY(mac), curr_tdls_peers,
+			 soc_obj->max_num_tdls_sta);
+		status = QDF_STATUS_E_PERM;
+		goto setlink;
+	}
+	update_peer = &req->update_peer_req;
+
+	if (update_peer->htcap_present)
+		curr_peer->spatial_streams = update_peer->ht_cap.mcsset[1];
+
+	tdls_set_peer_caps(vdev_obj, mac, &req->update_peer_req);
+	status = tdls_pe_update_peer(req);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto setlink;
+
+	return QDF_STATUS_SUCCESS;
+
+setlink:
+	tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE,
+			     TDLS_LINK_UNSPECIFIED);
+updatersp:
+	if (soc_obj->tdls_event_cb) {
+		ind.status = status;
+		ind.vdev = vdev;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_ADD_PEER, &ind);
+	}
+
+	return QDF_STATUS_E_PERM;
+}
+
+static QDF_STATUS
+tdls_update_peer_serialize_callback(struct wlan_serialization_command *cmd,
+				    enum wlan_serialization_cb_reason reason)
+{
+	struct tdls_update_peer_request *req;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!cmd || !cmd->umac_cmd) {
+		tdls_err("cmd: %p, reason: %d", cmd, reason);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	req = cmd->umac_cmd;
+	tdls_debug("reason: %d, req %p", reason, req);
+
+	switch (reason) {
+	case WLAN_SER_CB_ACTIVATE_CMD:
+		/* command moved to active list
+		 */
+		status = tdls_activate_update_peer(req);
+		break;
+
+	case WLAN_SER_CB_CANCEL_CMD:
+		/* command removed from pending list.
+		 * notify os interface the status
+		 */
+		status = tdls_internal_update_peer_rsp(req,
+						       QDF_STATUS_E_FAILURE);
+		break;
+
+	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
+		/* active command time out. */
+		status = tdls_internal_update_peer_rsp(req,
+						       QDF_STATUS_E_FAILURE);
+		break;
+
+	case WLAN_SER_CB_RELEASE_MEM_CMD:
+		/* command successfully completed.
+		 * release memory & release reference count
+		 */
+		wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID);
+		qdf_mem_free(req);
+		break;
+
+	default:
+		/* Do nothing but logging */
+		QDF_ASSERT(0);
+		status = QDF_STATUS_E_INVAL;
+		break;
+	}
+
+	return status;
+}
+
+QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req)
+{
+	struct wlan_serialization_command cmd = {0,};
+	enum wlan_serialization_status ser_cmd_status;
+	struct wlan_objmgr_vdev *vdev;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!req || !req->vdev) {
+		tdls_err("req: %p", req);
+		status = QDF_STATUS_E_FAILURE;
+		goto error;
+	}
+
+	vdev = req->vdev;
+	cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER;
+	cmd.cmd_id = 0;
+	cmd.cmd_cb = (wlan_serialization_cmd_callback)
+		tdls_update_peer_serialize_callback;
+	cmd.umac_cmd = req;
+	cmd.source = WLAN_UMAC_COMP_TDLS;
+	cmd.is_high_priority = false;
+	cmd.cmd_timeout_duration = WAIT_TIME_TDLS_ADD_STA;
+	cmd.vdev = req->vdev;
+
+	ser_cmd_status = wlan_serialization_request(&cmd);
+	tdls_debug("req: 0x%p wlan_serialization_request status:%d", req,
+		   ser_cmd_status);
+
+	switch (ser_cmd_status) {
+	case WLAN_SER_CMD_PENDING:
+		/* command moved to pending list. Do nothing */
+		break;
+	case WLAN_SER_CMD_ACTIVE:
+		/* command moved to active list. Do nothing */
+		break;
+	case WLAN_SER_CMD_DENIED_LIST_FULL:
+	case WLAN_SER_CMD_DENIED_RULES_FAILED:
+	case WLAN_SER_CMD_DENIED_UNSPECIFIED:
+		/* notify os interface about internal error*/
+		status = tdls_internal_update_peer_rsp(req,
+						       QDF_STATUS_E_FAILURE);
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+		/* cmd can't be serviced.
+		 * release tdls_add_peer_request memory
+		 */
+		qdf_mem_free(req);
+		break;
+	default:
+		QDF_ASSERT(0);
+		status = QDF_STATUS_E_INVAL;
+		break;
+	}
+
+	return status;
+error:
+	status = tdls_internal_update_peer_rsp(req, QDF_STATUS_E_FAILURE);
+	qdf_mem_free(req);
+	return status;
+}
+
+static QDF_STATUS tdls_activate_del_peer(struct tdls_oper_request *req)
+{
+	struct tdls_del_peer_request request = {0,};
+
+	request.vdev = req->vdev;
+	request.del_peer_req.peer_addr = req->peer_addr;
+
+	return tdls_pe_del_peer(&request);
+}
+
+static QDF_STATUS
+tdls_del_peer_serialize_callback(struct wlan_serialization_command *cmd,
+				 enum wlan_serialization_cb_reason reason)
+{
+	struct tdls_oper_request *req;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!cmd || !cmd->umac_cmd) {
+		tdls_err("cmd: %p, reason: %d", cmd, reason);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	req = cmd->umac_cmd;
+	tdls_debug("reason: %d, req %p", reason, req);
+
+	switch (reason) {
+	case WLAN_SER_CB_ACTIVATE_CMD:
+		/* command moved to active list
+		 */
+		status = tdls_activate_del_peer(req);
+		break;
+
+	case WLAN_SER_CB_CANCEL_CMD:
+		/* command removed from pending list.
+		 * notify os interface the status
+		 */
+		status = tdls_internal_del_peer_rsp(req);
+		break;
+
+	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
+		/* active command time out. */
+		status = tdls_internal_del_peer_rsp(req);
+		break;
+
+	case WLAN_SER_CB_RELEASE_MEM_CMD:
+		/* command successfully completed.
+		 * release memory & vdev reference count
+		 */
+		wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID);
+		qdf_mem_free(req);
+		break;
+
+	default:
+		/* Do nothing but logging */
+		QDF_ASSERT(0);
+		status = QDF_STATUS_E_INVAL;
+		break;
+	}
+
+	return status;
+}
+
+QDF_STATUS tdls_process_del_peer(struct tdls_oper_request *req)
+{
+	struct wlan_serialization_command cmd = {0,};
+	enum wlan_serialization_status ser_cmd_status;
+	struct wlan_objmgr_vdev *vdev;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!req || !req->vdev) {
+		tdls_err("req: %p", req);
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	vdev = req->vdev;
+	cmd.cmd_type = WLAN_SER_CMD_TDLS_DEL_PEER;
+	cmd.cmd_id = 0;
+	cmd.cmd_cb = (wlan_serialization_cmd_callback)
+		tdls_del_peer_serialize_callback;
+	cmd.umac_cmd = req;
+	cmd.source = WLAN_UMAC_COMP_TDLS;
+	cmd.is_high_priority = false;
+	cmd.cmd_timeout_duration = WAIT_TIME_TDLS_DEL_STA;
+	cmd.vdev = vdev;
+
+	ser_cmd_status = wlan_serialization_request(&cmd);
+	tdls_debug("req: 0x%p wlan_serialization_request status:%d", req,
+		   ser_cmd_status);
+
+	switch (ser_cmd_status) {
+	case WLAN_SER_CMD_PENDING:
+		/* command moved to pending list. Do nothing */
+		break;
+	case WLAN_SER_CMD_ACTIVE:
+		/* command moved to active list. Do nothing */
+		break;
+	case WLAN_SER_CMD_DENIED_LIST_FULL:
+	case WLAN_SER_CMD_DENIED_RULES_FAILED:
+	case WLAN_SER_CMD_DENIED_UNSPECIFIED:
+		/* notify os interface about internal error*/
+		status = tdls_internal_del_peer_rsp(req);
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+		/* cmd can't be serviced.
+		 * release tdls_add_peer_request memory
+		 */
+		qdf_mem_free(req);
+		break;
+	default:
+		QDF_ASSERT(0);
+		status = QDF_STATUS_E_INVAL;
+		break;
+	}
+
+	return status;
+error:
+	status = tdls_internal_del_peer_rsp(req);
+	qdf_mem_free(req);
+	return status;
+}
+
+/**
+ * tdls_process_add_peer_rsp() - handle response for update TDLS peer
+ * @rsp: TDLS add peer response
+ *
+ * Return: QDF_STATUS_SUCCESS for success; other values if failed
+ */
+static QDF_STATUS tdls_update_peer_rsp(struct tdls_add_sta_rsp *rsp)
+{
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_psoc *psoc;
+	struct tdls_soc_priv_obj *soc_obj;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct tdls_osif_indication ind;
+
+	psoc = rsp->psoc;
+	if (!psoc) {
+		tdls_err("psoc is NULL");
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_FAILURE;
+	}
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id,
+						    WLAN_TDLS_SB_ID);
+	if (!vdev) {
+		tdls_err("invalid vdev: %d", rsp->session_id);
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+
+	tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER);
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
+error:
+	soc_obj = wlan_psoc_get_tdls_soc_obj(psoc);
+	if (soc_obj && soc_obj->tdls_event_cb) {
+		ind.status = rsp->status_code;
+		ind.vdev = vdev;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_ADD_PEER, &ind);
+	}
+	qdf_mem_free(rsp);
+
+	return status;
+}
+
+/**
+ * tdls_process_add_peer_rsp() - handle response for add TDLS peer
+ * @rsp: TDLS add peer response
+ *
+ * Return: QDF_STATUS_SUCCESS for success; other values if failed
+ */
+static QDF_STATUS tdls_add_peer_rsp(struct tdls_add_sta_rsp *rsp)
+{
+	uint8_t sta_idx;
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_psoc *psoc;
+	struct tdls_vdev_priv_obj *vdev_obj;
+	struct tdls_soc_priv_obj *soc_obj = NULL;
+	struct tdls_conn_info *conn_rec;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct tdls_osif_indication ind;
+
+	psoc = rsp->psoc;
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id,
+						    WLAN_TDLS_SB_ID);
+	if (!vdev) {
+		tdls_err("invalid vdev: %d", rsp->session_id);
+		status =  QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	soc_obj = wlan_psoc_get_tdls_soc_obj(psoc);
+	vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev);
+	if (!soc_obj || !vdev_obj) {
+		tdls_err("soc object:%p, vdev object:%p", soc_obj, vdev_obj);
+		status = QDF_STATUS_E_FAILURE;
+		goto cmddone;
+	}
+	if (rsp->status_code) {
+		tdls_err("add sta failed. status code(=%d)", rsp->status_code);
+		status = QDF_STATUS_E_FAILURE;
+	} else {
+		conn_rec = soc_obj->tdls_conn_info;
+		for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta;
+		     sta_idx++) {
+			if (0 == conn_rec[sta_idx].sta_id) {
+				conn_rec[sta_idx].session_id = rsp->session_id;
+				conn_rec[sta_idx].sta_id = rsp->sta_id;
+				qdf_copy_macaddr(&conn_rec[sta_idx].peer_mac,
+						 &rsp->peermac);
+				tdls_warn("TDLS: STA IDX at %d is %d of mac "
+					  QDF_MAC_ADDRESS_STR, sta_idx,
+					  rsp->sta_id, QDF_MAC_ADDR_ARRAY
+					  (rsp->peermac.bytes));
+				break;
+			}
+		}
+
+		if (sta_idx < soc_obj->max_num_tdls_sta) {
+			status = tdls_set_sta_id(vdev_obj, rsp->peermac.bytes,
+						 rsp->sta_id);
+			if (QDF_IS_STATUS_ERROR(status)) {
+				tdls_err("set staid failed");
+				status = QDF_STATUS_E_FAILURE;
+				goto cmddone;
+			}
+
+			tdls_set_signature(vdev_obj, rsp->peermac.bytes,
+					   rsp->ucast_sig);
+		} else {
+			status = QDF_STATUS_E_FAILURE;
+		}
+	}
+
+cmddone:
+	tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER);
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
+error:
+	if (soc_obj && soc_obj->tdls_event_cb) {
+		ind.vdev = vdev;
+		ind.status = rsp->status_code;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_ADD_PEER, &ind);
+	}
+	qdf_mem_free(rsp);
+
+	return status;
+}
+
+QDF_STATUS tdls_process_add_peer_rsp(struct tdls_add_sta_rsp *rsp)
+{
+	tdls_debug("peer oper %d", rsp->tdls_oper);
+
+	if (rsp->tdls_oper == TDLS_OPER_ADD)
+		return tdls_add_peer_rsp(rsp);
+	else if (rsp->tdls_oper == TDLS_OPER_UPDATE)
+		return tdls_update_peer_rsp(rsp);
+
+	return QDF_STATUS_E_INVAL;
+}
+
+QDF_STATUS tdls_process_del_peer_rsp(struct tdls_del_sta_rsp *rsp)
+{
+	uint8_t sta_idx, id;
+	QDF_STATUS status = QDF_STATUS_E_FAILURE;
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_psoc *psoc;
+	struct tdls_vdev_priv_obj *vdev_obj;
+	struct tdls_soc_priv_obj *soc_obj = NULL;
+	struct tdls_conn_info *conn_rec;
+	struct tdls_peer *curr_peer = NULL;
+	const uint8_t *macaddr;
+	struct tdls_osif_indication ind;
+
+	tdls_debug("del peer rsp: vdev %d  peer " QDF_MAC_ADDRESS_STR,
+		   rsp->session_id, QDF_MAC_ADDR_ARRAY(rsp->peermac.bytes));
+	psoc = rsp->psoc;
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id,
+						    WLAN_TDLS_SB_ID);
+	if (!vdev) {
+		tdls_err("invalid vdev: %d", rsp->session_id);
+		status = QDF_STATUS_E_INVAL;
+		goto error;
+	}
+	soc_obj = wlan_psoc_get_tdls_soc_obj(psoc);
+	vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev);
+	if (!soc_obj || !vdev_obj) {
+		tdls_err("soc object:%p, vdev object:%p", soc_obj, vdev_obj);
+		status = QDF_STATUS_E_FAILURE;
+		goto cmddone;
+	}
+
+	conn_rec = soc_obj->tdls_conn_info;
+	for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; sta_idx++) {
+		if (conn_rec[sta_idx].session_id != rsp->session_id ||
+		    conn_rec[sta_idx].sta_id != rsp->sta_id)
+			continue;
+
+		macaddr = rsp->peermac.bytes;
+		tdls_warn("TDLS: del STA IDX = %x", rsp->sta_id);
+		curr_peer = tdls_find_peer(vdev_obj, macaddr);
+		if (curr_peer) {
+			tdls_debug(QDF_MAC_ADDRESS_STR " status is %d",
+				   QDF_MAC_ADDR_ARRAY(macaddr),
+				   curr_peer->link_status);
+			wlan_vdev_obj_lock(vdev);
+			id = wlan_vdev_get_id(vdev);
+			wlan_vdev_obj_unlock(vdev);
+			if (TDLS_IS_CONNECTED(curr_peer)) {
+				soc_obj->tdls_dereg_tl_peer(
+					soc_obj->tdls_tl_peer_data,
+					id, curr_peer->sta_id);
+				tdls_decrement_peer_count(soc_obj);
+			} else if (TDLS_LINK_CONNECTING ==
+				   curr_peer->link_status) {
+				soc_obj->tdls_dereg_tl_peer(
+					soc_obj->tdls_tl_peer_data,
+					id, curr_peer->sta_id);
+			}
+		}
+		tdls_reset_peer(vdev_obj, macaddr);
+		conn_rec[sta_idx].sta_id = 0;
+		conn_rec[sta_idx].session_id = 0xff;
+		qdf_mem_zero(&conn_rec[sta_idx].peer_mac,
+			     QDF_MAC_ADDR_SIZE);
+
+		status = QDF_STATUS_SUCCESS;
+		break;
+	}
+	macaddr = rsp->peermac.bytes;
+	if (!curr_peer) {
+		curr_peer = tdls_find_peer(vdev_obj, macaddr);
+
+		if (curr_peer)
+			tdls_set_peer_link_status(curr_peer, TDLS_LINK_IDLE,
+						  (curr_peer->link_status ==
+						   TDLS_LINK_TEARING) ?
+						  TDLS_LINK_UNSPECIFIED :
+						  TDLS_LINK_DROPPED_BY_REMOTE);
+	}
+
+cmddone:
+	tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_DEL_PEER);
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
+error:
+
+	if (soc_obj && soc_obj->tdls_event_cb) {
+		ind.vdev = vdev;
+		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
+				       TDLS_EVENT_DEL_PEER, &ind);
+	}
+	qdf_mem_free(rsp);
+
+	return status;
+}

+ 174 - 0
umac/tdls/core/src/wlan_tdls_cmds_process.h

@@ -21,3 +21,177 @@
  *
  * TDLS north bound commands include file
  */
+
+#ifndef _WLAN_TDLS_CMDS_PROCESS_H_
+#define _WLAN_TDLS_CMDS_PROCESS_H_
+
+/**
+ * enum tdls_add_oper - add peer type
+ * @TDLS_OPER_NONE: none
+ * @TDLS_OPER_ADD: add new peer
+ * @TDLS_OPER_UPDATE: used to update peer
+ */
+enum tdls_add_oper {
+	TDLS_OPER_NONE,
+	TDLS_OPER_ADD,
+	TDLS_OPER_UPDATE
+};
+
+/**
+ * struct tdls_add_sta_req - TDLS request struct TDLS module --> PE
+ *                           same as struct tSirTdlsAddStaReq;
+ * @message_type: eWNI_SME_TDLS_ADD_STA_REQ
+ * @length: message length
+ * @session_id: session id
+ * @transaction_id: transaction id for cmd
+ * @bssid: bssid
+ * @tdls_oper: add peer type
+ * @peermac: MAC address for TDLS peer
+ * @capability: mac capability as sSirMacCapabilityInfo
+ * @extn_capability: extent capability
+ * @supported_rates_length: rates length
+ * @supported_rates: supported rates
+ * @htcap_present: ht capability present
+ * @ht_cap: ht capability
+ * @vhtcap_present: vht capability present
+ * @vht_cap: vht capability
+ * @uapsd_queues: uapsd queue as sSirMacQosInfoStation
+ * @max_sp: maximum service period
+ */
+struct tdls_add_sta_req {
+	uint16_t message_type;
+	uint16_t length;
+	uint8_t session_id;
+	uint16_t transaction_id;
+	struct qdf_mac_addr bssid;
+	enum tdls_add_oper tdls_oper;
+	struct qdf_mac_addr peermac;
+	uint16_t capability;
+	uint8_t extn_capability[WLAN_MAC_MAX_EXTN_CAP];
+	uint8_t supported_rates_length;
+	uint8_t supported_rates[WLAN_MAC_MAX_SUPP_RATES];
+	uint8_t htcap_present;
+	struct htcap_cmn_ie ht_cap;
+	uint8_t vhtcap_present;
+	struct vhtcap vht_cap;
+	uint8_t uapsd_queues;
+	uint8_t max_sp;
+};
+
+/**
+ * struct tdls_add_sta_rsp - TDLS Response struct PE --> TDLS module
+ *                           same as struct sSirTdlsAddStaRsp
+ * @message_type: message type eWNI_SME_TDLS_ADD_STA_RSP
+ * @length: message length
+ * @status_code: status code as tSirResultCodes
+ * @peermac: MAC address of the TDLS peer
+ * @session_id: session id
+ * @sta_id: sta id
+ * @sta_type: sta type
+ * @ucast_sig: unicast signature
+ * @bcast_sig: broadcast signature
+ * @tdls_oper: add peer type
+ * @psoc: soc object
+ */
+struct tdls_add_sta_rsp {
+	uint16_t message_type;
+	uint16_t length;
+	QDF_STATUS status_code;
+	struct qdf_mac_addr peermac;
+	uint8_t session_id;
+	uint16_t sta_id;
+	uint16_t sta_type;
+	uint8_t ucast_sig;
+	uint8_t bcast_sig;
+	enum tdls_add_oper tdls_oper;
+	struct wlan_objmgr_psoc *psoc;
+};
+
+/**
+ * struct tdls_del_sta_req - TDLS Request struct TDLS module --> PE
+ *                           same as sSirTdlsDelStaReq
+ * @message_type: message type eWNI_SME_TDLS_DEL_STA_REQ
+ * @length: message length
+ * @session_id: session id
+ * @transaction_id: transaction id for cmd
+ * @bssid: bssid
+ * @peermac: MAC address of the TDLS peer
+ */
+struct tdls_del_sta_req {
+	uint16_t message_type;
+	uint16_t length;
+	uint8_t session_id;
+	uint16_t transaction_id;
+	struct qdf_mac_addr bssid;
+	struct qdf_mac_addr peermac;
+};
+
+/**
+ * struct tdls_del_sta_rsp - TDLS Response struct PE --> TDLS module
+ *                           same as sSirTdlsDelStaRsp
+ * @message_type: message type eWNI_SME_TDLS_DEL_STA_RSP
+ * @length: message length
+ * @session_id: session id
+ * @status_code: status code as tSirResultCodes
+ * @peermac: MAC address of the TDLS peer
+ * @sta_id: sta id
+ * @psoc: soc object
+ */
+struct tdls_del_sta_rsp {
+	uint16_t message_type;
+	uint16_t length;
+	uint8_t session_id;
+	QDF_STATUS status_code;
+	struct qdf_mac_addr peermac;
+	uint16_t sta_id;
+	struct wlan_objmgr_psoc *psoc;
+};
+
+/**
+ * tdls_process_add_peer() - add TDLS peer
+ * @req: TDLS add peer request
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other value if failed
+ */
+QDF_STATUS tdls_process_add_peer(struct tdls_add_peer_request *req);
+
+/**
+ * tdls_process_del_peer() - del TDLS peer
+ * @req: TDLS del peer request
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other value if failed
+ */
+QDF_STATUS tdls_process_del_peer(struct tdls_oper_request *req);
+
+/**
+ * tdls_process_update_peer() - update TDLS peer
+ * @req: TDLS update peer request
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other value if failed
+ */
+QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req);
+
+/**
+ * tdls_pe_del_peer() - send TDLS delete peer request to PE
+ * @req: TDLS delete peer request
+ *
+ * Return: QDF status
+ */
+QDF_STATUS tdls_pe_del_peer(struct tdls_del_peer_request *req);
+
+/**
+ * tdls_process_add_peer_rsp() - handle response for add or update TDLS peer
+ * @rsp: TDLS add peer response
+ *
+ * Return: QDF status
+ */
+QDF_STATUS tdls_process_add_peer_rsp(struct tdls_add_sta_rsp *rsp);
+
+/**
+ * tdls_process_add_peer_rsp() - handle response for delete TDLS peer
+ * @rsp: TDLS delete peer response
+ *
+ * Return: QDF status
+ */
+QDF_STATUS tdls_process_del_peer_rsp(struct tdls_del_sta_rsp *rsp);
+#endif

+ 92 - 14
umac/tdls/core/src/wlan_tdls_main.c

@@ -23,6 +23,8 @@
  */
 
 #include "wlan_tdls_main.h"
+#include "wlan_tdls_cmds_process.h"
+#include "wlan_tdls_peer.h"
 
 QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc,
 					     void *arg_list)
@@ -31,7 +33,7 @@ QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc,
 	struct tdls_soc_priv_obj *tdls_soc_obj;
 
 	tdls_soc_obj = qdf_mem_malloc(sizeof(*tdls_soc_obj));
-	if (tdls_soc_obj == NULL) {
+	if (!tdls_soc_obj) {
 		tdls_err("Failed to allocate memory for tdls object");
 		return QDF_STATUS_E_NOMEM;
 	}
@@ -61,7 +63,7 @@ QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc,
 
 	tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
 						WLAN_UMAC_COMP_TDLS);
-	if (tdls_soc_obj == NULL) {
+	if (!tdls_soc_obj) {
 		tdls_err("Failed to get tdls obj in psoc");
 		return QDF_STATUS_E_FAILURE;
 	}
@@ -78,19 +80,53 @@ QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc,
 	return status;
 }
 
-static void tdls_vdev_init(struct tdls_vdev_priv_obj *vdev)
+static QDF_STATUS tdls_vdev_init(struct tdls_vdev_priv_obj *vdev_obj)
 {
 	uint8_t i;
+	struct tdls_config_params *config;
+	struct tdls_user_config *user_config;
+	struct tdls_soc_priv_obj *soc_obj;
+
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
+	if (!soc_obj) {
+		tdls_err("tdls soc obj NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	config = &vdev_obj->threshold_config;
+	user_config = &soc_obj->tdls_configs;
+	config->tx_period_t = user_config->tdls_tx_states_period;
+	config->tx_packet_n = user_config->tdls_tx_pkt_threshold;
+	config->discovery_tries_n = user_config->tdls_max_discovery_attempt;
+	config->idle_timeout_t = user_config->tdls_idle_timeout;
+	config->idle_packet_n = user_config->tdls_idle_pkt_threshold;
+	config->rssi_trigger_threshold =
+		user_config->tdls_rssi_trigger_threshold;
+	config->rssi_teardown_threshold =
+		user_config->tdls_rssi_teardown_threshold;
+	config->rssi_delta = user_config->tdls_rssi_delta;
 
 	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
-		qdf_list_create(&vdev->peer_list[i],
+		qdf_list_create(&vdev_obj->peer_list[i],
 				WLAN_TDLS_PEER_SUB_LIST_SIZE);
 	}
+	qdf_mc_timer_init(&vdev_obj->peer_update_timer, QDF_TIMER_TYPE_SW,
+			  NULL, vdev_obj);
+	qdf_mc_timer_init(&vdev_obj->peer_discovery_timer, QDF_TIMER_TYPE_SW,
+			  NULL, vdev_obj);
+	return QDF_STATUS_SUCCESS;
+}
 
-	qdf_mc_timer_init(&vdev->peer_update_timer, QDF_TIMER_TYPE_SW,
-			  NULL, vdev);
-	qdf_mc_timer_init(&vdev->peer_update_timer, QDF_TIMER_TYPE_SW,
-			  NULL, vdev);
+static void tdls_vdev_deinit(struct tdls_vdev_priv_obj *vdev_obj)
+{
+	qdf_mc_timer_stop(&vdev_obj->peer_update_timer);
+	qdf_mc_timer_stop(&vdev_obj->peer_discovery_timer);
+
+	qdf_mc_timer_destroy(&vdev_obj->peer_update_timer);
+	qdf_mc_timer_destroy(&vdev_obj->peer_discovery_timer);
+
+	tdls_peer_idle_timers_destroy(vdev_obj);
+	tdls_free_peer_list(vdev_obj);
 }
 
 QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev,
@@ -107,14 +143,11 @@ QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev,
 	/* TODO: Add concurrency check */
 
 	tdls_vdev_obj = qdf_mem_malloc(sizeof(*tdls_vdev_obj));
-	if (tdls_vdev_obj == NULL) {
+	if (!tdls_vdev_obj) {
 		tdls_err("Failed to allocate memory for tdls vdev object");
 		return QDF_STATUS_E_NOMEM;
 	}
 
-	tdls_vdev_obj->vdev = vdev;
-	tdls_vdev_init(tdls_vdev_obj);
-
 	status = wlan_objmgr_vdev_component_obj_attach(vdev,
 						       WLAN_UMAC_COMP_TDLS,
 						       (void *)tdls_vdev_obj,
@@ -122,10 +155,15 @@ QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev,
 	if (QDF_IS_STATUS_ERROR(status)) {
 		tdls_err("Failed to attach vdev tdls component");
 		qdf_mem_free(tdls_vdev_obj);
+		goto out;
 	}
+	tdls_vdev_obj->vdev = vdev;
+	status = tdls_vdev_init(tdls_vdev_obj);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto out;
 
 	tdls_notice("tdls object attach to vdev successfully");
-
+out:
 	return status;
 }
 
@@ -142,7 +180,7 @@ QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev,
 
 	tdls_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
 							WLAN_UMAC_COMP_TDLS);
-	if (tdls_vdev_obj == NULL) {
+	if (!tdls_vdev_obj) {
 		tdls_err("Failed to get tdls vdev object");
 		return QDF_STATUS_E_FAILURE;
 	}
@@ -153,7 +191,47 @@ QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev,
 	if (QDF_IS_STATUS_ERROR(status))
 		tdls_err("Failed to detach vdev tdls component");
 
+	tdls_vdev_deinit(tdls_vdev_obj);
 	qdf_mem_free(tdls_vdev_obj);
 
 	return status;
 }
+
+QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!msg || !msg->bodyptr) {
+		tdls_err("msg: 0x%p, bodyptr: 0x%p", msg, msg->bodyptr);
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	switch (msg->type) {
+	case TDLS_CMD_TX_ACTION:
+		break;
+	case TDLS_CMD_ADD_STA:
+		tdls_process_add_peer(msg->bodyptr);
+		break;
+	case TDLS_CMD_CHANGE_STA:
+		tdls_process_update_peer(msg->bodyptr);
+		break;
+	case TDLS_CMD_ENABLE_LINK:
+		break;
+	case TDLS_CMD_DISABLE_LINK:
+		tdls_process_del_peer(msg->bodyptr);
+		break;
+	case TDLS_CMD_CONFIG_FORCE_PEER:
+		break;
+	case TDLS_CMD_REMOVE_FORCE_PEER:
+		break;
+	case TDLS_CMD_STATS_UPDATE:
+		break;
+	case TDLS_CMD_CONFIG_UPDATE:
+		break;
+	default:
+		break;
+	}
+
+	return status;
+}

+ 81 - 12
umac/tdls/core/src/wlan_tdls_main.h

@@ -39,6 +39,17 @@
 #define ENA_TDLS_BUFFER_STA   (1 << 1)  /* TDLS Buffer STA support */
 #define ENA_TDLS_SLEEP_STA    (1 << 2)  /* TDLS Sleep STA support */
 
+#define BW_20_OFFSET_BIT   0
+#define BW_40_OFFSET_BIT   1
+#define BW_80_OFFSET_BIT   2
+#define BW_160_OFFSET_BIT  3
+
+#define TDLS_SEC_OFFCHAN_OFFSET_0        0
+#define TDLS_SEC_OFFCHAN_OFFSET_40PLUS   40
+#define TDLS_SEC_OFFCHAN_OFFSET_40MINUS  (-40)
+#define TDLS_SEC_OFFCHAN_OFFSET_80       80
+#define TDLS_SEC_OFFCHAN_OFFSET_160      160
+
 #define tdls_log(level, args...) \
 	QDF_TRACE(QDF_MODULE_ID_TDLS, level, ## args)
 #define tdls_logfl(level, format, args...) \
@@ -163,13 +174,20 @@ struct tdls_set_state_info {
  * @tx_ack_cnf_cb_data: user data to tdls_tx_cnf_cb
  * @tdls_event_cb: tdls event callback
  * @tdls_evt_cb_data: tdls event user data
+ * @tdls_reg_tl_peer: callback to register the TDLS peer with TL
+ * @tdls_dereg_tl_peer: callback to unregister the TDLS peer
+ * @tdls_tl_peer_data: userdata for register/deregister TDLS peer
  * @tx_q_ack: queue for tx frames waiting for ack
  * @tdls_con_cap: tdls concurrency support
+ * @tdls_send_mgmt_req: store eWNI_SME_TDLS_SEND_MGMT_REQ value
+ * @tdls_add_sta_req: store eWNI_SME_TDLS_ADD_STA_REQ value
+ * @tdls_del_sta_req: store eWNI_SME_TDLS_DEL_STA_REQ value
+ * @tdls_update_peer_state: store WMA_UPDATE_TDLS_PEER_STATE value
  */
 struct tdls_soc_priv_obj {
 	struct wlan_objmgr_psoc *soc;
-	enum tdls_support_mode tdls_current_mode;
-	enum tdls_support_mode tdls_user_config_mode;
+	enum tdls_feature_mode tdls_current_mode;
+	enum tdls_feature_mode tdls_user_config_mode;
 	struct tdls_conn_info tdls_conn_info[WLAN_TDLS_STA_MAX_NUM];
 	struct tdls_user_config tdls_configs;
 	uint16_t max_num_tdls_sta;
@@ -188,8 +206,15 @@ struct tdls_soc_priv_obj {
 	void *tx_ack_cnf_cb_data;
 	tdls_evt_callback tdls_event_cb;
 	void *tdls_evt_cb_data;
+	tdls_register_tl_peer_callback tdls_reg_tl_peer;
+	tdls_deregister_tl_peer_callback tdls_dereg_tl_peer;
+	void *tdls_tl_peer_data;
 	qdf_list_t tx_q_ack;
 	enum tdls_conc_cap tdls_con_cap;
+	uint16_t tdls_send_mgmt_req;
+	uint16_t tdls_add_sta_req;
+	uint16_t tdls_del_sta_req;
+	uint16_t tdls_update_peer_state;
 };
 
 /**
@@ -225,6 +250,12 @@ struct tdls_vdev_priv_obj {
 	qdf_list_t tx_queue;
 };
 
+/**
+ * struct tdls_peer_mlme_info - tdls peer mlme info
+ **/
+struct tdls_peer_mlme_info {
+};
+
 /**
  * struct tdls_peer - tdls peer data
  * @node: node
@@ -266,7 +297,7 @@ struct tdls_peer {
 	uint16_t sta_id;
 	int8_t rssi;
 	enum tdls_peer_capab tdls_support;
-	enum tdls_link_status link_status;
+	enum tdls_link_state link_status;
 	uint8_t signature;
 	uint8_t is_responder;
 	uint8_t discovery_processed;
@@ -288,9 +319,10 @@ struct tdls_peer {
 	qdf_mc_timer_t peer_idle_timer;
 	bool is_peer_idle_timer_initialised;
 	uint8_t spatial_streams;
-	enum tdls_link_reason reason;
+	enum tdls_link_state_reason reason;
 	tdls_state_change_callback state_change_notification;
 	uint8_t qos;
+	struct tdls_peer_mlme_info *tdls_info;
 };
 
 /**
@@ -302,12 +334,29 @@ struct tdls_peer {
 static inline struct tdls_soc_priv_obj *
 wlan_vdev_get_tdls_soc_obj(struct wlan_objmgr_vdev *vdev)
 {
-	struct wlan_objmgr_psoc *psoc =
-		wlan_pdev_get_psoc(wlan_vdev_get_pdev(vdev));
+	struct wlan_objmgr_psoc *psoc;
+	struct tdls_soc_priv_obj *soc_obj;
+
+	if (!vdev) {
+		tdls_err("NULL vdev");
+		return NULL;
+	}
+
+	wlan_vdev_obj_lock(vdev);
+	psoc = wlan_vdev_get_psoc(vdev);
+	wlan_vdev_obj_unlock(vdev);
+	if (!psoc) {
+		tdls_err("can't get psoc");
+		return NULL;
+	}
 
-	return (struct tdls_soc_priv_obj *)
+	wlan_psoc_obj_lock(psoc);
+	soc_obj = (struct tdls_soc_priv_obj *)
 		wlan_objmgr_psoc_get_comp_private_obj(psoc,
 						      WLAN_UMAC_COMP_TDLS);
+	wlan_psoc_obj_unlock(psoc);
+
+	return soc_obj;
 }
 
 /**
@@ -319,9 +368,18 @@ wlan_vdev_get_tdls_soc_obj(struct wlan_objmgr_vdev *vdev)
 static inline struct tdls_soc_priv_obj *
 wlan_psoc_get_tdls_soc_obj(struct wlan_objmgr_psoc *psoc)
 {
-	return (struct tdls_soc_priv_obj *)
+	struct tdls_soc_priv_obj *soc_obj;
+	if (!psoc) {
+		tdls_err("NULL psoc");
+		return NULL;
+	}
+	wlan_psoc_obj_lock(psoc);
+	soc_obj = (struct tdls_soc_priv_obj *)
 		wlan_objmgr_psoc_get_comp_private_obj(psoc,
 						      WLAN_UMAC_COMP_TDLS);
+	wlan_psoc_obj_unlock(psoc);
+
+	return soc_obj;
 }
 
 /**
@@ -333,22 +391,33 @@ wlan_psoc_get_tdls_soc_obj(struct wlan_objmgr_psoc *psoc)
 static inline struct tdls_vdev_priv_obj *
 wlan_vdev_get_tdls_vdev_obj(struct wlan_objmgr_vdev *vdev)
 {
-	return (struct tdls_vdev_priv_obj *)
+	struct tdls_vdev_priv_obj *vdev_obj;
+
+	if (!vdev) {
+		tdls_err("NULL vdev");
+		return NULL;
+	}
+
+	wlan_vdev_obj_lock(vdev);
+	vdev_obj = (struct tdls_vdev_priv_obj *)
 		wlan_objmgr_vdev_get_comp_private_obj(vdev,
 						      WLAN_UMAC_COMP_TDLS);
+	wlan_vdev_obj_unlock(vdev);
+
+	return vdev_obj;
 }
 
 /**
  * tdls_set_link_status - tdls set link status
  * @vdev: vdev object
  * @mac: mac address of tdls peer
- * @link_status: tdls link status
+ * @link_state: tdls link state
  * @link_reason: reason
  */
 void tdls_set_link_status(struct tdls_vdev_priv_obj *vdev,
 			  const uint8_t *mac,
-			  enum tdls_link_status link_status,
-			  enum tdls_link_reason link_reason);
+			  enum tdls_link_state link_state,
+			  enum tdls_link_state_reason link_reason);
 /**
  * tdls_psoc_obj_create_notification() - tdls psoc create notification handler
  * @psoc: psoc object

+ 836 - 0
umac/tdls/core/src/wlan_tdls_peer.c

@@ -0,0 +1,836 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_tdls_peer.c
+ *
+ * TDLS peer basic operations
+ */
+#include "wlan_tdls_main.h"
+#include "wlan_tdls_peer.h"
+#include <wlan_reg_services_api.h>
+#include <wlan_utility.h>
+#include <wlan_policy_mgr_api.h>
+
+static uint8_t calculate_hash_key(const uint8_t *macaddr)
+{
+	uint8_t i, key;
+
+	for (i = 0, key = 0; i < 6; i++)
+		key ^= macaddr[i];
+
+	return key % WLAN_TDLS_PEER_LIST_SIZE;
+}
+
+struct tdls_peer *tdls_find_peer(struct tdls_vdev_priv_obj *vdev_obj,
+				 const uint8_t *macaddr)
+{
+	uint8_t key;
+	QDF_STATUS status;
+	struct tdls_peer *peer;
+	qdf_list_t *head;
+	qdf_list_node_t *p_node;
+
+	key = calculate_hash_key(macaddr);
+	head = &vdev_obj->peer_list[key];
+
+	status = qdf_list_peek_front(head, &p_node);
+	while (QDF_IS_STATUS_SUCCESS(status)) {
+		peer = qdf_container_of(p_node, struct tdls_peer, node);
+		if (WLAN_ADDR_EQ(&peer->peer_mac, macaddr)
+		    == QDF_STATUS_SUCCESS) {
+			return peer;
+		}
+		status = qdf_list_peek_next(head, p_node, &p_node);
+	}
+
+	tdls_debug("no tdls peer " QDF_MAC_ADDRESS_STR,
+		   QDF_MAC_ADDR_ARRAY(macaddr));
+	return NULL;
+}
+
+/**
+ * tdls_find_peer_handler() - helper function for tdls_find_all_peer
+ * @psoc: soc object
+ * @obj: vdev object
+ * @arg: used to keep search peer parameters
+ *
+ * Return: None.
+ */
+static void
+tdls_find_peer_handler(struct wlan_objmgr_psoc *psoc, void *obj, void *arg)
+{
+	struct wlan_objmgr_vdev *vdev = obj;
+	struct tdls_search_peer_param *tdls_param = arg;
+	struct tdls_vdev_priv_obj *vdev_obj;
+
+	if (tdls_param->peer)
+		return;
+
+	if (!vdev) {
+		tdls_err("invalid vdev");
+		return;
+	}
+
+	if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE &&
+	    wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE)
+		return;
+
+	vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
+							 WLAN_UMAC_COMP_TDLS);
+	if (!vdev_obj)
+		return;
+
+	tdls_param->peer = tdls_find_peer(vdev_obj, tdls_param->macaddr);
+}
+
+struct tdls_peer *
+tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr)
+{
+	struct tdls_search_peer_param tdls_search_param;
+	struct wlan_objmgr_psoc *psoc;
+
+	if (!soc_obj) {
+		tdls_err("tdls soc object is NULL");
+		return NULL;
+	}
+
+	psoc = soc_obj->soc;
+	if (!psoc) {
+		tdls_err("psoc is NULL");
+		return NULL;
+	}
+	tdls_search_param.macaddr = macaddr;
+	tdls_search_param.peer = NULL;
+
+	wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP,
+				     tdls_find_peer_handler,
+				     &tdls_search_param, 0, WLAN_TDLS_NB_ID);
+
+	return tdls_search_param.peer;
+}
+
+static uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc, uint8_t channel,
+				 uint8_t bw_offset)
+{
+	char country[REG_ALPHA2_LEN + 1];
+	QDF_STATUS status;
+
+	if (!psoc) {
+		tdls_err("psoc is NULL");
+		return 0;
+	}
+
+	status = wlan_reg_read_default_country(psoc, country);
+	if (QDF_IS_STATUS_ERROR(status))
+		return 0;
+
+	return wlan_reg_dmn_get_opclass_from_channel(country, channel,
+						     bw_offset);
+}
+
+/**
+ * tdls_add_peer() - add TDLS peer in TDLS vdev object
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of peer
+ *
+ * Allocate memory for the new peer, and add it to hash table.
+ *
+ * Return: new added TDLS peer, NULL if failed.
+ */
+static struct tdls_peer *tdls_add_peer(struct tdls_vdev_priv_obj *vdev_obj,
+				       const uint8_t *macaddr)
+{
+	struct tdls_peer *peer;
+	struct tdls_soc_priv_obj *soc_obj;
+	uint8_t key = 0;
+	qdf_list_t *head;
+
+	peer = qdf_mem_malloc(sizeof(*peer));
+	if (!peer) {
+		tdls_err("add tdls peer malloc memory failed!");
+		return NULL;
+	}
+
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
+	if (!soc_obj) {
+		tdls_err("NULL tdls soc object");
+		return NULL;
+	}
+
+	key = calculate_hash_key(macaddr);
+	head = &vdev_obj->peer_list[key];
+
+	qdf_mem_copy(&peer->peer_mac, macaddr, sizeof(peer->peer_mac));
+	peer->vdev_priv = vdev_obj;
+
+	peer->pref_off_chan_num =
+		soc_obj->tdls_configs.tdls_pre_off_chan_num;
+	peer->op_class_for_pref_off_chan =
+		tdls_find_opclass(soc_obj->soc,
+				  peer->pref_off_chan_num,
+				  soc_obj->tdls_configs.tdls_pre_off_chan_bw);
+
+	qdf_list_insert_back(head, &peer->node);
+
+	tdls_debug("add tdls peer: " QDF_MAC_ADDRESS_STR,
+		   QDF_MAC_ADDR_ARRAY(macaddr));
+	return peer;
+}
+
+struct tdls_peer *tdls_get_peer(struct tdls_vdev_priv_obj *vdev_obj,
+				const uint8_t *macaddr)
+{
+	struct tdls_peer *peer;
+
+	peer = tdls_find_peer(vdev_obj, macaddr);
+	if (!peer)
+		peer = tdls_add_peer(vdev_obj, macaddr);
+
+	return peer;
+}
+
+static struct tdls_peer *
+tdls_find_progress_peer_in_list(qdf_list_t *head,
+				const uint8_t *macaddr, uint8_t skip_self)
+{
+	QDF_STATUS status;
+	struct tdls_peer *peer;
+	qdf_list_node_t *p_node;
+
+	status = qdf_list_peek_front(head, &p_node);
+	while (QDF_IS_STATUS_SUCCESS(status)) {
+		peer = qdf_container_of(p_node, struct tdls_peer, node);
+		if (skip_self && macaddr &&
+		    WLAN_ADDR_EQ(&peer->peer_mac, macaddr)
+		    == QDF_STATUS_SUCCESS) {
+			status = qdf_list_peek_next(head, p_node, &p_node);
+			continue;
+		} else if (TDLS_LINK_CONNECTING == peer->link_status) {
+			tdls_debug(QDF_MAC_ADDRESS_STR " TDLS_LINK_CONNECTING",
+				   QDF_MAC_ADDR_ARRAY(peer->peer_mac.bytes));
+			return peer;
+		}
+		status = qdf_list_peek_next(head, p_node, &p_node);
+	}
+
+	return NULL;
+}
+
+/**
+ * tdls_find_progress_peer() - find the peer with ongoing TDLS progress
+ *                             on present vdev
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of peer, if NULL check for all the peer list
+ * @skip_self: If true, skip this macaddr. Otherwise, check all the peer list.
+ *             if macaddr is NULL, this argument is ignored, and check for all
+ *             the peer list.
+ *
+ * Return: Pointer to tdls_peer if TDLS is ongoing. Otherwise return NULL.
+ */
+static struct tdls_peer *
+tdls_find_progress_peer(struct tdls_vdev_priv_obj *vdev_obj,
+			const uint8_t *macaddr, uint8_t skip_self)
+{
+	uint8_t i;
+	struct tdls_peer *peer;
+	qdf_list_t *head;
+
+	if (!vdev_obj) {
+		tdls_err("invalid tdls vdev object");
+		return NULL;
+	}
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &vdev_obj->peer_list[i];
+
+		peer = tdls_find_progress_peer_in_list(head, macaddr,
+						       skip_self);
+		if (peer)
+			return peer;
+	}
+
+	return NULL;
+}
+
+/**
+ * tdls_find_progress_peer_handler() - helper function for tdls_is_progress
+ * @psoc: soc object
+ * @obj: vdev object
+ * @arg: used to keep search peer parameters
+ *
+ * Return: None.
+ */
+static void
+tdls_find_progress_peer_handler(struct wlan_objmgr_psoc *psoc,
+				void *obj, void *arg)
+{
+	struct wlan_objmgr_vdev *vdev = obj;
+	struct tdls_search_progress_param *tdls_progress = arg;
+	struct tdls_vdev_priv_obj *vdev_obj;
+
+	if (tdls_progress->peer)
+		return;
+
+	if (!vdev) {
+		tdls_err("invalid vdev");
+		return;
+	}
+
+	if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE &&
+	    wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE)
+		return;
+
+	vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
+							 WLAN_UMAC_COMP_TDLS);
+
+	tdls_progress->peer = tdls_find_progress_peer(vdev_obj,
+						      tdls_progress->macaddr,
+						      tdls_progress->skip_self);
+}
+
+struct tdls_peer *tdls_is_progress(struct tdls_vdev_priv_obj *vdev_obj,
+				   const uint8_t *macaddr, uint8_t skip_self)
+{
+	struct tdls_search_progress_param tdls_progress;
+	struct wlan_objmgr_psoc *psoc;
+
+	if (!vdev_obj) {
+		tdls_err("invalid tdls vdev object");
+		return NULL;
+	}
+
+	wlan_vdev_obj_lock(vdev_obj->vdev);
+	psoc = wlan_vdev_get_psoc(vdev_obj->vdev);
+	wlan_vdev_obj_unlock(vdev_obj->vdev);
+	if (!psoc) {
+		tdls_err("invalid psoc");
+		return NULL;
+	}
+	tdls_progress.macaddr = macaddr;
+	tdls_progress.skip_self = skip_self;
+	tdls_progress.peer = NULL;
+
+	wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP,
+				     tdls_find_progress_peer_handler,
+				     &tdls_progress, 0, WLAN_TDLS_NB_ID);
+
+	return tdls_progress.peer;
+}
+
+struct tdls_peer *
+tdls_find_first_connected_peer(struct tdls_vdev_priv_obj *vdev_obj)
+{
+	uint16_t i;
+	struct tdls_peer *peer;
+	qdf_list_t *head;
+	qdf_list_node_t *p_node;
+	QDF_STATUS status;
+
+	if (!vdev_obj) {
+		tdls_err("invalid tdls vdev object");
+		return NULL;
+	}
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &vdev_obj->peer_list[i];
+
+		status = qdf_list_peek_front(head, &p_node);
+		while (QDF_IS_STATUS_SUCCESS(status)) {
+			peer = qdf_container_of(p_node, struct tdls_peer, node);
+
+			if (peer && TDLS_LINK_CONNECTED == peer->link_status) {
+				tdls_debug(QDF_MAC_ADDRESS_STR
+					   " TDLS_LINK_CONNECTED",
+					   QDF_MAC_ADDR_ARRAY(
+						   peer->peer_mac.bytes));
+				return peer;
+			}
+			status = qdf_list_peek_next(head, p_node, &p_node);
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * tdls_determine_channel_opclass() - determine channel and opclass
+ * @soc_obj: TDLS soc object
+ * @vdev_obj: TDLS vdev object
+ * @peer: TDLS peer
+ * @channel: pointer to channel
+ * @opclass: pinter to opclass
+ *
+ * Function determines the channel and operating class
+ *
+ * Return: None.
+ */
+static void tdls_determine_channel_opclass(struct tdls_soc_priv_obj *soc_obj,
+					   struct tdls_vdev_priv_obj *vdev_obj,
+					   struct tdls_peer *peer,
+					   uint32_t *channel, uint32_t *opclass)
+{
+	uint32_t vdev_id;
+	enum tQDF_ADAPTER_MODE opmode;
+	/*
+	 * If tdls offchannel is not enabled then we provide base channel
+	 * and in that case pass opclass as 0 since opclass is mainly needed
+	 * for offchannel cases.
+	 */
+	if (!(TDLS_IS_OFF_CHANNEL_ENABLED(
+		      soc_obj->tdls_configs.tdls_feature_flags)) ||
+	      soc_obj->tdls_fw_off_chan_mode != ENABLE_CHANSWITCH) {
+		wlan_vdev_obj_lock(vdev_obj->vdev);
+		vdev_id = wlan_vdev_get_id(vdev_obj->vdev);
+		opmode = wlan_vdev_mlme_get_opmode(vdev_obj->vdev);
+		wlan_vdev_obj_unlock(vdev_obj->vdev);
+
+		*channel = policy_mgr_get_channel(soc_obj->soc,
+						  opmode, &vdev_id);
+		*opclass = 0;
+	} else {
+		*channel = peer->pref_off_chan_num;
+		*opclass = peer->op_class_for_pref_off_chan;
+	}
+	tdls_debug("channel:%d opclass:%d", *channel, *opclass);
+}
+
+/**
+ * tdls_get_wifi_hal_state() - get TDLS wifi hal state on current peer
+ * @peer: TDLS peer
+ * @state: output parameter to store the TDLS wifi hal state
+ * @reason: output parameter to store the reason of the current peer
+ *
+ * Return: None.
+ */
+static void tdls_get_wifi_hal_state(struct tdls_peer *peer, uint32_t *state,
+				    int32_t *reason)
+{
+	struct wlan_objmgr_vdev *vdev;
+	struct tdls_soc_priv_obj *soc_obj;
+
+	vdev = peer->vdev_priv->vdev;
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	if (!soc_obj) {
+		tdls_err("can't get tdls object");
+		return;
+	}
+
+	*reason = peer->reason;
+
+	switch (peer->link_status) {
+	case TDLS_LINK_IDLE:
+	case TDLS_LINK_DISCOVERED:
+	case TDLS_LINK_DISCOVERING:
+	case TDLS_LINK_CONNECTING:
+		*state = QCA_WIFI_HAL_TDLS_S_ENABLED;
+		break;
+	case TDLS_LINK_CONNECTED:
+		if ((TDLS_IS_OFF_CHANNEL_ENABLED(
+			     soc_obj->tdls_configs.tdls_feature_flags)) &&
+		     (soc_obj->tdls_fw_off_chan_mode == ENABLE_CHANSWITCH))
+			*state = QCA_WIFI_HAL_TDLS_S_ESTABLISHED_OFF_CHANNEL;
+		else
+			*state = QCA_WIFI_HAL_TDLS_S_ENABLED;
+		break;
+	case TDLS_LINK_TEARING:
+		*state = QCA_WIFI_HAL_TDLS_S_DROPPED;
+		break;
+	}
+}
+
+/**
+ * tdls_extract_peer_state_param() - extract peer update params from TDLS peer
+ * @peer_param: output peer update params
+ * @peer: TDLS peer
+ *
+ * This is used when enable TDLS link
+ *
+ * Return: None.
+ */
+void tdls_extract_peer_state_param(struct tdls_peer_update_state *peer_param,
+				   struct tdls_peer *peer)
+{
+	uint16_t i, num;
+	struct tdls_vdev_priv_obj *vdev_obj;
+	struct tdls_soc_priv_obj *soc_obj;
+	enum channel_state ch_state;
+	struct wlan_objmgr_pdev *pdev;
+	uint8_t chan_id;
+
+	vdev_obj = peer->vdev_priv;
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
+	pdev = wlan_vdev_get_pdev(vdev_obj->vdev);
+	if (!soc_obj || !pdev) {
+		tdls_err("soc_obj: %p, pdev: %p", soc_obj, pdev);
+		return;
+	}
+
+	qdf_mem_zero(peer_param, sizeof(*peer_param));
+	wlan_vdev_obj_lock(vdev_obj->vdev);
+	peer_param->vdev_id = wlan_vdev_get_id(vdev_obj->vdev);
+	wlan_vdev_obj_unlock(vdev_obj->vdev);
+
+	qdf_mem_copy(peer_param->peer_macaddr,
+		     peer->peer_mac.bytes, QDF_MAC_ADDR_SIZE);
+	peer_param->peer_state = TDLS_PEER_STATE_CONNCTED;
+	peer_param->peer_cap.is_peer_responder = peer->is_responder;
+	peer_param->peer_cap.peer_uapsd_queue = peer->uapsd_queues;
+	peer_param->peer_cap.peer_max_sp = peer->max_sp;
+	peer_param->peer_cap.peer_buff_sta_support = peer->buf_sta_capable;
+	peer_param->peer_cap.peer_off_chan_support =
+		peer->off_channel_capable;
+	peer_param->peer_cap.peer_curr_operclass = 0;
+	peer_param->peer_cap.self_curr_operclass = 0;
+	peer_param->peer_cap.peer_chanlen = peer->supported_channels_len;
+	peer_param->peer_cap.pref_off_channum = peer->pref_off_chan_num;
+	peer_param->peer_cap.pref_off_chan_bandwidth =
+		soc_obj->tdls_configs.tdls_pre_off_chan_bw;
+	peer_param->peer_cap.opclass_for_prefoffchan =
+		peer->op_class_for_pref_off_chan;
+
+	if (wlan_reg_is_dfs_ch(pdev, peer_param->peer_cap.pref_off_channum)) {
+		tdls_err("Resetting TDLS off-channel from %d to %d",
+			 peer_param->peer_cap.pref_off_channum,
+			 WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF);
+		peer_param->peer_cap.pref_off_channum =
+			WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF;
+	}
+
+	num = 0;
+	for (i = 0; i < peer->supported_channels_len; i++) {
+		chan_id = peer->supported_channels[i];
+		ch_state = wlan_reg_get_channel_state(pdev, chan_id);
+
+		if (CHANNEL_STATE_INVALID != ch_state &&
+		    CHANNEL_STATE_DFS != ch_state &&
+		    !wlan_is_dsrc_channel(wlan_is_dsrc_channel(chan_id))) {
+			peer_param->peer_cap.peer_chan[num].chan_id = chan_id;
+			peer_param->peer_cap.peer_chan[num].pwr =
+				wlan_reg_get_channel_reg_power(pdev, chan_id);
+			peer_param->peer_cap.peer_chan[num].dfs_set = false;
+			num++;
+		}
+	}
+
+	peer_param->peer_cap.peer_oper_classlen =
+		peer->supported_oper_classes_len;
+	for (i = 0; i < peer->supported_oper_classes_len; i++)
+		peer_param->peer_cap.peer_oper_class[i] =
+			peer->supported_oper_classes[i];
+}
+
+/**
+ * tdls_set_link_status() - set link statue for TDLS peer
+ * @vdev_obj: TDLS vdev object
+ * @mac: MAC address of current TDLS peer
+ * @link_status: link status
+ * @link_reason: reason with link status
+ *
+ * Return: None.
+ */
+void tdls_set_link_status(struct tdls_vdev_priv_obj *vdev_obj,
+			  const uint8_t *mac,
+			  enum tdls_link_state link_status,
+			  enum tdls_link_state_reason link_reason)
+{
+	uint32_t state = 0;
+	int32_t res = 0;
+	uint32_t op_class = 0;
+	uint32_t channel = 0;
+	struct tdls_peer *peer;
+	struct tdls_soc_priv_obj *soc_obj;
+
+	peer = tdls_find_peer(vdev_obj, mac);
+	if (!peer) {
+		tdls_err("peer is NULL, can't set link status %d, reason %d",
+			 link_status, link_reason);
+		return;
+	}
+
+	peer->link_status = link_status;
+
+	if (link_status >= TDLS_LINK_DISCOVERED)
+		peer->discovery_attempt = 0;
+
+	if (peer->is_forced_peer && peer->state_change_notification) {
+		peer->reason = link_reason;
+
+		soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
+		if (!soc_obj) {
+			tdls_err("NULL psoc object");
+			return;
+		}
+
+		tdls_determine_channel_opclass(soc_obj, vdev_obj,
+					       peer, &channel, &op_class);
+		tdls_get_wifi_hal_state(peer, &state, &res);
+		peer->state_change_notification(mac, op_class, channel,
+						state, res, soc_obj->soc);
+	}
+}
+
+void tdls_set_peer_link_status(struct tdls_peer *peer,
+			       enum tdls_link_state link_status,
+			       enum tdls_link_state_reason link_reason)
+{
+	uint32_t state = 0;
+	int32_t res = 0;
+	uint32_t op_class = 0;
+	uint32_t channel = 0;
+	struct tdls_soc_priv_obj *soc_obj;
+	struct tdls_vdev_priv_obj *vdev_obj;
+
+	peer->link_status = link_status;
+
+	if (link_status >= TDLS_LINK_DISCOVERED)
+		peer->discovery_attempt = 0;
+
+	if (peer->is_forced_peer && peer->state_change_notification) {
+		peer->reason = link_reason;
+
+		vdev_obj = peer->vdev_priv;
+		soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
+		if (!soc_obj) {
+			tdls_err("NULL psoc object");
+			return;
+		}
+
+		tdls_determine_channel_opclass(soc_obj, vdev_obj,
+					       peer, &channel, &op_class);
+		tdls_get_wifi_hal_state(peer, &state, &res);
+		peer->state_change_notification(peer->peer_mac.bytes,
+						op_class, channel, state,
+						res, soc_obj->soc);
+	}
+}
+
+void tdls_set_peer_caps(struct tdls_vdev_priv_obj *vdev_obj,
+			const uint8_t *macaddr,
+			struct tdls_update_peer_params  *req_info)
+{
+	uint8_t is_buffer_sta, is_off_channel_supported, is_qos_wmm_sta;
+	struct tdls_soc_priv_obj *soc_obj;
+	struct tdls_peer *curr_peer;
+	uint32_t feature;
+
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
+	if (!soc_obj) {
+		tdls_err("NULL psoc object");
+		return;
+	}
+
+	curr_peer = tdls_find_peer(vdev_obj, macaddr);
+	if (!curr_peer) {
+		tdls_err("NULL tdls peer");
+		return;
+	}
+
+	feature = soc_obj->tdls_configs.tdls_feature_flags;
+	if ((1 << 4) & req_info->extn_capability[3])
+		is_buffer_sta = 1;
+
+	if ((1 << 6) & req_info->extn_capability[3])
+		is_off_channel_supported = 1;
+
+	if (TDLS_IS_WMM_ENABLED(feature) && req_info->is_qos_wmm_sta)
+		is_qos_wmm_sta = true;
+
+	curr_peer->uapsd_queues = req_info->uapsd_queues;
+	curr_peer->max_sp = req_info->max_sp;
+	curr_peer->buf_sta_capable = is_buffer_sta;
+	curr_peer->off_channel_capable = is_off_channel_supported;
+
+	qdf_mem_copy(curr_peer->supported_channels,
+		     req_info->supported_channels,
+		     req_info->supported_channels_len);
+
+	curr_peer->supported_channels_len = req_info->supported_channels_len;
+
+	qdf_mem_copy(curr_peer->supported_oper_classes,
+		     req_info->supported_oper_classes,
+		     req_info->supported_oper_classes_len);
+
+	curr_peer->supported_oper_classes_len =
+		req_info->supported_oper_classes_len;
+
+	curr_peer->qos = is_qos_wmm_sta;
+}
+
+QDF_STATUS tdls_set_sta_id(struct tdls_vdev_priv_obj *vdev_obj,
+			   const uint8_t *macaddr, uint8_t sta_id)
+{
+	struct tdls_peer *peer;
+
+	peer = tdls_find_peer(vdev_obj, macaddr);
+	if (!peer) {
+		tdls_err("peer is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	peer->sta_id = sta_id;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS tdls_set_signature(struct tdls_vdev_priv_obj *vdev_obj,
+			      const uint8_t *macaddr, uint8_t signature)
+{
+	struct tdls_peer *peer;
+
+	peer = tdls_find_peer(vdev_obj, macaddr);
+	if (!peer) {
+		tdls_err("peer is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+	peer->signature = signature;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS tdls_set_force_peer(struct tdls_vdev_priv_obj *vdev_obj,
+			       const uint8_t *macaddr, bool forcepeer)
+{
+	struct tdls_peer *peer;
+
+	peer = tdls_find_peer(vdev_obj, macaddr);
+	if (!peer) {
+		tdls_err("peer is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+	peer->is_forced_peer = forcepeer;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS tdls_set_callback(struct tdls_peer *peer,
+			     tdls_state_change_callback callback)
+{
+	if (!peer) {
+		tdls_err("peer is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+	peer->state_change_notification = callback;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS tdls_set_extctrl_param(struct tdls_peer *peer, uint32_t chan,
+				  uint32_t max_latency, uint32_t op_class,
+				  uint32_t min_bandwidth)
+{
+	if (!peer) {
+		tdls_err("peer is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+	peer->op_class_for_pref_off_chan = (uint8_t)op_class;
+	peer->pref_off_chan_num = (uint8_t)chan;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS tdls_reset_peer(struct tdls_vdev_priv_obj *vdev_obj,
+			   const uint8_t *macaddr)
+{
+	struct tdls_soc_priv_obj *soc_obj;
+	struct tdls_peer *curr_peer;
+	struct tdls_user_config *config;
+
+	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
+	if (!soc_obj) {
+		tdls_err("NULL psoc object");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	curr_peer = tdls_find_peer(vdev_obj, macaddr);
+	if (!curr_peer) {
+		tdls_err("NULL tdls peer");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!curr_peer->is_forced_peer) {
+		config = &soc_obj->tdls_configs;
+		curr_peer->pref_off_chan_num = config->tdls_pre_off_chan_num;
+		curr_peer->op_class_for_pref_off_chan =
+			tdls_find_opclass(soc_obj->soc,
+					  curr_peer->pref_off_chan_num,
+					  config->tdls_pre_off_chan_bw);
+	}
+
+	tdls_set_peer_link_status(curr_peer, TDLS_LINK_IDLE,
+				  TDLS_LINK_UNSPECIFIED);
+	curr_peer->sta_id = 0;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+void tdls_peer_idle_timers_destroy(struct tdls_vdev_priv_obj *vdev_obj)
+{
+	uint16_t i;
+	struct tdls_peer *peer;
+	qdf_list_t *head;
+	qdf_list_node_t *p_node;
+	QDF_STATUS status;
+
+	if (!vdev_obj) {
+		tdls_err("NULL tdls vdev object");
+		return;
+	}
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &vdev_obj->peer_list[i];
+
+		status = qdf_list_peek_front(head, &p_node);
+		while (QDF_IS_STATUS_SUCCESS(status)) {
+			peer = qdf_container_of(p_node, struct tdls_peer, node);
+			if (peer && peer->is_peer_idle_timer_initialised) {
+				tdls_debug(QDF_MAC_ADDRESS_STR
+					   ": destroy  idle timer ",
+					   QDF_MAC_ADDR_ARRAY(
+						   peer->peer_mac.bytes));
+				qdf_mc_timer_stop(&peer->peer_idle_timer);
+				qdf_mc_timer_destroy(&peer->peer_idle_timer);
+			}
+			status = qdf_list_peek_next(head, p_node, &p_node);
+		}
+	}
+}
+
+void tdls_free_peer_list(struct tdls_vdev_priv_obj *vdev_obj)
+{
+	uint16_t i;
+	struct tdls_peer *peer;
+	qdf_list_t *head;
+	qdf_list_node_t *p_node;
+
+	if (!vdev_obj) {
+		tdls_err("NULL tdls vdev object");
+		return;
+	}
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &vdev_obj->peer_list[i];
+
+		while (QDF_IS_STATUS_SUCCESS(
+			       qdf_list_remove_front(head, &p_node))) {
+			peer = qdf_container_of(p_node, struct tdls_peer, node);
+			qdf_mem_free(peer);
+		}
+		qdf_list_destroy(head);
+	}
+}

+ 245 - 0
umac/tdls/core/src/wlan_tdls_peer.h

@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_tdls_peer.h
+ *
+ * TDLS peer function declaration
+ */
+
+#if !defined(_WLAN_TDLS_PEER_H_)
+#define _WLAN_TDLS_PEER_H_
+
+/**
+ * struct tdls_search_peer_param - used to search TDLS peer
+ * @macaddr: MAC address of peer
+ * @peer: pointer to the found peer
+ */
+struct tdls_search_peer_param {
+	const uint8_t *macaddr;
+	struct tdls_peer *peer;
+};
+
+/**
+ * struct tdls_progress_param - used to search progress TDLS peer
+ * @skip_self: skip self peer
+ * @macaddr: MAC address of peer
+ * @peer: pointer to the found peer
+ */
+struct tdls_search_progress_param {
+	uint8_t skip_self;
+	const uint8_t *macaddr;
+	struct tdls_peer *peer;
+};
+
+/**
+ * tdls_get_peer() -  find or add an TDLS peer in TDLS vdev object
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of peer
+ *
+ * Search the TDLS peer in the hash table and create a new one if not found.
+ *
+ * Return: Pointer to tdls_peer, NULL if failed.
+ */
+struct tdls_peer *tdls_get_peer(struct tdls_vdev_priv_obj *vdev_obj,
+				const uint8_t *macaddr);
+
+/**
+ * tdls_find_peer() - find TDLS peer in TDLS vdev object
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of peer
+ *
+ * This is in scheduler thread context, no lock required.
+ *
+ * Return: If peer is found, then it returns pointer to tdls_peer;
+ *         otherwise, it returns NULL.
+ */
+struct tdls_peer *tdls_find_peer(struct tdls_vdev_priv_obj *vdev_obj,
+				 const uint8_t *macaddr);
+
+/**
+ * tdls_find_all_peer() - find peer matching the input MACaddr in soc range
+ * @soc_obj: TDLS soc object
+ * @macaddr: MAC address of TDLS peer
+ *
+ * This is in scheduler thread context, no lock required.
+ *
+ * Return: TDLS peer if a matching is detected; NULL otherwise
+ */
+struct tdls_peer *
+tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr);
+
+/**
+ * tdls_find_first_connected_peer() - find the 1st connected tdls peer from vdev
+ * @vdev_obj: tdls vdev object
+ *
+ * This function searches for the 1st connected TDLS peer
+ *
+ * Return: The 1st connected TDLS peer if found; NULL otherwise
+ */
+struct tdls_peer *
+tdls_find_first_connected_peer(struct tdls_vdev_priv_obj *vdev_obj);
+
+/**
+ * tdls_is_progress() - find the peer with ongoing TDLS progress on present psoc
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of the peer
+ * @skip_self: if 1, skip checking self. If 0, search include self
+ *
+ * This is used in scheduler thread context, no lock required.
+ *
+ * Return: TDLS peer if found; NULL otherwise
+ */
+struct tdls_peer *tdls_is_progress(struct tdls_vdev_priv_obj *vdev_obj,
+				   const uint8_t *macaddr, uint8_t skip_self);
+
+/**
+ * tdls_extract_peer_state_param() - extract peer update params from TDL peer
+ * @peer_param: output peer update params
+ * @peer: TDLS peer
+ *
+ * This is used when enable TDLS link
+ *
+ * Return: None.
+ */
+void tdls_extract_peer_state_param(struct tdls_peer_update_state *peer_param,
+				   struct tdls_peer *peer);
+
+/**
+ * tdls_set_link_status() - set link statue for TDLS peer
+ * @peer: TDLS peer
+ * @link_state: link state
+ * @link_reason: reason with link status
+ *
+ * This is in scheduler thread context, no lock required.
+ *
+ * Return: None.
+ */
+void tdls_set_peer_link_status(struct tdls_peer *peer,
+			       enum tdls_link_state link_state,
+			       enum tdls_link_state_reason link_reason);
+
+/**
+ * tdls_set_peer_caps() - set capability for TDLS peer
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address for the TDLS peer
+ * @req_info: parameters to update peer capability
+ *
+ * This is in scheduler thread context, no lock required.
+ *
+ * Return: None.
+ */
+void tdls_set_peer_caps(struct tdls_vdev_priv_obj *vdev_obj,
+			const uint8_t *macaddr,
+			struct tdls_update_peer_params  *req_info);
+
+/**
+ * tdls_set_sta_id() - set station ID on a TDLS peer
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of the TDLS peer
+ * @sta_id: station ID
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other values if failed
+ */
+QDF_STATUS tdls_set_sta_id(struct tdls_vdev_priv_obj *vdev_obj,
+			   const uint8_t *macaddr, uint8_t sta_id);
+
+/**
+ * tdls_set_signature() - set TDLS peer's signature
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of the TDLS peer
+ * @signature: signature value
+ *
+ * This is used in scheduler thread context, no lock required.
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other values if failed
+ */
+QDF_STATUS tdls_set_signature(struct tdls_vdev_priv_obj *vdev_obj,
+			      const uint8_t *macaddr, uint8_t signature);
+
+/**
+ * tdls_set_force_peer() - set/clear is_forced_peer flag on peer
+ * @vdev_obj: TDLS vdev object
+ * @macaddr: MAC address of TDLS peer
+ * @forcepeer: value used to set is_forced_peer flag
+ *
+ * This is used in scheduler thread context, no lock required.
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other values if failed
+ */
+QDF_STATUS tdls_set_force_peer(struct tdls_vdev_priv_obj *vdev_obj,
+			       const uint8_t *macaddr, bool forcepeer);
+
+/**
+ * tdls_set_callback() - set state change callback on current TDLS peer
+ * @peer: TDLS peer
+ * @callback: state change callback
+ *
+ * This is used in scheduler thread context, no lock required.
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other values if failed
+ */
+QDF_STATUS tdls_set_callback(struct tdls_peer *peer,
+			     tdls_state_change_callback callback);
+
+/**
+ * tdls_set_extctrl_param() - set external control parameter on TDLS peer
+ * @peer: TDLS peer
+ * @chan: channel
+ * @max_latency: maximum latency
+ * @op_class: operation class
+ * @min_bandwidth: minimal bandwidth
+ *
+ * This is used in scheduler thread context, no lock required.
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other values if failed
+ */
+QDF_STATUS tdls_set_extctrl_param(struct tdls_peer *peer, uint32_t chan,
+				  uint32_t max_latency, uint32_t op_class,
+				  uint32_t min_bandwidth);
+
+/**
+ * tdls_reset_peer() - reset TDLS peer identified by MAC address
+ * @vdev_obj: TDLS vdev object
+ * @mac: MAC address of the peer
+ *
+ * Return: QDF_STATUS_SUCCESS if success; other values if failed
+ */
+QDF_STATUS tdls_reset_peer(struct tdls_vdev_priv_obj *vdev_obj,
+			   const uint8_t *mac);
+
+/**
+ * tdls_peer_idle_timers_destroy() - destroy peer idle timers
+ * @vdev_obj: TDLS vdev object
+ *
+ * Loop through the idle peer list and destroy their timers
+ *
+ * Return: None
+ */
+void tdls_peer_idle_timers_destroy(struct tdls_vdev_priv_obj *vdev_obj);
+
+/**
+ * tdls_free_peer_list() - free TDLS peer list
+ * @vdev_obj: TDLS vdev object
+ *
+ * Free all the tdls peers
+ *
+ * Return: None
+ */
+void tdls_free_peer_list(struct tdls_vdev_priv_obj *vdev_obj);
+#endif

+ 185 - 48
umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h

@@ -30,15 +30,36 @@
 #include <wlan_cmn.h>
 #include <wlan_cmn_ieee80211.h>
 
-#define WLAN_TDLS_STA_MAX_NUM           8
-#define WLAN_TDLS_PEER_LIST_SIZE        256
-#define WLAN_TDLS_CT_TABLE_SIZE         8
-#define WLAN_TDLS_PEER_SUB_LIST_SIZE    10
-#define WLAN_MAC_MAX_EXTN_CAP           8
-#define WLAN_MAC_MAX_SUPP_CHANNELS      100
-#define WLAN_MAX_SUPP_OPER_CLASSES      32
-#define WLAN_MAC_MAX_SUPP_RATES         32
-#define ENABLE_CHANSWITCH               1
+#define WLAN_TDLS_STA_MAX_NUM                        8
+#define WLAN_TDLS_STA_P_UAPSD_OFFCHAN_MAX_NUM        1
+#define WLAN_TDLS_PEER_LIST_SIZE                     16
+#define WLAN_TDLS_CT_TABLE_SIZE                      8
+#define WLAN_TDLS_PEER_SUB_LIST_SIZE                 10
+#define WLAN_MAC_MAX_EXTN_CAP                        8
+#define WLAN_MAC_MAX_SUPP_CHANNELS                   100
+#define WLAN_MAX_SUPP_OPER_CLASSES                   32
+#define WLAN_MAC_MAX_SUPP_RATES                      32
+#define WLAN_CHANNEL_14                              14
+#define ENABLE_CHANSWITCH                            1
+#define DISABLE_CHANSWITCH                           2
+#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN      1
+#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX      165
+#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF      36
+
+/** Maximum time(ms) to wait for tdls add sta to complete **/
+#define WAIT_TIME_TDLS_ADD_STA      1500
+
+/** Maximum time(ms) to wait for tdls del sta to complete **/
+#define WAIT_TIME_TDLS_DEL_STA      1500
+
+/** Maximum time(ms) to wait for Link Establish Req to complete **/
+#define WAIT_TIME_TDLS_LINK_ESTABLISH_REQ      1500
+
+/** Maximum time(ms) to wait for tdls mgmt to complete **/
+#define WAIT_TIME_TDLS_MGMT         11000
+
+#define TDLS_TEARDOWN_PEER_UNREACHABLE   25
+#define TDLS_TEARDOWN_PEER_UNSPEC_REASON 26
 
 #define TDLS_STA_INDEX_CHECK(sta_id) \
 	(((sta_id) >= 1) && ((sta_id) < 0xFF))
@@ -65,7 +86,23 @@ enum tdls_peer_capab {
 };
 
 /**
- * enum tdls_link_status - tdls link status
+ * enum tdls_peer_state - tdls peer state
+ * @TDLS_PEER_STATE_PEERING: tdls connection in progress
+ * @TDLS_PEER_STATE_CONNCTED: tdls peer is connected
+ * @TDLS_PEER_STATE_TEARDOWN: tdls peer is tear down
+ * @TDLS_PEER_ADD_MAC_ADDR: add peer mac into connection table
+ * @TDLS_PEER_REMOVE_MAC_ADDR: remove peer mac from connection table
+ */
+enum tdls_peer_state {
+	TDLS_PEER_STATE_PEERING,
+	TDLS_PEER_STATE_CONNCTED,
+	TDLS_PEER_STATE_TEARDOWN,
+	TDLS_PEER_ADD_MAC_ADDR,
+	TDLS_PEER_REMOVE_MAC_ADDR
+};
+
+/**
+ * enum tdls_link_state - tdls link state
  * @TDLS_LINK_IDLE: tdls link idle
  * @TDLS_LINK_DISCOVERING: tdls link discovering
  * @TDLS_LINK_DISCOVERED: tdls link discovered
@@ -73,7 +110,7 @@ enum tdls_peer_capab {
  * @TDLS_LINK_CONNECTED: tdls link connected
  * @TDLS_LINK_TEARING: tdls link tearing
  */
-enum tdls_link_status {
+enum tdls_link_state {
 	TDLS_LINK_IDLE = 0,
 	TDLS_LINK_DISCOVERING,
 	TDLS_LINK_DISCOVERED,
@@ -83,7 +120,7 @@ enum tdls_link_status {
 };
 
 /**
- * enum tdls_link_reason - tdls link reason
+ * enum tdls_link_state_reason - tdls link reason
  * @TDLS_LINK_SUCCESS: Success
  * @TDLS_LINK_UNSPECIFIED: Unspecified reason
  * @TDLS_LINK_NOT_SUPPORTED: Remote side doesn't support TDLS
@@ -91,7 +128,7 @@ enum tdls_link_status {
  * @TDLS_LINK_NOT_BENEFICIAL: Going to AP is better than direct
  * @TDLS_LINK_DROPPED_BY_REMOTE: Remote side doesn't want it anymore
  */
-enum tdls_link_reason {
+enum tdls_link_state_reason {
 	TDLS_LINK_SUCCESS,
 	TDLS_LINK_UNSPECIFIED         = -1,
 	TDLS_LINK_NOT_SUPPORTED       = -2,
@@ -101,7 +138,7 @@ enum tdls_link_reason {
 };
 
 /**
- * enum tdls_support_mode - TDLS support mode
+ * enum tdls_feature_mode - TDLS support mode
  * @TDLS_SUPPORT_DISABLED: Disabled in ini or FW
  * @TDLS_SUPPORT_SUSPENDED: TDLS supported by ini and FW, but disabled
  *            temporarily due to off-channel operations or due to other reasons
@@ -109,7 +146,7 @@ enum tdls_link_reason {
  * @TDLS_SUPPORT_IMP_MODE: Implicit mode
  * @TDLS_SUPPORT_EXT_CONTROL: External control mode
  */
-enum tdls_support_mode {
+enum tdls_feature_mode {
 	TDLS_SUPPORT_DISABLED = 0,
 	TDLS_SUPPORT_SUSPENDED,
 	TDLS_SUPPORT_EXP_TRIG_ONLY,
@@ -124,6 +161,9 @@ enum tdls_support_mode {
  * @TDLS_EVENT_RX_MGMT: rx discovery response frame
  * @TDLS_EVENT_ADD_PEER: add peer or update peer
  * @TDLS_EVENT_DEL_PEER: delete peer
+ * @TDLS_EVENT_DISCOVERY_REQ: dicovery request
+ * @TDLS_EVENT_TEARDOWN_REQ: teardown request
+ * @TDLS_EVENT_SETUP_REQ: setup request
  */
 enum tdls_event_type {
 	TDLS_EVENT_VDEV_STATE_CHANGE = 0,
@@ -131,11 +171,13 @@ enum tdls_event_type {
 	TDLS_EVENT_RX_MGMT,
 	TDLS_EVENT_ADD_PEER,
 	TDLS_EVENT_DEL_PEER,
+	TDLS_EVENT_DISCOVERY_REQ,
+	TDLS_EVENT_TEARDOWN_REQ,
+	TDLS_EVENT_SETUP_REQ,
 };
 
 /**
  * enum tdls_state_t - tdls state
- *
  * @QCA_WIFI_HAL_TDLS_DISABLED: TDLS is not enabled, or is disabled now
  * @QCA_WIFI_HAL_TDLS_ENABLED: TDLS is enabled, but not yet tried
  * @QCA_WIFI_HAL_TDLS_ESTABLISHED: Direct link is established
@@ -152,12 +194,25 @@ enum tdls_state_t {
 	QCA_WIFI_HAL_TDLS_S_FAILED,
 };
 
+/**
+ * enum tdls_off_chan_mode - mode for WMI_TDLS_SET_OFFCHAN_MODE_CMDID
+ * @TDLS_ENABLE_OFFCHANNEL: enable off channel
+ * @TDLS_DISABLE_OFFCHANNEL: disable off channel
+ */
+enum tdls_off_chan_mode {
+	TDLS_ENABLE_OFFCHANNEL,
+	TDLS_DISABLE_OFFCHANNEL
+};
+
 /**
  * enum tdls_event_msg_type - TDLS event message type
- * @TDLS_SHOULD_DISCOVER: should do discover
- * @TDLS_SHOULD_TEARDOWN: notify teardown the link
+ * @TDLS_SHOULD_DISCOVER: should do discover for peer (based on tx bytes per
+ * second > tx_discover threshold)
+ * @TDLS_SHOULD_TEARDOWN: recommend teardown the link for peer due to tx bytes
+ * per second below tx_teardown_threshold
  * @TDLS_PEER_DISCONNECTED: tdls peer disconnected
- * @TDLS_CONNECTION_TRACKER_NOTIFY: connection tracker notify
+ * @TDLS_CONNECTION_TRACKER_NOTIFY: TDLS/BT role change notification for
+ * connection tracker
  */
 enum tdls_event_msg_type {
 	TDLS_SHOULD_DISCOVER = 0,
@@ -168,19 +223,25 @@ enum tdls_event_msg_type {
 
 /**
  * enum tdls_event_reason - TDLS event reason
- * @TDLS_TEARDOWN_TX:
- * @TDLS_TEARDOWN_RSSI:
- * @TDLS_TEARDOWN_SCAN:
- * @TDLS_TEARDOWN_PTR_TIMEOUT:
- * @TDLS_TEARDOWN_BAD_PTR:
- * @TDLS_TEARDOWN_NO_RSP:
- * @TDLS_DISCONNECTED_PEER_DELETE:
- * @TDLS_PEER_ENTER_BUF_STA:
- * @TDLS_PEER_EXIT_BUF_STA:
- * @TDLS_ENTER_BT_BUSY:
- * @TDLS_EXIT_BT_BUSY:
- * @DLS_SCAN_STARTED:
- * @TDLS_SCAN_COMPLETED:
+ * @TDLS_TEARDOWN_TX: tdls teardown recommended due to low transmits
+ * @TDLS_TEARDOWN_RSSI: tdls link tear down recommended due to poor RSSI
+ * @TDLS_TEARDOWN_SCAN: tdls link tear down recommended due to offchannel scan
+ * @TDLS_TEARDOWN_PTR_TIMEOUT: tdls peer disconnected due to PTR timeout
+ * @TDLS_TEARDOWN_BAD_PTR: tdls peer disconnected due wrong PTR format
+ * @TDLS_TEARDOWN_NO_RSP: tdls peer not responding
+ * @TDLS_DISCONNECTED_PEER_DELETE: tdls peer disconnected due to peer deletion
+ * @TDLS_PEER_ENTER_BUF_STA: tdls entered buffer STA role, TDLS connection
+ * tracker needs to handle this
+ * @TDLS_PEER_EXIT_BUF_STA: tdls exited buffer STA role, TDLS connection tracker
+ * needs to handle this
+ * @TDLS_ENTER_BT_BUSY: BT entered busy mode, TDLS connection tracker needs to
+ * handle this
+ * @TDLS_EXIT_BT_BUSY: BT exited busy mode, TDLS connection tracker needs to
+ * handle this
+ * @DLS_SCAN_STARTED: TDLS module received a scan start event, TDLS connection
+ * tracker needs to handle this
+ * @TDLS_SCAN_COMPLETED: TDLS module received a scan complete event, TDLS
+ * connection tracker needs to handle this
  */
 enum tdls_event_reason {
 	TDLS_TEARDOWN_TX,
@@ -198,6 +259,19 @@ enum tdls_event_reason {
 	TDLS_SCAN_COMPLETED,
 };
 
+/**
+ * struct tdls_osif_indication - tdls indication to os if layer
+ * @vdev: vdev object
+ * @reason: used with teardown indication
+ * @peer_mac: MAC address of the TDLS peer
+ */
+struct tdls_osif_indication {
+	struct wlan_objmgr_vdev *vdev;
+	uint16_t reason;
+	uint8_t peer_mac[QDF_MAC_ADDR_SIZE];
+	QDF_STATUS status;
+};
+
 /**
  * struct tx_frame - tx frame
  * @buf: frame buffer
@@ -268,13 +342,14 @@ enum tdls_feature_bit {
  * @tdls_pre_off_chan_num: tdls off channel number
  * @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
  */
 struct tdls_user_config {
 	uint32_t tdls_tx_states_period;
 	uint32_t tdls_tx_pkt_threshold;
 	uint32_t tdls_rx_pkt_threshold;
 	uint32_t tdls_max_discovery_attempt;
-	uint32_t tdls_idle_timeoute;
+	uint32_t tdls_idle_timeout;
 	uint32_t tdls_idle_pkt_threshold;
 	uint32_t tdls_rssi_trigger_threshold;
 	uint32_t tdls_rssi_teardown_threshold;
@@ -287,6 +362,7 @@ struct tdls_user_config {
 	uint32_t tdls_pre_off_chan_num;
 	uint32_t tdls_pre_off_chan_bw;
 	uint32_t tdls_peer_kickout_threshold;
+	uint32_t delayed_trig_framint;
 };
 
 /**
@@ -331,32 +407,60 @@ struct tdls_tx_cnf {
 
 /* This callback is used to report state change of peer to wpa_supplicant */
 typedef int (*tdls_state_change_callback)(const uint8_t *mac,
-					uint32_t opclass,
-					uint32_t channel,
-					uint32_t state,
-					int32_t reason, void *ctx);
+					  uint32_t opclass,
+					  uint32_t channel,
+					  uint32_t state,
+					  int32_t reason, void *ctx);
 
 /* This callback is used to report events to os_if layer */
-typedef QDF_STATUS (*tdls_evt_callback) (void *data,
-					 enum tdls_event_type ev_type,
-					 void *event);
+typedef void (*tdls_evt_callback) (void *data,
+				   enum tdls_event_type ev_type,
+				   struct tdls_osif_indication *event);
 
 /* prototype of callback registered by hdd to receive the ack cnf */
 typedef int (*tdls_tx_ack_cnf_callback)(void *user_data,
-				struct tdls_tx_cnf *tx_ack_cnf_cb_data);
-
+					struct tdls_tx_cnf *tx_ack_cnf_cb_data);
+
+/* This callback is used to register TDLS peer with TL */
+typedef QDF_STATUS (*tdls_register_tl_peer_callback)(void *userdata,
+						     uint32_t vdev_id,
+						     const uint8_t *mac,
+						     uint16_t stat_id,
+						     uint8_t ucastsig,
+						     uint8_t qos);
+
+/* This callback is used to deregister TDLS peer */
+typedef QDF_STATUS (*tdls_deregister_tl_peer_callback)(void *userdata,
+						       uint32_t vdev_id,
+						       uint8_t sta_id);
 /**
  * struct tdls_start_params - tdls start params
+ * @config: tdls user config
+ * @tdls_send_mgmt_req: pass eWNI_SME_TDLS_SEND_MGMT_REQ value
+ * @tdls_add_sta_req: pass eWNI_SME_TDLS_ADD_STA_REQ value
+ * @tdls_del_sta_req: pass eWNI_SME_TDLS_DEL_STA_REQ value
+ * @tdls_update_peer_state: pass WMA_UPDATE_TDLS_PEER_STATE value
  * @tdls_event_cb: tdls event callback
  * @tdls_evt_cb_data: tdls event data
  * @ack_cnf_cb: tdls tx ack callback to indicate the tx status
  * @tx_ack_cnf_cb_data: tdls tx ack user data
+ * @tdls_reg_tl_peer: tdls register tdls peer
+ * @tdls_dereg_tl_peer: tdls deregister tdls peer
+ * @tdls_tl_peer_data: userdata for register/deregister TDLS peer
  */
 struct tdls_start_params {
+	struct tdls_user_config config;
+	uint16_t tdls_send_mgmt_req;
+	uint16_t tdls_add_sta_req;
+	uint16_t tdls_del_sta_req;
+	uint16_t tdls_update_peer_state;
 	tdls_evt_callback tdls_event_cb;
 	void *tdls_evt_cb_data;
 	tdls_tx_ack_cnf_callback ack_cnf_cb;
 	void *tx_ack_cnf_cb_data;
+	tdls_register_tl_peer_callback tdls_reg_tl_peer;
+	tdls_deregister_tl_peer_callback tdls_dereg_tl_peer;
+	void *tdls_tl_peer_data;
 };
 
 /**
@@ -366,7 +470,7 @@ struct tdls_start_params {
  * @vdev_id: vdev id
  */
 struct tdls_add_peer_params {
-	const uint8_t *peer_addr;
+	uint8_t peer_addr[QDF_MAC_ADDR_SIZE];
 	uint32_t peer_type;
 	uint32_t vdev_id;
 };
@@ -437,7 +541,7 @@ struct vhtcap {
 };
 
 struct tdls_update_peer_params {
-	const uint8_t *peer_addr;
+	uint8_t peer_addr[QDF_MAC_ADDR_SIZE];
 	uint32_t peer_type;
 	uint32_t vdev_id;
 	uint16_t capability;
@@ -454,6 +558,7 @@ struct tdls_update_peer_params {
 	uint8_t supported_channels[WLAN_MAC_MAX_SUPP_CHANNELS];
 	uint8_t supported_oper_classes_len;
 	uint8_t supported_oper_classes[WLAN_MAX_SUPP_OPER_CLASSES];
+	bool is_qos_wmm_sta;
 };
 
 struct tdls_update_peer_request {
@@ -461,6 +566,36 @@ struct tdls_update_peer_request {
 	struct tdls_update_peer_params update_peer_req;
 };
 
+/**
+ * struct tdls_oper_request - tdls operation request
+ * @vdev: vdev object
+ * @peer_addr: MAC address of the TDLS peer
+ */
+struct tdls_oper_request {
+	struct wlan_objmgr_vdev *vdev;
+	uint8_t peer_addr[QDF_MAC_ADDR_SIZE];
+};
+
+/**
+ * struct tdls_oper_config_force_peer_request - tdls enable force peer request
+ * @vdev: vdev object
+ * @peer_addr: MAC address of the TDLS peer
+ * @chan: channel
+ * @max_latency: maximum latency
+ * @op_class: operation class
+ * @min_bandwidth: minimal bandwidth
+ * @callback: state change callback
+ */
+struct tdls_oper_config_force_peer_request {
+	struct wlan_objmgr_vdev *vdev;
+	uint8_t peer_addr[QDF_MAC_ADDR_SIZE];
+	uint32_t chan;
+	uint32_t max_latency;
+	uint32_t op_class;
+	uint32_t min_bandwidth;
+	tdls_state_change_callback callback;
+};
+
 /**
  * struct tdls_info - tdls info
  *
@@ -550,20 +685,22 @@ struct tdls_peer_params {
 };
 
 /**
- * struct tdls_peer_update_params - TDLS peer state parameters
+ * struct tdls_peer_update_state - TDLS peer state parameters
  * @vdev_id: vdev id
  * @peer_macaddr: peer mac address
  * @peer_cap: peer capabality
+ * @resp_reqd: response needed
  */
-struct tdls_peer_update_params {
+struct tdls_peer_update_state {
 	uint32_t vdev_id;
 	uint8_t peer_macaddr[QDF_MAC_ADDR_SIZE];
 	uint32_t peer_state;
 	struct tdls_peer_params peer_cap;
+	bool resp_reqd;
 };
 
 /**
- * struct tdls_chan_switch_params - channel switch parameter structure
+ * struct tdls_channel_switch_params - channel switch parameter structure
  * @vdev_id: vdev ID
  * @peer_mac_addr: Peer mac address
  * @tdls_off_ch_bw_offset: Target off-channel bandwitdh offset
@@ -571,7 +708,7 @@ struct tdls_peer_update_params {
  * @oper_class: Operating class for target channel
  * @is_responder: Responder or initiator
  */
-struct tdls_chan_switch_params {
+struct tdls_channel_switch_params {
 	uint32_t    vdev_id;
 	uint8_t     peer_mac_addr[QDF_MAC_ADDR_SIZE];
 	uint16_t    tdls_off_ch_bw_offset;

+ 26 - 2
umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h

@@ -28,6 +28,9 @@
 #include <scheduler_api.h>
 #include <wlan_tdls_public_structs.h>
 #include <wlan_objmgr_cmn.h>
+#include <wlan_objmgr_psoc_obj.h>
+#include <wlan_objmgr_pdev_obj.h>
+#include <wlan_objmgr_vdev_obj.h>
 
 /**
  * ucfg_tdls_init() - TDLS module initialization API
@@ -66,8 +69,9 @@ QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc);
  *
  * Return: QDF_STATUS
  */
-QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc,
-				struct tdls_start_params *req);
+QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc,
+				   struct tdls_start_params *req);
+
 /**
  * ucfg_tdls_psoc_stop() - TDLS module stop
  * @psoc: psoc object
@@ -75,4 +79,24 @@ QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc,
  * Return: QDF_STATUS
  */
 QDF_STATUS ucfg_tdls_psoc_stop(struct wlan_objmgr_psoc *psoc);
+
+/**
+ * ucfg_tdls_add_peer() - handle TDLS add peer
+ * @vdev: vdev object
+ * @add_peer_req: add peer request parameters
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS ucfg_tdls_add_peer(struct wlan_objmgr_vdev *vdev,
+			      struct tdls_add_peer_params *add_peer_req);
+
+/**
+ * ucfg_tdls_update_peer() - handle TDLS update peer
+ * @vdev: vdev object
+ * @update_peer: update TDLS request parameters
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS ucfg_tdls_update_peer(struct wlan_objmgr_vdev *vdev,
+				 struct tdls_update_peer_params *update_peer);
 #endif

+ 136 - 7
umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c

@@ -24,6 +24,7 @@
 
 #include <wlan_tdls_ucfg_api.h>
 #include "../../core/src/wlan_tdls_main.h"
+#include "../../core/src/wlan_tdls_cmds_process.h"
 #include <wlan_objmgr_global_obj.h>
 #include <wlan_objmgr_cmn.h>
 
@@ -113,6 +114,7 @@ QDF_STATUS ucfg_tdls_deinit(void)
 static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj)
 {
 	uint8_t sta_idx;
+	uint32_t feature;
 
 	soc_obj->connected_peer_count = 0;
 	soc_obj->tdls_nss_switch_in_progress = false;
@@ -120,7 +122,16 @@ static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj)
 	soc_obj->tdls_nss_teardown_complete = false;
 	soc_obj->tdls_nss_transition_mode = TDLS_NSS_TRANSITION_S_UNKNOWN;
 	soc_obj->tdls_user_config_mode = TDLS_SUPPORT_DISABLED;
-	soc_obj->max_num_tdls_sta = WLAN_TDLS_STA_MAX_NUM;
+
+	feature = soc_obj->tdls_configs.tdls_feature_flags;
+	if (TDLS_IS_BUFFER_STA_ENABLED(feature) ||
+	    TDLS_IS_SLEEP_STA_ENABLED(feature) ||
+	    TDLS_IS_OFF_CHANNEL_ENABLED(feature))
+		soc_obj->max_num_tdls_sta =
+			WLAN_TDLS_STA_P_UAPSD_OFFCHAN_MAX_NUM;
+		else
+			soc_obj->max_num_tdls_sta = WLAN_TDLS_STA_MAX_NUM;
+
 
 	for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; sta_idx++) {
 		soc_obj->tdls_conn_info[sta_idx].sta_id = 0;
@@ -142,7 +153,7 @@ QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc)
 	tdls_notice("tdls psoc open");
 	soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
 							WLAN_UMAC_COMP_TDLS);
-	if (soc_obj == NULL) {
+	if (!soc_obj) {
 		tdls_err("Failed to get tdls psoc component");
 		return QDF_STATUS_E_FAILURE;
 	}
@@ -152,15 +163,21 @@ QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc)
 	return status;
 }
 
-QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc,
-				struct tdls_start_params *req)
+QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc,
+				   struct tdls_start_params *req)
 {
 	struct tdls_soc_priv_obj *soc_obj;
+	uint32_t tdls_feature_flags;
+
+	tdls_notice("tdls update config ");
+	if (!psoc || !req) {
+		tdls_err("psoc: 0x%p, req: 0x%p", psoc, req);
+		return QDF_STATUS_E_FAILURE;
+	}
 
-	tdls_notice("tdls psoc start");
 	soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
 							WLAN_UMAC_COMP_TDLS);
-	if (soc_obj == NULL) {
+	if (!soc_obj) {
 		tdls_err("Failed to get tdls psoc component");
 		return QDF_STATUS_E_FAILURE;
 	}
@@ -171,6 +188,28 @@ QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc,
 	soc_obj->tdls_tx_cnf_cb = req->ack_cnf_cb;
 	soc_obj->tx_ack_cnf_cb_data = req->tx_ack_cnf_cb_data;
 
+	/* Save callbacks to register/deregister TDLS sta with TL */
+	soc_obj->tdls_reg_tl_peer = req->tdls_reg_tl_peer;
+	soc_obj->tdls_dereg_tl_peer = req->tdls_dereg_tl_peer;
+	soc_obj->tdls_tl_peer_data = req->tdls_tl_peer_data;
+
+	/* Save legacy PE/WMA commands in TDLS soc object */
+	soc_obj->tdls_send_mgmt_req = req->tdls_send_mgmt_req;
+	soc_obj->tdls_add_sta_req = req->tdls_add_sta_req;
+	soc_obj->tdls_del_sta_req = req->tdls_del_sta_req;
+	soc_obj->tdls_update_peer_state = req->tdls_update_peer_state;
+
+	/* Update TDLS user config */
+	qdf_mem_copy(&soc_obj->tdls_configs, &req->config, sizeof(req->config));
+	tdls_feature_flags = soc_obj->tdls_configs.tdls_feature_flags;
+
+	if (!TDLS_IS_IMPLICIT_TRIG_ENABLED(tdls_feature_flags))
+		soc_obj->tdls_current_mode = TDLS_SUPPORT_EXP_TRIG_ONLY;
+	else if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(tdls_feature_flags))
+		soc_obj->tdls_current_mode = TDLS_SUPPORT_EXT_CONTROL;
+	else
+		soc_obj->tdls_current_mode = TDLS_SUPPORT_IMP_MODE;
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -181,7 +220,7 @@ QDF_STATUS ucfg_tdls_psoc_stop(struct wlan_objmgr_psoc *psoc)
 	tdls_notice("tdls psoc stop");
 	soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
 							WLAN_UMAC_COMP_TDLS);
-	if (soc_obj == NULL) {
+	if (!soc_obj) {
 		tdls_err("Failed to get tdls psoc component");
 		return QDF_STATUS_E_FAILURE;
 	}
@@ -203,3 +242,93 @@ QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc)
 
 	return status;
 }
+
+QDF_STATUS ucfg_tdls_add_peer(struct wlan_objmgr_vdev *vdev,
+			      struct tdls_add_peer_params *add_peer_req)
+{
+	struct scheduler_msg msg = {0, };
+	struct tdls_add_peer_request *req;
+	QDF_STATUS status;
+
+	if (!vdev || !add_peer_req) {
+		tdls_err("vdev: %p, req %p", vdev, add_peer_req);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+	tdls_debug("vdevid: %d, peertype: %d",
+		   add_peer_req->vdev_id, add_peer_req->peer_type);
+
+	status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("can't get vdev");
+		return status;
+	}
+
+	req = qdf_mem_malloc(sizeof(*req));
+	if (!req) {
+		tdls_err("mem allocate fail");
+		status = QDF_STATUS_E_NOMEM;
+		goto dec_ref;
+	}
+
+	qdf_mem_copy(&req->add_peer_req, add_peer_req, sizeof(*add_peer_req));
+	req->vdev = vdev;
+
+	msg.bodyptr = req;
+	msg.callback = tdls_process_cmd;
+	msg.type = TDLS_CMD_ADD_STA;
+	status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("post add peer msg fail");
+		qdf_mem_free(req);
+		goto dec_ref;
+	}
+
+	return status;
+dec_ref:
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+	return status;
+}
+
+QDF_STATUS ucfg_tdls_update_peer(struct wlan_objmgr_vdev *vdev,
+				 struct tdls_update_peer_params *update_peer)
+{
+	struct scheduler_msg msg = {0,};
+	struct tdls_update_peer_request *req;
+	QDF_STATUS status;
+
+	if (!vdev || !update_peer) {
+		tdls_err("vdev: %p, update_peer: %p", vdev, update_peer);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	tdls_debug("vdev_id: %d, peertype: %d",
+		   update_peer->vdev_id, update_peer->peer_type);
+	status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("can't get vdev");
+		return status;
+	}
+	req = qdf_mem_malloc(sizeof(*req));
+	if (!req) {
+		tdls_err("mem allocate fail");
+		status = QDF_STATUS_E_NOMEM;
+		goto dec_ref;
+	}
+	qdf_mem_copy(&req->update_peer_req, update_peer, sizeof(*update_peer));
+	req->vdev = vdev;
+
+	msg.bodyptr = req;
+	msg.callback = tdls_process_cmd;
+	msg.type = TDLS_CMD_CHANGE_STA;
+	status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("post update peer msg fail");
+		qdf_mem_free(req);
+		goto dec_ref;
+	}
+
+	return status;
+dec_ref:
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+	return status;
+}