Browse Source

qcacld-3.0: Add TWT sta teardown support to componentization

Add TWT sta teardown support to componentization.

Change-Id: I5a0f05105aad1aa92257af47f50913ae1e61730a
CRs-Fixed: 3085543
Srinivas Girigowda 3 years ago
parent
commit
fa14a0ad6b

+ 9 - 1
components/target_if/twt/src/target_if_ext_twt_cmd.c

@@ -43,7 +43,15 @@ QDF_STATUS
 target_if_twt_teardown_req(struct wlan_objmgr_psoc *psoc,
 			   struct twt_del_dialog_param *req)
 {
-	return QDF_STATUS_SUCCESS;
+	struct wmi_unified *wmi_handle;
+
+	wmi_handle = get_wmi_unified_hdl_from_psoc(psoc);
+	if (!wmi_handle) {
+		target_if_err("wmi_handle is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	return wmi_unified_twt_del_dialog_cmd(wmi_handle, req);
 }
 
 QDF_STATUS

+ 44 - 1
components/target_if/twt/src/target_if_ext_twt_evt.c

@@ -87,7 +87,50 @@ static int
 target_if_twt_teardown_complete_event_handler(ol_scn_t scn, uint8_t *event,
 					      uint32_t len)
 {
-	return 0;
+	QDF_STATUS qdf_status;
+	struct wmi_unified *wmi_handle;
+	struct wlan_objmgr_psoc *psoc;
+	struct twt_del_dialog_complete_event_param *data;
+	struct wlan_lmac_if_twt_rx_ops *twt_rx_ops;
+
+	TARGET_IF_ENTER();
+
+	psoc = target_if_get_psoc_from_scn_hdl(scn);
+	if (!psoc) {
+		target_if_err("psoc is null");
+		return -EINVAL;
+	}
+
+	wmi_handle = get_wmi_unified_hdl_from_psoc(psoc);
+	if (!wmi_handle) {
+		target_if_err("wmi_handle is null");
+		return -EINVAL;
+	}
+
+	twt_rx_ops = wlan_twt_get_rx_ops(psoc);
+	if (!twt_rx_ops || !twt_rx_ops->twt_teardown_comp_cb) {
+		target_if_err("No valid twt teardown complete rx ops");
+		return -EINVAL;
+	}
+
+	data = qdf_mem_malloc(sizeof(*data));
+	if (!data)
+		return -ENOMEM;
+
+	qdf_status = wmi_extract_twt_del_dialog_comp_event(wmi_handle,
+							   event, data);
+	if (QDF_IS_STATUS_ERROR(qdf_status)) {
+		target_if_err("extract twt del dialog event failed (status=%d)",
+			      qdf_status);
+		goto done;
+	}
+
+	qdf_status = twt_rx_ops->twt_teardown_comp_cb(psoc, data);
+
+done:
+	qdf_mem_free(data);
+	return qdf_status_to_os_return(qdf_status);
+
 }
 
 static int

+ 175 - 12
components/umac/twt/core/src/wlan_twt_main.c

@@ -24,6 +24,7 @@
 #include <wlan_mlme_main.h>
 #include "wlan_twt_main.h"
 #include "twt/core/src/wlan_twt_priv.h"
+#include "twt/core/src/wlan_twt_common.h"
 #include <wlan_twt_tgt_if_ext_tx_api.h>
 
 /**
@@ -76,16 +77,7 @@ wlan_twt_add_session(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
-/**
- * wlan_twt_set_command_in_progress() - Set TWT command is in progress
- * @psoc: Pointer to psoc object
- * @peer_mac: Pointer to peer mac address
- * @dialog_id: Dialog id
- * @cmd: TWT command
- *
- * Return: QDF_STATUS
- */
-static QDF_STATUS
+QDF_STATUS
 wlan_twt_set_command_in_progress(struct wlan_objmgr_psoc *psoc,
 				 struct qdf_mac_addr *peer_mac,
 				 uint8_t dialog_id,
@@ -846,7 +838,40 @@ wlan_twt_sta_teardown_req(struct wlan_objmgr_psoc *psoc,
 			  struct twt_del_dialog_param *req,
 			  void *context)
 {
-	return QDF_STATUS_SUCCESS;
+	bool cmd_in_progress;
+	enum wlan_twt_commands active_cmd = WLAN_TWT_NONE;
+	QDF_STATUS status;
+
+	if (!wlan_twt_is_setup_done(psoc, &req->peer_macaddr, req->dialog_id)) {
+		twt_err("vdev%d: TWT session %d setup incomplete",
+			  req->vdev_id, req->dialog_id);
+		return QDF_STATUS_E_AGAIN;
+	}
+
+	cmd_in_progress =
+		wlan_twt_is_command_in_progress(psoc, &req->peer_macaddr,
+			req->dialog_id, WLAN_TWT_SETUP, &active_cmd) ||
+		wlan_twt_is_command_in_progress(
+			psoc, &req->peer_macaddr, req->dialog_id,
+			WLAN_TWT_TERMINATE, &active_cmd);
+	if (cmd_in_progress) {
+		twt_debug("Already TWT command:%d is in progress", active_cmd);
+		return QDF_STATUS_E_PENDING;
+	}
+
+	wlan_twt_set_ack_context(psoc, &req->peer_macaddr,
+				 req->dialog_id, context);
+	wlan_twt_set_command_in_progress(psoc, &req->peer_macaddr,
+					 req->dialog_id, WLAN_TWT_TERMINATE);
+
+	status = tgt_twt_teardown_req_send(psoc, req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		twt_err("tgt_twt_teardown_req_send failed (status=%d)", status);
+		wlan_twt_set_command_in_progress(psoc, &req->peer_macaddr,
+						 req->dialog_id, WLAN_TWT_NONE);
+	}
+
+	return status;
 }
 
 /**
@@ -869,7 +894,47 @@ wlan_twt_teardown_req(struct wlan_objmgr_psoc *psoc,
 		      struct twt_del_dialog_param *req,
 		      void *context)
 {
-	return QDF_STATUS_SUCCESS;
+	enum QDF_OPMODE opmode;
+	uint32_t pdev_id;
+	struct wlan_objmgr_pdev *pdev;
+	QDF_STATUS status;
+
+	pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, req->vdev_id,
+						WLAN_TWT_ID);
+	if (pdev_id == WLAN_INVALID_PDEV_ID) {
+		twt_err("Invalid pdev id");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	pdev = wlan_objmgr_get_pdev_by_id(psoc, pdev_id, WLAN_TWT_ID);
+	if (!pdev) {
+		twt_err("Invalid pdev");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	status = wlan_twt_check_all_twt_support(psoc, req->dialog_id);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		twt_err("All TWT sessions not supported by target");
+		return status;
+	}
+
+	opmode = wlan_get_opmode_from_vdev_id(pdev, req->vdev_id);
+
+	switch (opmode) {
+	case QDF_SAP_MODE:
+		status = wlan_twt_sap_teardown_req(psoc, req);
+		break;
+	case QDF_STA_MODE:
+		status = wlan_twt_sta_teardown_req(psoc, req, context);
+		break;
+	default:
+		twt_err("TWT teardown not supported in mode: %d", opmode);
+		status = QDF_STATUS_E_INVAL;
+		break;
+	}
+
+	wlan_objmgr_pdev_release_ref(pdev, WLAN_TWT_ID);
+	return status;
 }
 
 QDF_STATUS
@@ -1132,6 +1197,18 @@ cleanup:
 static bool
 wlan_is_twt_teardown_failed(enum HOST_TWT_DEL_STATUS teardown_status)
 {
+	switch (teardown_status) {
+	case HOST_TWT_DEL_STATUS_DIALOG_ID_NOT_EXIST:
+	case HOST_TWT_DEL_STATUS_INVALID_PARAM:
+	case HOST_TWT_DEL_STATUS_DIALOG_ID_BUSY:
+	case HOST_TWT_DEL_STATUS_NO_RESOURCE:
+	case HOST_TWT_DEL_STATUS_NO_ACK:
+	case HOST_TWT_DEL_STATUS_UNKNOWN_ERROR:
+		return true;
+	default:
+		return false;
+	}
+
 	return false;
 }
 
@@ -1139,12 +1216,98 @@ static void
 wlan_twt_handle_sta_del_dialog_event(struct wlan_objmgr_psoc *psoc,
 			      struct twt_del_dialog_complete_event_param *event)
 {
+	bool is_evt_allowed, usr_cfg_ps_enable;
+	enum wlan_twt_commands active_cmd = WLAN_TWT_NONE;
+
+	is_evt_allowed = wlan_twt_is_command_in_progress(
+					psoc, &event->peer_macaddr,
+					event->dialog_id,
+					WLAN_TWT_TERMINATE, &active_cmd);
+	if (!is_evt_allowed &&
+	    event->dialog_id != TWT_ALL_SESSIONS_DIALOG_ID &&
+	    event->status != HOST_TWT_DEL_STATUS_ROAMING &&
+	    event->status != HOST_TWT_DEL_STATUS_PEER_INIT_TEARDOWN &&
+	    event->status != HOST_TWT_DEL_STATUS_CONCURRENCY) {
+		twt_err("Drop TWT Del dialog event for dialog_id:%d status:%d active_cmd:%d",
+			event->dialog_id, event->status, active_cmd);
+
+		return;
+	}
+
+	usr_cfg_ps_enable = mlme_get_user_ps(psoc, event->vdev_id);
+	if (!usr_cfg_ps_enable &&
+	    event->status == HOST_TWT_DEL_STATUS_OK)
+		event->status = HOST_TWT_DEL_STATUS_PS_DISABLE_TEARDOWN;
+
+	mlme_twt_osif_teardown_complete_ind(psoc, event);
+
+	if (event->status == HOST_TWT_DEL_STATUS_ROAMING ||
+	    event->status == HOST_TWT_DEL_STATUS_CONCURRENCY)
+		wlan_twt_set_wait_for_notify(psoc, event->vdev_id, true);
+
+	wlan_twt_set_command_in_progress(psoc, &event->peer_macaddr,
+					 event->dialog_id, WLAN_TWT_NONE);
+
+	if (wlan_is_twt_teardown_failed(event->status))
+		return;
+
+	wlan_twt_set_setup_done(psoc, &event->peer_macaddr,
+				event->dialog_id, false);
+	wlan_twt_set_session_state(psoc, &event->peer_macaddr, event->dialog_id,
+				   WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED);
+	wlan_twt_init_context(psoc, &event->peer_macaddr, event->dialog_id);
 }
 
 QDF_STATUS
 wlan_twt_teardown_complete_event_handler(struct wlan_objmgr_psoc *psoc,
 			      struct twt_del_dialog_complete_event_param *event)
 {
+	enum QDF_OPMODE opmode;
+	uint32_t pdev_id, vdev_id;
+	struct wlan_objmgr_pdev *pdev;
+
+	vdev_id = event->vdev_id;
+	pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, vdev_id, WLAN_TWT_ID);
+	if (pdev_id == WLAN_INVALID_PDEV_ID) {
+		twt_err("Invalid pdev id");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	pdev = wlan_objmgr_get_pdev_by_id(psoc, pdev_id, WLAN_TWT_ID);
+	if (!pdev) {
+		twt_err("Invalid pdev");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	opmode = wlan_get_opmode_from_vdev_id(pdev, vdev_id);
+
+	switch (opmode) {
+	case QDF_SAP_MODE:
+		mlme_twt_osif_teardown_complete_ind(psoc, event);
+
+		/*
+		 * If this is an unsolicited TWT del event initiated from the
+		 * peer, then no need to clear the active command in progress
+		 */
+		if (event->status != HOST_TWT_DEL_STATUS_PEER_INIT_TEARDOWN) {
+			/* Reset the active TWT command to none */
+			wlan_twt_sap_set_command_in_progress(psoc,
+				event->vdev_id, &event->peer_macaddr,
+				event->dialog_id, WLAN_TWT_NONE);
+			wlan_twt_sap_init_context(psoc, event->vdev_id,
+				&event->peer_macaddr, event->dialog_id);
+		}
+		break;
+	case QDF_STA_MODE:
+		wlan_twt_handle_sta_del_dialog_event(psoc, event);
+		break;
+	default:
+		twt_debug("TWT Teardown is not supported on %s",
+				  qdf_opmode_str(opmode));
+		break;
+	}
+
+	wlan_objmgr_pdev_release_ref(pdev, WLAN_TWT_ID);
 	return QDF_STATUS_SUCCESS;
 }
 

+ 15 - 0
components/umac/twt/core/src/wlan_twt_main.h

@@ -36,6 +36,21 @@ wlan_twt_is_max_sessions_reached(struct wlan_objmgr_psoc *psoc,
 				 struct qdf_mac_addr *peer_mac,
 				 uint8_t dialog_id);
 
+/**
+ * wlan_twt_set_command_in_progress() - Set TWT command is in progress
+ * @psoc: Pointer to psoc object
+ * @peer_mac: Pointer to peer mac address
+ * @dialog_id: Dialog id
+ * @cmd: TWT command
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+wlan_twt_set_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				 struct qdf_mac_addr *peer_mac,
+				 uint8_t dialog_id,
+				 enum wlan_twt_commands cmd);
+
 /**
  * wlan_twt_setup_req() - twt setup request
  * @psoc: Pointer to psoc object

+ 28 - 0
components/umac/twt/dispatcher/inc/wlan_twt_ucfg_ext_api.h

@@ -113,6 +113,34 @@ bool ucfg_twt_is_setup_in_progress(struct wlan_objmgr_psoc *psoc,
 				   struct qdf_mac_addr *peer_mac,
 				   uint8_t dialog_id);
 
+/**
+ * ucfg_twt_set_command_in_progress() - Set TWT command is in progress
+ * @psoc: Pointer to psoc object
+ * @peer_mac: Pointer to peer mac address
+ * @dialog_id: Dialog id
+ * @cmd: TWT command
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+ucfg_twt_set_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				 struct qdf_mac_addr *peer_mac,
+				 uint8_t dialog_id,
+				 enum wlan_twt_commands cmd);
+
+/**
+ * ucfg_twt_reset_active_command() - Reset active command to WLAN_TWT_NONE
+ * @psoc: Pointer to psoc object
+ * @peer_mac: Pointer to peer mac address
+ * @dialog_id: Dialog id
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+ucfg_twt_reset_active_command(struct wlan_objmgr_psoc *psoc,
+			      struct qdf_mac_addr *peer_mac,
+			      uint8_t dialog_id);
+
 /**
  * ucfg_twt_init_context() - Initialize TWT context
  * @psoc: Pointer to global psoc object

+ 1 - 1
components/umac/twt/dispatcher/src/wlan_twt_tgt_if_ext_rx_api.c

@@ -37,7 +37,7 @@ static QDF_STATUS
 tgt_twt_teardown_complete_resp_handler(struct wlan_objmgr_psoc *psoc,
 			     struct twt_del_dialog_complete_event_param *event)
 {
-	return QDF_STATUS_SUCCESS;
+	return wlan_twt_teardown_complete_event_handler(psoc, event);
 }
 
 static QDF_STATUS

+ 23 - 1
components/umac/twt/dispatcher/src/wlan_twt_tgt_if_ext_tx_api.c

@@ -61,7 +61,29 @@ QDF_STATUS
 tgt_twt_teardown_req_send(struct wlan_objmgr_psoc *psoc,
 			  struct twt_del_dialog_param *req)
 {
-	return QDF_STATUS_SUCCESS;
+	struct wlan_lmac_if_twt_tx_ops *tx_ops;
+	QDF_STATUS status;
+
+	if (!psoc) {
+		twt_err("psoc is null");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!req) {
+		twt_err("Invalid input");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	tx_ops = wlan_twt_get_tx_ops(psoc);
+	if (!tx_ops || !tx_ops->teardown_req) {
+		twt_err("teardown_req tx_ops is null");
+		status = QDF_STATUS_E_NULL_VALUE;
+		return status;
+	}
+
+	status = tx_ops->teardown_req(psoc, req);
+
+	return status;
 }
 
 QDF_STATUS

+ 20 - 0
components/umac/twt/dispatcher/src/wlan_twt_ucfg_ext_api.c

@@ -121,3 +121,23 @@ ucfg_twt_set_osif_cb(osif_twt_get_global_ops_cb osif_twt_ops)
 	mlme_set_osif_twt_cb(osif_twt_ops);
 	return QDF_STATUS_SUCCESS;
 }
+
+QDF_STATUS
+ucfg_twt_set_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				 struct qdf_mac_addr *peer_mac,
+				 uint8_t dialog_id,
+				 enum wlan_twt_commands cmd)
+{
+	return wlan_twt_set_command_in_progress(psoc, peer_mac,
+						dialog_id, cmd);
+}
+
+QDF_STATUS
+ucfg_twt_reset_active_command(struct wlan_objmgr_psoc *psoc,
+			      struct qdf_mac_addr *peer_mac,
+			      uint8_t dialog_id)
+{
+	return wlan_twt_set_command_in_progress(psoc, peer_mac, dialog_id,
+						WLAN_TWT_NONE);
+}
+

+ 36 - 0
core/hdd/src/wlan_hdd_twt.c

@@ -87,6 +87,12 @@ QDF_STATUS hdd_send_twt_requestor_enable_cmd(struct hdd_context *hdd_ctx)
 	return QDF_STATUS_SUCCESS;
 }
 
+void hdd_twt_del_dialog_in_ps_disable(struct hdd_context *hdd_ctx,
+				      struct qdf_mac_addr *mac_addr,
+				      uint8_t vdev_id)
+{
+}
+
 void hdd_send_twt_role_disable_cmd(struct hdd_context *hdd_ctx,
 				   enum twt_role role)
 {
@@ -124,6 +130,35 @@ QDF_STATUS hdd_send_twt_responder_disable_cmd(struct hdd_context *hdd_ctx)
 	return QDF_STATUS_SUCCESS;
 }
 
+/**
+ * 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 wlan_objmgr_vdev *vdev,
+				     struct nlattr *twt_param_attr)
+{
+	enum QDF_OPMODE device_mode = adapter->device_mode;
+
+	switch (device_mode) {
+	case QDF_STA_MODE:
+		return osif_twt_sta_teardown_req(vdev, twt_param_attr);
+	case QDF_SAP_MODE:
+		return osif_twt_sap_teardown_req(vdev, twt_param_attr);
+	default:
+		hdd_err_rl("TWT terminate is not supported on %s",
+			   qdf_opmode_str(adapter->device_mode));
+		return -EOPNOTSUPP;
+	}
+}
+
 static int hdd_twt_configure(struct hdd_adapter *adapter,
 			     struct nlattr **tb)
 {
@@ -169,6 +204,7 @@ static int hdd_twt_configure(struct hdd_adapter *adapter,
 	case QCA_WLAN_TWT_GET:
 		break;
 	case QCA_WLAN_TWT_TERMINATE:
+		ret = hdd_twt_terminate_session(adapter, vdev, twt_param_attr);
 		break;
 	case QCA_WLAN_TWT_SUSPEND:
 		break;

+ 165 - 2
os_if/twt/src/osif_twt_ext_req.c

@@ -327,6 +327,43 @@ osif_twt_parse_add_dialog_attrs(struct nlattr **tb,
 	return 0;
 }
 
+/**
+ * osif_twt_parse_del_dialog_attrs() - Parse TWT del dialog parameters
+ * values from QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS
+ * @tb: nl attributes
+ * @params: twt del dialog parameters
+ *
+ * Handles QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX
+ *
+ * Return: 0 or -EINVAL.
+ */
+static int
+osif_twt_parse_del_dialog_attrs(struct nlattr **tb,
+				struct twt_del_dialog_param *params)
+{
+	int cmd_id;
+
+	cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID;
+	if (tb[cmd_id]) {
+		params->dialog_id = nla_get_u8(tb[cmd_id]);
+	} else {
+		params->dialog_id = 0;
+		osif_debug("TWT_TERMINATE_FLOW_ID not specified. set to zero");
+	}
+
+	cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_ID;
+	if (tb[cmd_id]) {
+		params->dialog_id = nla_get_u8(tb[cmd_id]);
+		osif_debug("TWT_SETUP_BCAST_ID %d", params->dialog_id);
+	}
+
+	osif_debug("twt: dialog_id %d vdev %d peer mac_addr "QDF_MAC_ADDR_FMT,
+		   params->dialog_id, params->vdev_id,
+		   QDF_MAC_ADDR_REF(params->peer_macaddr.bytes));
+
+	return 0;
+}
+
 static int osif_fill_peer_macaddr(struct wlan_objmgr_vdev *vdev,
 				  uint8_t *mac_addr)
 {
@@ -469,6 +506,84 @@ cleanup:
 	return ret;
 }
 
+static int
+osif_send_sta_twt_teardown_req(struct wlan_objmgr_vdev *vdev,
+			       struct wlan_objmgr_psoc *psoc,
+			       struct twt_del_dialog_param *twt_params)
+{
+	QDF_STATUS status;
+	int twt_cmd, ret = 0;
+	struct osif_request *request;
+	struct twt_ack_context *ack_priv;
+	void *context;
+	static const struct osif_request_params params = {
+				.priv_size = sizeof(*ack_priv),
+				.timeout_ms = TWT_ACK_COMPLETE_TIMEOUT,
+	};
+
+	request = osif_request_alloc(&params);
+	if (!request) {
+		osif_err("Request allocation failure");
+		return -ENOMEM;
+	}
+
+	context = osif_request_cookie(request);
+
+	status = ucfg_twt_teardown_req(psoc, twt_params, context);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		ret = qdf_status_to_os_return(status);
+		osif_err("Failed to send del dialog command");
+		goto cleanup;
+	}
+
+	twt_cmd = HOST_TWT_DEL_DIALOG_CMDID;
+
+	status = osif_twt_ack_wait_response(psoc, request, twt_cmd);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		ucfg_twt_reset_active_command(psoc, &twt_params->peer_macaddr,
+					      twt_params->dialog_id);
+		ret = qdf_status_to_os_return(status);
+		goto cleanup;
+	}
+
+	ack_priv = osif_request_priv(request);
+	if (ack_priv->status != HOST_TWT_DEL_STATUS_OK) {
+		osif_err("Received TWT ack error:%d. Reset twt command",
+			  ack_priv->status);
+
+		switch (ack_priv->status) {
+		case HOST_TWT_DEL_STATUS_INVALID_PARAM:
+		case HOST_TWT_DEL_STATUS_UNKNOWN_ERROR:
+			ret = -EINVAL;
+			break;
+		case HOST_TWT_DEL_STATUS_DIALOG_ID_NOT_EXIST:
+			ret = -EAGAIN;
+			break;
+		case HOST_TWT_DEL_STATUS_DIALOG_ID_BUSY:
+			ret = -EINPROGRESS;
+			break;
+		case HOST_TWT_DEL_STATUS_NO_RESOURCE:
+			ret = -ENOMEM;
+			break;
+		case HOST_TWT_DEL_STATUS_ROAMING:
+		case HOST_TWT_DEL_STATUS_CHAN_SW_IN_PROGRESS:
+		case HOST_TWT_DEL_STATUS_SCAN_IN_PROGRESS:
+			ret = -EBUSY;
+			break;
+		case HOST_TWT_DEL_STATUS_CONCURRENCY:
+			ret = -EAGAIN;
+			break;
+		default:
+			ret = -EAGAIN;
+			break;
+		}
+	}
+
+cleanup:
+	osif_request_put(request);
+	return ret;
+}
+
 int osif_twt_send_requestor_enable_cmd(struct wlan_objmgr_psoc *psoc,
 				       uint8_t pdev_id)
 {
@@ -648,6 +763,54 @@ int osif_twt_sap_teardown_req(struct wlan_objmgr_vdev *vdev,
 int osif_twt_sta_teardown_req(struct wlan_objmgr_vdev *vdev,
 			      struct nlattr *twt_param_attr)
 {
-	return 0;
-}
+	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1];
+	struct wlan_objmgr_psoc *psoc;
+	int ret = 0;
+	uint8_t vdev_id, pdev_id;
+	struct twt_del_dialog_param params = {0};
+
+	psoc = wlan_vdev_get_psoc(vdev);
+	if (!psoc) {
+		osif_err("NULL psoc");
+		return -EINVAL;
+	}
 
+	vdev_id = wlan_vdev_get_id(vdev);
+
+	if (!wlan_cm_is_vdev_connected(vdev)) {
+		osif_err_rl("Not associated!, vdev %d", vdev_id);
+		/*
+		 * Return success, since STA is not associated and there is
+		 * no TWT session.
+		 */
+		return 0;
+	}
+
+	if (wlan_cm_host_roam_in_progress(psoc, vdev_id))
+		return -EBUSY;
+
+	if (wlan_get_vdev_status(vdev)) {
+		osif_err_rl("Scan in progress");
+		return -EBUSY;
+	}
+
+	ret = wlan_cfg80211_nla_parse_nested(tb2,
+					 QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX,
+					 twt_param_attr,
+					 qca_wlan_vendor_twt_add_dialog_policy);
+	if (ret)
+		return ret;
+
+	ret = osif_fill_peer_macaddr(vdev, params.peer_macaddr.bytes);
+	if (ret)
+		return ret;
+
+	params.vdev_id = vdev_id;
+	pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, vdev_id, WLAN_TWT_ID);
+
+	ret = osif_twt_parse_del_dialog_attrs(tb2, &params);
+	if (ret)
+		return ret;
+
+	return osif_send_sta_twt_teardown_req(vdev, psoc, &params);
+}

+ 182 - 0
os_if/twt/src/osif_twt_ext_rsp.c

@@ -131,6 +131,49 @@ twt_add_status_to_vendor_twt_status(enum HOST_ADD_TWT_STATUS status)
 	}
 }
 
+/**
+ * twt_del_status_to_vendor_twt_status() - convert from
+ * HOST_DEL_TWT_STATUS to qca_wlan_vendor_twt_status
+ * @status: HOST_DEL_TWT_STATUS value from firmare
+ *
+ * Return: qca_wlan_vendor_twt_status values corresponding
+ * to HOST_DEL_TWT_STATUS.
+ */
+static enum qca_wlan_vendor_twt_status
+twt_del_status_to_vendor_twt_status(enum HOST_TWT_DEL_STATUS status)
+{
+	switch (status) {
+	case HOST_TWT_DEL_STATUS_OK:
+		return QCA_WLAN_VENDOR_TWT_STATUS_OK;
+	case HOST_TWT_DEL_STATUS_DIALOG_ID_NOT_EXIST:
+		return QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST;
+	case HOST_TWT_DEL_STATUS_INVALID_PARAM:
+		return QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM;
+	case HOST_TWT_DEL_STATUS_DIALOG_ID_BUSY:
+		return QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY;
+	case HOST_TWT_DEL_STATUS_NO_RESOURCE:
+		return QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE;
+	case HOST_TWT_DEL_STATUS_NO_ACK:
+		return QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK;
+	case HOST_TWT_DEL_STATUS_UNKNOWN_ERROR:
+		return QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR;
+	case HOST_TWT_DEL_STATUS_PEER_INIT_TEARDOWN:
+		return QCA_WLAN_VENDOR_TWT_STATUS_PEER_INITIATED_TERMINATE;
+	case HOST_TWT_DEL_STATUS_ROAMING:
+		return QCA_WLAN_VENDOR_TWT_STATUS_ROAM_INITIATED_TERMINATE;
+	case HOST_TWT_DEL_STATUS_CONCURRENCY:
+		return QCA_WLAN_VENDOR_TWT_STATUS_SCC_MCC_CONCURRENCY_TERMINATE;
+	case HOST_TWT_DEL_STATUS_CHAN_SW_IN_PROGRESS:
+		return QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS;
+	case HOST_TWT_DEL_STATUS_SCAN_IN_PROGRESS:
+		return QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS;
+	case HOST_TWT_DEL_STATUS_PS_DISABLE_TEARDOWN:
+		return QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE;
+	default:
+		return QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR;
+	}
+}
+
 /**
  * twt_add_cmd_to_vendor_twt_resp_type() - convert from
  * HOST_TWT_COMMAND to qca_wlan_vendor_twt_setup_resp_type
@@ -332,6 +375,60 @@ osif_twt_setup_pack_resp_nlmsg(struct sk_buff *reply_skb,
 	return QDF_STATUS_SUCCESS;
 }
 
+/**
+ * osif_twt_teardown_pack_resp_nlmsg() - pack nlmsg response for teardown
+ * @reply_skb: pointer to the response skb structure
+ * @event: twt event buffer with firmware response
+ *
+ * Return: QDF_STATUS_SUCCESS on Success, other QDF_STATUS error codes
+ * on failure
+ */
+static QDF_STATUS
+osif_twt_teardown_pack_resp_nlmsg(struct sk_buff *reply_skb,
+			     struct twt_del_dialog_complete_event_param *event)
+{
+	struct nlattr *config_attr;
+	enum qca_wlan_vendor_twt_status vendor_status;
+	int attr;
+
+	if (nla_put_u8(reply_skb, QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION,
+		       QCA_WLAN_TWT_TERMINATE)) {
+		osif_err("Failed to put TWT operation");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	config_attr = nla_nest_start(reply_skb,
+				     QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS);
+	if (!config_attr) {
+		osif_err("nla_nest_start error");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	attr = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID;
+	if (nla_put_u8(reply_skb, attr, event->dialog_id)) {
+		osif_debug("Failed to put dialog_id");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	attr = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS;
+	vendor_status = twt_del_status_to_vendor_twt_status(event->status);
+	if (nla_put_u8(reply_skb, attr, vendor_status)) {
+		osif_err("Failed to put QCA_WLAN_TWT_TERMINATE");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	attr = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR;
+	if (nla_put(reply_skb, attr, QDF_MAC_ADDR_SIZE,
+		    event->peer_macaddr.bytes)) {
+		osif_err("Failed to put mac_addr");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	nla_nest_end(reply_skb, config_attr);
+
+	return QDF_STATUS_SUCCESS;
+}
+
 static void
 osif_twt_setup_response(struct wlan_objmgr_psoc *psoc,
 			struct twt_add_dialog_complete_event *event)
@@ -390,6 +487,82 @@ fail:
 	wlan_objmgr_vdev_release_ref(vdev, WLAN_TWT_ID);
 }
 
+/**
+ * osif_get_twt_event_len() - calculate length of skb
+ * required for sending twt terminate, pause and resume
+ * command responses.
+ *
+ * Return: length of skb
+ */
+static uint32_t osif_get_twt_event_len(void)
+{
+	uint32_t len = 0;
+
+	len += NLMSG_HDRLEN;
+	/* QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID */
+	len += nla_total_size(sizeof(u8));
+	/* QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS */
+	len += nla_total_size(sizeof(u8));
+	/* QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR*/
+	len += nla_total_size(QDF_MAC_ADDR_SIZE);
+
+	return len;
+}
+
+static void
+osif_twt_teardown_response(struct wlan_objmgr_psoc *psoc,
+			   struct twt_del_dialog_complete_event_param *event)
+{
+	struct sk_buff *twt_vendor_event;
+	struct wireless_dev *wdev;
+	struct wlan_objmgr_vdev *vdev;
+	struct vdev_osif_priv *osif_priv;
+	size_t data_len;
+	QDF_STATUS status;
+
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
+						event->vdev_id, WLAN_TWT_ID);
+	if (!vdev) {
+		osif_err("vdev is null");
+		return;
+	}
+
+	osif_priv = wlan_vdev_get_ospriv(vdev);
+	if (!osif_priv) {
+		osif_err("osif_priv is null");
+		goto fail;
+	}
+
+	wdev = osif_priv->wdev;
+	if (!wdev) {
+		osif_err("wireless dev is null");
+		goto fail;
+	}
+
+	data_len = osif_get_twt_event_len() + nla_total_size(sizeof(u8));
+	data_len += NLA_HDRLEN;
+	twt_vendor_event = wlan_cfg80211_vendor_event_alloc(
+				wdev->wiphy, wdev, data_len,
+				QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT_INDEX,
+				GFP_KERNEL);
+	if (!twt_vendor_event) {
+		osif_err("TWT: Alloc teardown resp skb fail");
+		goto fail;
+	}
+
+	status = osif_twt_teardown_pack_resp_nlmsg(twt_vendor_event, event);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		osif_err("Failed to pack nl del dialog response");
+		wlan_cfg80211_vendor_free_skb(twt_vendor_event);
+		goto fail;
+	}
+
+	wlan_cfg80211_vendor_event(twt_vendor_event, GFP_KERNEL);
+
+fail:
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TWT_ID);
+}
+
 QDF_STATUS
 osif_twt_setup_complete_cb(struct wlan_objmgr_psoc *psoc,
 			   struct twt_add_dialog_complete_event *event,
@@ -414,6 +587,15 @@ QDF_STATUS
 osif_twt_teardown_complete_cb(struct wlan_objmgr_psoc *psoc,
 			      struct twt_del_dialog_complete_event_param *event)
 {
+	uint32_t vdev_id = event->vdev_id;
+
+	osif_debug("TWT: del dialog_id:%d status:%d vdev_id:%d peer mac_addr "
+		  QDF_MAC_ADDR_FMT, event->dialog_id,
+		  event->status, vdev_id,
+		  QDF_MAC_ADDR_REF(event->peer_macaddr.bytes));
+
+	osif_twt_teardown_response(psoc, event);
+
 	return QDF_STATUS_SUCCESS;
 }