Просмотр исходного кода

qcacld-3.0: Add support for SAP TWT teardown

Add support for SAP TWT teardown.

Change-Id: I108ea425e69e07ee8a3657ca5afcb436420b2626
CRs-Fixed: 2944872
Srinivas Girigowda 4 лет назад
Родитель
Сommit
5a04deba8b

+ 89 - 0
components/mlme/core/inc/wlan_mlme_twt_api.h

@@ -131,6 +131,60 @@ void mlme_set_twt_peer_capabilities(struct wlan_objmgr_psoc *psoc,
 				    tDot11fIEhe_cap *he_cap,
 				    tDot11fIEhe_op *he_op);
 
+/**
+ * mlme_twt_any_peer_cmd_in_progress() - Iterate through the list of peers
+ * and check if the given command is in progress
+ * @psoc: Pointer to psoc object
+ * @vdev_id: Vdev id
+ * @dialog_id: Dialog id
+ * @cmd: command
+ *
+ * This API is used to check for the given @dialog_id if the
+ * @cmd command is in progress for any of the peers.
+ *
+ * Return: true if command is in progress, false otherwise
+ */
+bool
+mlme_twt_any_peer_cmd_in_progress(struct wlan_objmgr_psoc *psoc,
+				  uint8_t vdev_id,
+				  uint8_t dialog_id,
+				  enum wlan_twt_commands cmd);
+
+/**
+ * mlme_sap_set_twt_all_peers_cmd_in_progress()  - Iterate through the list
+ * of peers and set the command in the TWT session entry in the TWT context
+ * @psoc: Pointer to global psoc object
+ * @vdev_id: Vdev id
+ * @dialog_id: Dialog ID
+ * @cmd: Command
+ *
+ * This API iterates through the list of peers and updates the active
+ * command to @cmd for the given dialog_id.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+mlme_sap_set_twt_all_peers_cmd_in_progress(struct wlan_objmgr_psoc *psoc,
+					   uint8_t vdev_id,
+					   uint8_t dialog_id,
+					   enum wlan_twt_commands cmd);
+
+/**
+ * mlme_init_all_peers_twt_context()  - Iterate through the list
+ * of peers and initialize the TWT context structure
+ * @psoc: Pointer to global psoc object
+ * @vdev_id: Vdev id
+ * @dialog_id: Dialog ID
+ *
+ * This API iterates through the list of peers and initializes
+ * the TWT context structure
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+mlme_init_all_peers_twt_context(struct wlan_objmgr_psoc *psoc,
+				uint8_t vdev_id,
+				uint8_t dialog_id);
 /**
  * mlme_init_twt_context() - Initialize TWT context structure
  * @psoc: Pointer to psoc object
@@ -174,6 +228,25 @@ bool mlme_is_twt_notify_in_progress(struct wlan_objmgr_psoc *psoc,
  */
 bool mlme_is_flexible_twt_enabled(struct wlan_objmgr_psoc *psoc);
 
+/**
+ * mlme_sap_set_twt_command_in_progress() - Set TWT command is in progress.
+ * @psoc: Pointer to psoc object
+ * @vdev_id: vdev id
+ * @peer_mac: Pointer to peer mac address
+ * @dialog_id: Dialog id
+ * @cmd: TWT command
+ *
+ * if the broadcast MAC address is passed, then
+ * set TWT command is in progress for all the peers
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS mlme_sap_set_twt_command_in_progress(struct wlan_objmgr_psoc *psoc,
+						uint8_t vdev_id,
+						struct qdf_mac_addr *peer_mac,
+						uint8_t dialog_id,
+						enum wlan_twt_commands cmd);
+
 /**
  * mlme_set_twt_command_in_progress() - Set TWT command is in progress.
  * @psoc: Pointer to psoc object
@@ -188,6 +261,22 @@ QDF_STATUS mlme_set_twt_command_in_progress(struct wlan_objmgr_psoc *psoc,
 					    uint8_t dialog_id,
 					    enum wlan_twt_commands cmd);
 
+/**
+ * mlme_sap_twt_peer_is_cmd_in_progress() - For a given peer_mac check if
+ * the given command is in progress
+ * @psoc: Pointer to psoc object
+ * @peer_mac: Pointer to peer mac address
+ * @dialog_id: Dialog id
+ * @cmd: TWT command
+ *
+ * Return: True if given command is in progress.
+ */
+bool mlme_sap_twt_peer_is_cmd_in_progress(struct wlan_objmgr_psoc *psoc,
+					  struct qdf_mac_addr *peer_mac,
+					  uint8_t dialog_id,
+					  enum wlan_twt_commands cmd);
+
+
 /**
  * mlme_twt_is_command_in_progress() - Check if given command is in progress
  * @psoc: Pointer to psoc object

+ 276 - 0
components/mlme/core/src/wlan_mlme_twt_api.c

@@ -220,6 +220,77 @@ QDF_STATUS mlme_init_twt_context(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
+QDF_STATUS
+mlme_init_all_peers_twt_context(struct wlan_objmgr_psoc *psoc,
+				uint8_t vdev_id,
+				uint8_t dialog_id)
+{
+	qdf_list_t *peer_list;
+	struct wlan_objmgr_peer *peer, *peer_next;
+	struct wlan_objmgr_vdev *vdev;
+	struct peer_mlme_priv_obj *peer_priv;
+
+	if (!psoc) {
+		mlme_legacy_err("psoc is NULL, dialog_id: %d", dialog_id);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
+						    WLAN_MLME_NB_ID);
+	if (!vdev) {
+		mlme_legacy_err("vdev is NULL, vdev_id: %d dialog_id: %d",
+				vdev_id, dialog_id);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	peer_list = &vdev->vdev_objmgr.wlan_peer_list;
+	if (!peer_list) {
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_NB_ID);
+		mlme_legacy_err("Peer list for vdev obj is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+        peer = wlan_vdev_peer_list_peek_active_head(vdev, peer_list,
+                                                    WLAN_MLME_NB_ID);
+	while (peer) {
+		peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer,
+							  WLAN_UMAC_COMP_MLME);
+		if (peer_priv) {
+			uint8_t i = 0;
+			uint8_t num_twt_sessions =
+					WLAN_MAX_TWT_SESSIONS_PER_PEER;
+
+			peer_priv->twt_ctx.num_twt_sessions =
+					num_twt_sessions;
+			for (i = 0; i < num_twt_sessions; i++) {
+				uint8_t existing_dialog_id =
+				peer_priv->twt_ctx.session_info[i].dialog_id;
+
+				if (existing_dialog_id == dialog_id ||
+				    dialog_id == WLAN_ALL_SESSIONS_DIALOG_ID) {
+					peer_priv->twt_ctx.session_info[i].setup_done = false;
+					peer_priv->twt_ctx.session_info[i].dialog_id =
+							WLAN_ALL_SESSIONS_DIALOG_ID;
+				}
+			}
+		}
+
+		peer_next =
+			wlan_peer_get_next_active_peer_of_vdev(vdev,
+							       peer_list,
+							       peer,
+							       WLAN_MLME_NB_ID);
+
+		wlan_objmgr_peer_release_ref(peer, WLAN_MLME_NB_ID);
+		peer = peer_next;
+        }
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_NB_ID);
+	mlme_legacy_debug("init done");
+        return QDF_STATUS_SUCCESS;
+}
+
+
 bool mlme_is_twt_setup_done(struct wlan_objmgr_psoc *psoc,
 			    struct qdf_mac_addr *peer_mac, uint8_t dialog_id)
 {
@@ -447,6 +518,211 @@ bool mlme_is_flexible_twt_enabled(struct wlan_objmgr_psoc *psoc)
 }
 #endif
 
+QDF_STATUS
+mlme_sap_set_twt_all_peers_cmd_in_progress(struct wlan_objmgr_psoc *psoc,
+					   uint8_t vdev_id,
+					   uint8_t dialog_id,
+					   enum wlan_twt_commands cmd)
+{
+	qdf_list_t *peer_list;
+	struct wlan_objmgr_peer *peer, *peer_next;
+	struct wlan_objmgr_vdev *vdev;
+	struct peer_mlme_priv_obj *peer_priv;
+
+	if (!psoc) {
+		mlme_legacy_err("psoc is NULL, dialog_id: %d", dialog_id);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
+						    WLAN_MLME_NB_ID);
+	if (!vdev) {
+		mlme_legacy_err("vdev is NULL, vdev_id: %d dialog_id: %d",
+				vdev_id, dialog_id);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	peer_list = &vdev->vdev_objmgr.wlan_peer_list;
+	if (!peer_list) {
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_NB_ID);
+		mlme_legacy_err("Peer list for vdev obj is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+        peer = wlan_vdev_peer_list_peek_active_head(vdev, peer_list,
+                                                    WLAN_MLME_NB_ID);
+	while (peer) {
+		peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer,
+							  WLAN_UMAC_COMP_MLME);
+		if (peer_priv) {
+			uint8_t i = 0;
+			uint8_t num_twt_sessions =
+				peer_priv->twt_ctx.num_twt_sessions;
+
+			for (i = 0; i < num_twt_sessions; i++) {
+				uint8_t existing_dialog_id =
+				peer_priv->twt_ctx.session_info[i].dialog_id;
+
+				if (existing_dialog_id == dialog_id ||
+				    dialog_id == WLAN_ALL_SESSIONS_DIALOG_ID) {
+					peer_priv->twt_ctx.session_info[i].active_cmd = cmd;
+
+					if (dialog_id !=
+					    WLAN_ALL_SESSIONS_DIALOG_ID) {
+						break;
+					}
+				}
+			}
+		}
+
+		peer_next =
+			wlan_peer_get_next_active_peer_of_vdev(vdev,
+							       peer_list,
+							       peer,
+							       WLAN_MLME_NB_ID);
+
+		wlan_objmgr_peer_release_ref(peer, WLAN_MLME_NB_ID);
+		peer = peer_next;
+        }
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_NB_ID);
+        return QDF_STATUS_SUCCESS;
+}
+
+bool
+mlme_twt_any_peer_cmd_in_progress(struct wlan_objmgr_psoc *psoc,
+				  uint8_t vdev_id,
+				  uint8_t dialog_id,
+				  enum wlan_twt_commands cmd)
+{
+	qdf_list_t *peer_list;
+	struct wlan_objmgr_peer *peer, *peer_next;
+	struct wlan_objmgr_vdev *vdev;
+	struct peer_mlme_priv_obj *peer_priv;
+	bool cmd_in_progress = false;
+
+	if (!psoc) {
+		mlme_legacy_err("psoc is NULL, dialog_id: %d", dialog_id);
+		return false;
+	}
+
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
+						    WLAN_MLME_NB_ID);
+	if (!vdev) {
+		mlme_legacy_err("vdev is NULL, vdev_id: %d dialog_id: %d",
+				vdev_id, dialog_id);
+		return false;
+	}
+
+	peer_list = &vdev->vdev_objmgr.wlan_peer_list;
+	if (!peer_list) {
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_NB_ID);
+		mlme_legacy_err("Peer list for vdev obj is NULL");
+		return false;
+	}
+
+        peer = wlan_vdev_peer_list_peek_active_head(vdev, peer_list,
+                                                    WLAN_MLME_NB_ID);
+	while (peer) {
+		peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer,
+							  WLAN_UMAC_COMP_MLME);
+		if (peer_priv) {
+			uint8_t i = 0;
+			uint8_t num_twt_sessions =
+				peer_priv->twt_ctx.num_twt_sessions;
+
+			for (i = 0; i < num_twt_sessions; i++) {
+				enum wlan_twt_commands active_cmd;
+				uint8_t existing_dialog_id;
+
+				active_cmd =
+				 peer_priv->twt_ctx.session_info[i].active_cmd;
+				existing_dialog_id =
+				 peer_priv->twt_ctx.session_info[i].dialog_id;
+
+				if (existing_dialog_id == dialog_id ||
+				    dialog_id == WLAN_ALL_SESSIONS_DIALOG_ID) {
+					cmd_in_progress = (active_cmd == cmd);
+
+					if (dialog_id !=
+						WLAN_ALL_SESSIONS_DIALOG_ID ||
+					    cmd_in_progress) {
+						wlan_objmgr_peer_release_ref(
+							peer,
+							WLAN_MLME_NB_ID);
+						wlan_objmgr_vdev_release_ref(
+							vdev,
+							WLAN_MLME_NB_ID);
+						return cmd_in_progress;
+					}
+				}
+			}
+		}
+
+		peer_next =
+			wlan_peer_get_next_active_peer_of_vdev(vdev,
+							       peer_list,
+							       peer,
+							       WLAN_MLME_NB_ID);
+
+		wlan_objmgr_peer_release_ref(peer, WLAN_MLME_NB_ID);
+		peer = peer_next;
+        }
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_NB_ID);
+	return cmd_in_progress;
+}
+
+bool mlme_sap_twt_peer_is_cmd_in_progress(struct wlan_objmgr_psoc *psoc,
+				     struct qdf_mac_addr *peer_mac,
+				     uint8_t dialog_id,
+				     enum wlan_twt_commands cmd)
+{
+	struct wlan_objmgr_peer *peer;
+	struct peer_mlme_priv_obj *peer_priv;
+	uint8_t i = 0;
+	bool cmd_in_progress = false;
+
+	peer = wlan_objmgr_get_peer_by_mac(psoc, peer_mac->bytes,
+					   WLAN_MLME_NB_ID);
+	if (!peer) {
+		mlme_legacy_err("Peer object not found");
+		return false;
+	}
+
+	peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer,
+							  WLAN_UMAC_COMP_MLME);
+	if (!peer_priv) {
+		wlan_objmgr_peer_release_ref(peer, WLAN_MLME_NB_ID);
+		mlme_legacy_err(" peer mlme component object is NULL");
+		return false;
+	}
+
+	for (i = 0; i < peer_priv->twt_ctx.num_twt_sessions; i++) {
+		enum wlan_twt_commands active_cmd;
+		uint8_t existing_dialog_id;
+
+		active_cmd =
+			peer_priv->twt_ctx.session_info[i].active_cmd;
+		existing_dialog_id =
+			peer_priv->twt_ctx.session_info[i].dialog_id;
+
+		if (existing_dialog_id == dialog_id ||
+		    dialog_id == WLAN_ALL_SESSIONS_DIALOG_ID ||
+		    existing_dialog_id == WLAN_ALL_SESSIONS_DIALOG_ID) {
+			cmd_in_progress = (active_cmd == cmd);
+
+			if (dialog_id != WLAN_ALL_SESSIONS_DIALOG_ID ||
+			    cmd_in_progress) {
+				break;
+			}
+		}
+	}
+
+	wlan_objmgr_peer_release_ref(peer, WLAN_MLME_NB_ID);
+	return cmd_in_progress;
+}
+
 QDF_STATUS mlme_set_twt_command_in_progress(struct wlan_objmgr_psoc *psoc,
 					    struct qdf_mac_addr *peer_mac,
 					    uint8_t dialog_id,

+ 3 - 0
core/hdd/src/wlan_hdd_softap_tx_rx.c

@@ -43,6 +43,7 @@
 #include <wlan_hdd_regulatory.h>
 #include "wlan_ipa_ucfg_api.h"
 #include "wlan_policy_mgr_ucfg.h"
+#include "wlan_mlme_twt_ucfg_api.h"
 #include <wma_types.h>
 #include "wlan_hdd_sta_info.h"
 #include "ol_defines.h"
@@ -1422,6 +1423,8 @@ QDF_STATUS hdd_softap_register_sta(struct hdd_adapter *adapter,
 				   WLAN_START_ALL_NETIF_QUEUE_N_CARRIER,
 				   WLAN_CONTROL_PATH);
 	ucfg_mlme_update_oce_flags(hdd_ctx->pdev);
+	ucfg_mlme_init_twt_context(hdd_ctx->psoc, sta_mac,
+				   WLAN_ALL_SESSIONS_DIALOG_ID);
 	return qdf_status;
 }
 

+ 131 - 16
core/hdd/src/wlan_hdd_twt.c

@@ -1849,30 +1849,117 @@ int hdd_send_twt_del_dialog_cmd(struct hdd_context *hdd_ctx,
 }
 
 /**
- * hdd_twt_terminate_session - Process TWT terminate
- * operation in the received vendor command and
- * send it to firmare
- * @adapter: adapter pointer
- * @twt_param_attr: nl attributes
- *
- * Handles QCA_WLAN_TWT_TERMINATE
+ * hdd_send_sap_twt_del_dialog_cmd() - Send SAP TWT del dialog command
+ * @hdd_ctx: HDD Context
+ * @twt_params: Pointer to del dialog cmd params structure
  *
  * Return: 0 on success, negative value on failure
  */
-static int hdd_twt_terminate_session(struct hdd_adapter *adapter,
-				     struct nlattr *twt_param_attr)
+static
+int hdd_send_sap_twt_del_dialog_cmd(struct hdd_context *hdd_ctx,
+				    struct wmi_twt_del_dialog_param *twt_params)
+{
+	QDF_STATUS status;
+	int ret = 0;
+
+	status = sme_sap_del_dialog_cmd(hdd_ctx->mac_handle,
+				    hdd_twt_del_dialog_comp_cb,
+				    twt_params);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Failed to send del dialog command");
+		ret = qdf_status_to_os_return(status);
+	}
+
+	return ret;
+}
+
+
+static int hdd_sap_twt_terminate_session(struct hdd_adapter *adapter,
+					 struct nlattr *twt_param_attr)
 {
-	struct hdd_station_ctx *hdd_sta_ctx = NULL;
-	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1];
 	struct wmi_twt_del_dialog_param params = {0};
 	QDF_STATUS status;
-	int id;
-	int ret;
+	int id, id1, ret;
+	bool is_associated;
+	struct qdf_mac_addr mac_addr;
 
-	if (adapter->device_mode != QDF_STA_MODE &&
-	    adapter->device_mode != QDF_P2P_CLIENT_MODE)
+	params.vdev_id = adapter->vdev_id;
+
+	ret = wlan_cfg80211_nla_parse_nested(tb,
+					     QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX,
+					     twt_param_attr,
+					     qca_wlan_vendor_twt_add_dialog_policy);
+	if (ret)
+		return ret;
+
+	id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID;
+	id1 = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR;
+	if (tb[id] && tb[id1]) {
+		params.dialog_id = nla_get_u8(tb[id]);
+		nla_memcpy(params.peer_macaddr, tb[id1], QDF_MAC_ADDR_SIZE);
+	} else if (!tb[id] && !tb[id1]) {
+		struct qdf_mac_addr bcast_addr = QDF_MAC_ADDR_BCAST_INIT;
+
+		params.dialog_id = TWT_ALL_SESSIONS_DIALOG_ID;
+		qdf_mem_copy(params.peer_macaddr, bcast_addr.bytes,
+			     QDF_MAC_ADDR_SIZE);
+	} else {
+		hdd_err_rl("get_params dialog_id or mac_addr is missing");
+		return -EINVAL;
+	}
+
+	if (!params.dialog_id)
+		params.dialog_id = TWT_ALL_SESSIONS_DIALOG_ID;
+
+	if (params.dialog_id != TWT_ALL_SESSIONS_DIALOG_ID &&
+	    QDF_IS_ADDR_BROADCAST(params.peer_macaddr)) {
+		hdd_err("Bcast MAC valid with dlg_id:%d but here dlg_id is:%d",
+			TWT_ALL_SESSIONS_DIALOG_ID, params.dialog_id);
+		return -EINVAL;
+	}
+
+	status = hdd_twt_check_all_twt_support(adapter->hdd_ctx->psoc,
+					       params.dialog_id);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_debug("All TWT sessions not supported by target");
 		return -EOPNOTSUPP;
+	}
+
+	qdf_mem_copy(mac_addr.bytes, params.peer_macaddr, QDF_MAC_ADDR_SIZE);
+
+	if (!qdf_is_macaddr_broadcast(&mac_addr)) {
+		is_associated = hdd_is_peer_associated(adapter, &mac_addr);
+		if (!is_associated) {
+			hdd_err_rl("Association doesn't exist for STA: "
+				   QDF_MAC_ADDR_FMT,
+				   QDF_MAC_ADDR_REF(mac_addr.bytes));
+			/*
+			 * Return success, since STA is not associated and
+			 * there is no TWT session.
+			 */
+			return 0;
+		}
+	}
+
+	hdd_debug("vdev_id %d dialog_id %d peer mac_addr "
+		  QDF_MAC_ADDR_FMT, params.vdev_id, params.dialog_id,
+		  QDF_MAC_ADDR_REF(params.peer_macaddr));
+
+	ret = hdd_send_sap_twt_del_dialog_cmd(adapter->hdd_ctx, &params);
+
+	return ret;
+}
+
+static int hdd_sta_twt_terminate_session(struct hdd_adapter *adapter,
+					 struct nlattr *twt_param_attr)
+{
+	struct hdd_station_ctx *hdd_sta_ctx = NULL;
+	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1];
+	struct wmi_twt_del_dialog_param params = {0};
+	QDF_STATUS status;
+	int id, ret;
 
 	hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
 	if (!hdd_cm_is_vdev_associated(adapter)) {
@@ -1930,7 +2017,7 @@ static int hdd_twt_terminate_session(struct hdd_adapter *adapter,
 		return -EAGAIN;
 	}
 
-	hdd_debug("twt_terminate: vdev_id %d dialog_id %d peer mac_addr "
+	hdd_debug("vdev_id %d dialog_id %d peer mac_addr "
 		  QDF_MAC_ADDR_FMT, params.vdev_id, params.dialog_id,
 		  QDF_MAC_ADDR_REF(params.peer_macaddr));
 
@@ -1939,6 +2026,34 @@ static int hdd_twt_terminate_session(struct hdd_adapter *adapter,
 	return ret;
 }
 
+/**
+ * hdd_twt_terminate_session - Process TWT terminate
+ * operation in the received vendor command and
+ * send it to firmare
+ * @adapter: adapter pointer
+ * @twt_param_attr: nl attributes
+ *
+ * Handles QCA_WLAN_TWT_TERMINATE
+ *
+ * Return: 0 on success, negative value on failure
+ */
+static int hdd_twt_terminate_session(struct hdd_adapter *adapter,
+				     struct nlattr *twt_param_attr)
+{
+	enum QDF_OPMODE device_mode = adapter->device_mode;
+
+	switch (device_mode) {
+	case QDF_STA_MODE:
+		return hdd_sta_twt_terminate_session(adapter, twt_param_attr);
+	case QDF_SAP_MODE:
+		return hdd_sap_twt_terminate_session(adapter, twt_param_attr);
+	default:
+		hdd_err_rl("TWT terminate is not supported on %s",
+			   qdf_opmode_str(adapter->device_mode));
+		return -EOPNOTSUPP;
+	}
+}
+
 /**
  * hdd_twt_nudge_pack_resp_nlmsg() - pack the skb with
  * firmware response for twt nudge command

+ 13 - 0
core/sme/inc/sme_api.h

@@ -3811,6 +3811,19 @@ QDF_STATUS sme_del_dialog_cmd(mac_handle_t mac_handle,
 			      twt_del_dialog_cb del_dialog_cb,
 			      struct wmi_twt_del_dialog_param *twt_params);
 
+/**
+ * sme_sap_del_dialog_cmd() - Register callback and send TWT del dialog
+ * command to firmware
+ * @mac_handle: MAC handle
+ * @twt_del_dialog_cb: Function callback to handle del_dialog event
+ * @twt_params: TWT del dialog parameters
+ *
+ * Return: QDF Status
+ */
+QDF_STATUS sme_sap_del_dialog_cmd(mac_handle_t mac_handle,
+				  twt_del_dialog_cb del_dialog_cb,
+				  struct wmi_twt_del_dialog_param *twt_params);
+
 /**
  * sme_pause_dialog_cmd() - Register callback and send TWT pause dialog
  * command to firmware

+ 254 - 1
core/sme/src/common/sme_api.c

@@ -1936,6 +1936,161 @@ static QDF_STATUS sme_process_antenna_mode_resp(struct mac_context *mac,
 }
 
 #ifdef WLAN_SUPPORT_TWT
+/**
+ * sme_sap_twt_is_command_in_progress() - Based on the input peer mac address
+ * invoke the appropriate function to check if the given command is in progress
+ * @psoc: Pointer to psoc object
+ * @vdev_id: Vdev id
+ * @peer_mac: Peer MAC address
+ * @dialog_id: Dialog id
+ * @cmd: command
+ *
+ * If the input @peer_mac is a broadcast MAC address then the expectation is
+ * to iterate through the list of all peers and check for any given @dialog_id
+ * if the command @cmd is in progress.
+ * Note: If @peer_mac is broadcast MAC address then @dialog_id shall always
+ * be WLAN_ALL_SESSIONS_DIALOG_ID.
+ * For ex: If TWT teardown command is issued on a particular @dialog_id and
+ * non-broadcast peer mac and FW response is not yet received then for that
+ * particular @dialog_id and @peer_mac, TWT teardown is the active command,
+ * then if the driver receives another TWT teardown request with broadcast
+ * peer mac, then API mlme_twt_any_peer_cmd_in_progress() shall iterate
+ * through the list of all peers and returns command in progress as true.
+ *
+ * If the input @peer_mac is a non-broadcast MAC address then
+ * mlme_sap_twt_peer_is_cmd_in_progress() shall check only for that
+ * particular @peer_mac and @dialog_id.
+ *
+ * Return: true if command is in progress, false otherwise
+ */
+static bool
+sme_sap_twt_is_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				   uint8_t vdev_id,
+				   struct qdf_mac_addr *peer_mac,
+				   uint8_t dialog_id,
+				   enum wlan_twt_commands cmd)
+{
+	if (qdf_is_macaddr_broadcast(peer_mac)) {
+		return mlme_twt_any_peer_cmd_in_progress(psoc, vdev_id,
+							 dialog_id, cmd);
+	} else {
+		return mlme_sap_twt_peer_is_cmd_in_progress(psoc, peer_mac,
+							    dialog_id, cmd);
+	}
+}
+
+/**
+ * sme_sap_add_twt_session() - Based on the input peer mac address
+ * invoke the appropriate function to add dialog_id to the TWT session context
+ * @psoc: Pointer to psoc object
+ * @vdev_id: Vdev id
+ * @peer_mac: Peer MAC address
+ * @dialog_id: Dialog id
+ *
+ * If the input @peer_mac is a broadcast MAC address then there is nothing
+ * to do, because the initialized structure is already in the expected format
+ * Note: If @peer_mac is broadcast MAC address then @dialog_id shall always
+ * be WLAN_ALL_SESSIONS_DIALOG_ID.
+ *
+ * If the input @peer_mac is a non-broadcast MAC address then
+ * mlme_add_twt_session() shall add the @dialog_id to the @peer_mac
+ * TWT session context.
+ *
+ * Return: None
+ */
+static void
+sme_sap_add_twt_session(struct wlan_objmgr_psoc *psoc,
+			uint8_t vdev_id,
+			struct qdf_mac_addr *peer_mac,
+			uint8_t dialog_id)
+{
+	if (!qdf_is_macaddr_broadcast(peer_mac))
+		mlme_add_twt_session(psoc, peer_mac, dialog_id);
+}
+
+/**
+ * sme_sap_set_twt_command_in_progress() - Based on the input peer mac address
+ * invoke the appropriate function to set the command is in progress
+ * @psoc: Pointer to psoc object
+ * @vdev_id: Vdev id
+ * @peer_mac: Peer MAC address
+ * @dialog_id: Dialog id
+ * @cmd: command
+ *
+ * If the input @peer_mac is a broadcast MAC address then the expectation is
+ * to iterate through the list of all peers and set the active command to @cmd
+ * for the given @dialog_id
+ * Note: If @peer_mac is broadcast MAC address then @dialog_id shall always
+ * be WLAN_ALL_SESSIONS_DIALOG_ID.
+ * For ex: If TWT teardown command is issued on broadcast @peer_mac, then
+ * it is same as issuing TWT teardown for all the peers (all TWT sessions).
+ * Invoking mlme_sap_set_twt_all_peers_cmd_in_progress() shall iterate through
+ * all the peers and set the active command to @cmd.
+ *
+ * If the input @peer_mac is a non-broadcast MAC address then
+ * mlme_set_twt_command_in_progress() shall set the active command to @cmd
+ * only for that particular @peer_mac and @dialog_id.
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+sme_sap_set_twt_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				    uint8_t vdev_id,
+				    struct qdf_mac_addr *peer_mac,
+				    uint8_t dialog_id,
+				    enum wlan_twt_commands cmd)
+{
+	if (qdf_is_macaddr_broadcast(peer_mac)) {
+		return mlme_sap_set_twt_all_peers_cmd_in_progress(psoc,
+								  vdev_id,
+								  dialog_id,
+								  cmd);
+	} else {
+		return mlme_set_twt_command_in_progress(psoc, peer_mac,
+							dialog_id, cmd);
+	}
+}
+
+/**
+ * sme_sap_init_twt_context() - Based on the input peer mac address
+ * invoke the appropriate function to initialize the TWT session context
+ * @psoc: Pointer to psoc object
+ * @vdev_id: Vdev id
+ * @peer_mac: Peer MAC address
+ * @dialog_id: Dialog id
+ *
+ * If the input @peer_mac is a broadcast MAC address then the expectation is
+ * to iterate through the list of all peers and initialize the TWT session
+ * context
+ * Note: If @peer_mac is broadcast MAC address then @dialog_id shall always
+ * be WLAN_ALL_SESSIONS_DIALOG_ID.
+ * For ex: If TWT teardown command is issued on broadcast @peer_mac, then
+ * it is same as issuing TWT teardown for all the peers (all TWT sessions).
+ * Then active command for all the peers is set to @WLAN_TWT_TERMINATE.
+ * Upon receiving the TWT teardown WMI event, mlme_init_all_peers_twt_context()
+ * shall iterate through the list of all peers and initializes the TWT session
+ * context back to its initial state.
+ *
+ * If the input @peer_mac is a non-broadcast MAC address then
+ * mlme_init_twt_context() shall initialize the TWT session context
+ * only for that particular @peer_mac and @dialog_id.
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+sme_sap_init_twt_context(struct wlan_objmgr_psoc *psoc,
+			 uint8_t vdev_id,
+			 struct qdf_mac_addr *peer_mac,
+			 uint8_t dialog_id)
+{
+	if (qdf_is_macaddr_broadcast(peer_mac)) {
+		return mlme_init_all_peers_twt_context(psoc, vdev_id,
+						       dialog_id);
+	} else {
+		return mlme_init_twt_context(psoc, peer_mac, dialog_id);
+	}
+}
+
 /**
  * sme_process_twt_add_renego_failure() - Process TWT re-negotiation failure
  *
@@ -2176,6 +2331,22 @@ sme_process_twt_del_dialog_event(
 		callback = mac->sme.twt_del_dialog_cb;
 		if (callback)
 			callback(mac->psoc, param);
+
+		/*
+		 * If this is an unsolicited TWT del event initiated from the
+		 * peer, then no need to clear the active command in progress
+		 */
+		if (param->status !=
+		    WMI_HOST_DEL_TWT_STATUS_PEER_INIT_TEARDOWN) {
+			/* Reset the active TWT command to none */
+			sme_sap_set_twt_command_in_progress(mac->psoc,
+				param->vdev_id,
+				(struct qdf_mac_addr *)param->peer_macaddr,
+				param->dialog_id, WLAN_TWT_NONE);
+			sme_sap_init_twt_context(mac->psoc, param->vdev_id,
+				      (struct qdf_mac_addr *)
+				      param->peer_macaddr, param->dialog_id);
+		}
 		break;
 	case QDF_STA_MODE:
 		sme_process_sta_twt_del_dialog_event(mac, param);
@@ -14105,7 +14276,7 @@ QDF_STATUS sme_del_dialog_cmd(mac_handle_t mac_handle,
 
 	status = sme_acquire_global_lock(&mac->sme);
 	if (QDF_IS_STATUS_ERROR(status)) {
-		sme_err("failed to register del dialog callback");
+		sme_err("Failed to acquire SME global lock");
 		qdf_mem_free(cmd_params);
 		return status;
 	}
@@ -14138,6 +14309,88 @@ QDF_STATUS sme_del_dialog_cmd(mac_handle_t mac_handle,
 	return status;
 }
 
+QDF_STATUS sme_sap_del_dialog_cmd(mac_handle_t mac_handle,
+				  twt_del_dialog_cb del_dialog_cb,
+				  struct wmi_twt_del_dialog_param *twt_params)
+{
+	struct mac_context *mac = MAC_CONTEXT(mac_handle);
+	struct scheduler_msg twt_msg = {0};
+	bool is_twt_cmd_in_progress;
+	QDF_STATUS status;
+	void *wma_handle;
+	struct wmi_twt_del_dialog_param *cmd_params;
+
+	SME_ENTER();
+
+	is_twt_cmd_in_progress =
+		sme_sap_twt_is_command_in_progress(mac->psoc,
+			twt_params->vdev_id,
+			(struct qdf_mac_addr *)twt_params->peer_macaddr,
+			twt_params->dialog_id, WLAN_TWT_TERMINATE);
+
+	if (is_twt_cmd_in_progress) {
+		sme_debug("Already TWT teardown command is in progress");
+		return QDF_STATUS_E_PENDING;
+	}
+
+	wma_handle = cds_get_context(QDF_MODULE_ID_WMA);
+	if (!wma_handle)
+		return QDF_STATUS_E_FAILURE;
+
+	/* bodyptr should be freeable */
+	cmd_params = qdf_mem_malloc(sizeof(*cmd_params));
+	if (!cmd_params)
+		return QDF_STATUS_E_NOMEM;
+
+	qdf_mem_copy(cmd_params, twt_params, sizeof(*cmd_params));
+
+	status = sme_acquire_global_lock(&mac->sme);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		qdf_mem_free(cmd_params);
+		sme_err("Failed to acquire SME global lock");
+		return status;
+	}
+
+	/*
+	 * Add the dialog id to TWT context to drop back to back
+	 * commands
+	 */
+	sme_sap_add_twt_session(mac->psoc, twt_params->vdev_id,
+			       (struct qdf_mac_addr *)twt_params->peer_macaddr,
+			       twt_params->dialog_id);
+
+	sme_sap_set_twt_command_in_progress(mac->psoc, twt_params->vdev_id,
+				(struct qdf_mac_addr *)twt_params->peer_macaddr,
+				twt_params->dialog_id, WLAN_TWT_TERMINATE);
+
+	/* Serialize the req through MC thread */
+	mac->sme.twt_del_dialog_cb = del_dialog_cb;
+	twt_msg.bodyptr = cmd_params;
+	twt_msg.type = WMA_TWT_DEL_DIALOG_REQUEST;
+	sme_release_global_lock(&mac->sme);
+
+	status = scheduler_post_message(QDF_MODULE_ID_SME,
+					QDF_MODULE_ID_WMA,
+					QDF_MODULE_ID_WMA, &twt_msg);
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+			  FL("Post twt del dialog msg fail"));
+		sme_sap_set_twt_command_in_progress(mac->psoc,
+				twt_params->vdev_id,
+				(struct qdf_mac_addr *)twt_params->peer_macaddr,
+				twt_params->dialog_id, WLAN_TWT_NONE);
+		sme_sap_init_twt_context(mac->psoc,
+				twt_params->vdev_id,
+				(struct qdf_mac_addr *)twt_params->peer_macaddr,
+				twt_params->dialog_id);
+		qdf_mem_free(cmd_params);
+	}
+
+	SME_EXIT();
+	return status;
+}
+
 QDF_STATUS
 sme_pause_dialog_cmd(mac_handle_t mac_handle,
 		     struct wmi_twt_pause_dialog_cmd_param *twt_params)