From 188bf78da82be575b0c353e1dc98f56a6bc9bf19 Mon Sep 17 00:00:00 2001 From: Rajasekaran Kalidoss Date: Sun, 19 Jul 2020 22:23:25 +0530 Subject: [PATCH] qcacld-3.0: Add support for TWT Terminate vendor command Add support for TWT Terminate command request and response. CRs-Fixed: 2736287 Change-Id: I710a600c53ad2b0f805ade70f254ea6aedcd4216 --- core/hdd/inc/wlan_hdd_twt.h | 2 + core/hdd/src/wlan_hdd_twt.c | 273 ++++++++++++++++++++++++++++++++++ core/sme/inc/sme_api.h | 15 ++ core/sme/inc/sme_internal.h | 10 ++ core/sme/src/common/sme_api.c | 36 +++++ core/wma/inc/wma_internal.h | 11 ++ core/wma/src/wma_main.c | 5 + core/wma/src/wma_twt.c | 34 +++++ 8 files changed, 386 insertions(+) diff --git a/core/hdd/inc/wlan_hdd_twt.h b/core/hdd/inc/wlan_hdd_twt.h index adbe8a8ce80e..730e8682c1a3 100644 --- a/core/hdd/inc/wlan_hdd_twt.h +++ b/core/hdd/inc/wlan_hdd_twt.h @@ -35,6 +35,8 @@ struct hdd_context; struct hdd_adapter; struct wma_tgt_cfg; struct wmi_twt_add_dialog_param; +struct wmi_twt_del_dialog_param; + extern const struct nla_policy qca_wlan_vendor_twt_add_dialog_policy[ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1]; diff --git a/core/hdd/src/wlan_hdd_twt.c b/core/hdd/src/wlan_hdd_twt.c index 6a8c66700f27..3e84c8c6be66 100644 --- a/core/hdd/src/wlan_hdd_twt.c +++ b/core/hdd/src/wlan_hdd_twt.c @@ -36,6 +36,7 @@ #define TWT_SETUP_COMPLETE_TIMEOUT 1000 #define TWT_DISABLE_COMPLETE_TIMEOUT 1000 +#define TWT_TERMINATE_COMPLETE_TIMEOUT 1000 /** * struct twt_add_dialog_complete_event - TWT add dialog complete event @@ -61,6 +62,17 @@ struct twt_add_dialog_comp_ev_priv { struct twt_add_dialog_complete_event add_dialog_comp_ev_buf; }; +/** + * struct twt_del_dialog_comp_ev_priv - private struct for twt del dialog + * @del_dialog_comp_ev_buf: buffer from TWT del dialog complete_event + * + * This TWT del dialog private structure is registered with os_if to + * retrieve the TWT del dialog response event buffer. + */ +struct twt_del_dialog_comp_ev_priv { + struct wmi_twt_del_dialog_complete_event_param del_dialog_comp_ev_buf; +}; + const struct nla_policy wlan_hdd_wifi_twt_config_policy[ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_MAX + 1] = { @@ -130,6 +142,37 @@ int wmi_twt_add_cmd_to_vendor_twt_resp_type(enum WMI_HOST_TWT_COMMAND type) } } +/** + * wmi_twt_del_status_to_vendor_twt_status() - convert from + * WMI_HOST_DEL_TWT_STATUS to qca_wlan_vendor_twt_status + * @status: WMI_HOST_DEL_TWT_STATUS value from firmare + * + * Return: qca_wlan_vendor_twt_status values corresponsing + * to the firmware failure status + */ +static +int wmi_twt_del_status_to_vendor_twt_status(enum WMI_HOST_DEL_TWT_STATUS status) +{ + switch (status) { + case WMI_HOST_DEL_TWT_STATUS_OK: + return QCA_WLAN_VENDOR_TWT_STATUS_OK; + case WMI_HOST_DEL_TWT_STATUS_DIALOG_ID_NOT_EXIST: + return QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST; + case WMI_HOST_DEL_TWT_STATUS_INVALID_PARAM: + return QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM; + case WMI_HOST_DEL_TWT_STATUS_DIALOG_ID_BUSY: + return QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY; + case WMI_HOST_DEL_TWT_STATUS_NO_RESOURCE: + return QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE; + case WMI_HOST_DEL_TWT_STATUS_NO_ACK: + return QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK; + case WMI_HOST_DEL_TWT_STATUS_UNKNOWN_ERROR: + return QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR; + default: + return QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR; + } +} + /** * wmi_twt_add_status_to_vendor_twt_status() - convert from * WMI_HOST_ADD_TWT_STATUS to qca_wlan_vendor_twt_status @@ -461,6 +504,235 @@ static int hdd_twt_setup_session(struct hdd_adapter *adapter, return ret; } +/** + * hdd_get_twt_terminate_event_len() - calculate length of skb + * required for sending twt terminate response. + * + * Return: length of skb + */ +static uint32_t hdd_get_twt_terminate_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)); + + return len; +} + +/** + * hdd_twt_terminate_pack_resp_nlmsg() - pack the skb with + * firmware response for twt terminate command + * @reply_skb: skb to store the response + * @params: Pointer to del dialog complete event buffer + * + * Return: QDF_STATUS + */ +static QDF_STATUS +hdd_twt_terminate_pack_resp_nlmsg(struct sk_buff *reply_skb, + struct wmi_twt_del_dialog_complete_event_param *params) +{ + int vendor_status; + + if (nla_put_u8(reply_skb, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID, + params->dialog_id)) { + hdd_debug("TWT: Failed to put dialog_id"); + return QDF_STATUS_E_FAILURE; + } + + vendor_status = wmi_twt_del_status_to_vendor_twt_status(params->status); + if (nla_put_u8(reply_skb, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS, + vendor_status)) { + hdd_err("TWT: Failed to put QCA_WLAN_TWT_TERMINATE"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_twt_del_dialog_comp_cb() - callback function + * to get twt terminate command complete event + * @context: private context + * @params: Pointer to del dialog complete event buffer + * + * Return: None + */ +static void +hdd_twt_del_dialog_comp_cb(void *context, + struct wmi_twt_del_dialog_complete_event_param *params) +{ + struct osif_request *request; + struct twt_del_dialog_comp_ev_priv *priv; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + qdf_mem_copy(&priv->del_dialog_comp_ev_buf, params, + sizeof(*params)); + osif_request_complete(request); + osif_request_put(request); + + hdd_debug("TWT: del dialog_id:%d, status:%d vdev_id %d peer mac_addr " + QDF_MAC_ADDR_FMT, params->dialog_id, + params->status, params->vdev_id, + QDF_MAC_ADDR_REF(params->peer_macaddr)); + + hdd_exit(); +} + +/** + * hdd_send_twt_del_dialog_cmd() - Send TWT del dialog command to target + * @hdd_ctx: HDD Context + * @twt_params: Pointer to del dialog cmd params structure + * + * Return: QDF_STATUS + */ +static +int hdd_send_twt_del_dialog_cmd(struct hdd_context *hdd_ctx, + struct wmi_twt_del_dialog_param *twt_params) +{ + struct wmi_twt_del_dialog_complete_event_param *del_dialog_comp_ev_params; + struct twt_del_dialog_comp_ev_priv *priv; + static const struct osif_request_params osif_req_params = { + .priv_size = sizeof(*priv), + .timeout_ms = TWT_TERMINATE_COMPLETE_TIMEOUT, + .dealloc = NULL, + }; + struct osif_request *request; + struct sk_buff *reply_skb = NULL; + QDF_STATUS status; + void *cookie; + int skb_len; + int ret = 0; + + request = osif_request_alloc(&osif_req_params); + if (!request) { + hdd_err("twt osif request allocation failure"); + ret = -ENOMEM; + goto err; + } + + cookie = osif_request_cookie(request); + + status = sme_del_dialog_cmd(hdd_ctx->mac_handle, + hdd_twt_del_dialog_comp_cb, + twt_params, + cookie); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to send del dialog command"); + ret = qdf_status_to_os_return(status); + goto err; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("twt: del dialog req timedout"); + ret = -ETIMEDOUT; + goto err; + } + + priv = osif_request_priv(request); + del_dialog_comp_ev_params = &priv->del_dialog_comp_ev_buf; + + skb_len = hdd_get_twt_terminate_event_len(); + reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + skb_len); + if (!reply_skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + ret = -ENOMEM; + goto err; + } + + status = hdd_twt_terminate_pack_resp_nlmsg(reply_skb, + del_dialog_comp_ev_params); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to pack nl del dialog response"); + ret = qdf_status_to_os_return(status); + goto err; + } + + ret = wlan_cfg80211_vendor_cmd_reply(reply_skb); + +err: + if (request) + osif_request_put(request); + if (ret && reply_skb) + kfree_skb(reply_skb); + return ret; +} + +/** + * hdd_twt_terminate_session - Process TWT terminate + * operation in the recevied 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) +{ + struct hdd_station_ctx *hdd_sta_ctx = NULL; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1]; + struct wmi_twt_del_dialog_param params = {0}; + int id; + int ret; + + if (adapter->device_mode != QDF_STA_MODE && + adapter->device_mode != QDF_P2P_CLIENT_MODE) { + return -EOPNOTSUPP; + } + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (hdd_sta_ctx->conn_info.conn_state != eConnectionState_Associated) { + hdd_err_rl("Invalid state, vdev %d mode %d state %d", + adapter->vdev_id, adapter->device_mode, + hdd_sta_ctx->conn_info.conn_state); + return -EINVAL; + } + + qdf_mem_copy(params.peer_macaddr, + hdd_sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE); + 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; + if (tb[id]) { + params.dialog_id = nla_get_u8(tb[id]); + } else { + params.dialog_id = 0; + hdd_debug("TWT_TERMINATE_FLOW_ID not specified. set to zero"); + } + + hdd_debug("twt_terminate: 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_twt_del_dialog_cmd(adapter->hdd_ctx, ¶ms); + + return ret; +} + /** * hdd_twt_configure - Process the TWT * operation in the recevied vendor command @@ -506,6 +778,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, twt_param_attr); break; case QCA_WLAN_TWT_SUSPEND: break; diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h index d172a8793c5f..69dfa58c3343 100644 --- a/core/sme/inc/sme_api.h +++ b/core/sme/inc/sme_api.h @@ -3647,6 +3647,21 @@ QDF_STATUS sme_add_dialog_cmd(mac_handle_t mac_handle, struct wmi_twt_add_dialog_param *twt_params, void *context); +/** + * sme_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 + * @context: os_if_request cookie + * + * Return: QDF Status + */ +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, + void *context); + /** * sme_deregister_twt_enable_complete_cb() - TWT enable deregistrar * @mac_handle: Opaque handle to the global MAC context diff --git a/core/sme/inc/sme_internal.h b/core/sme/inc/sme_internal.h index 71836744e25c..84540f728f20 100644 --- a/core/sme/inc/sme_internal.h +++ b/core/sme/inc/sme_internal.h @@ -169,6 +169,15 @@ typedef void (*twt_disable_cb)(hdd_handle_t hdd_handle); typedef void (*twt_add_dialog_cb)(void *context, struct wmi_twt_add_dialog_complete_event_param *params, struct wmi_twt_add_dialog_additional_params *additional_params); + +/** + * typedef twt_del_dialog_cb - TWT delete dialog callback signature. + * @context: Opaque context that the client can use to associate the + * callback with the request. + * @params: TWT delete dialog complete event parameters. + */ +typedef void (*twt_del_dialog_cb)(void *context, + struct wmi_twt_del_dialog_complete_event_param *params); #endif #ifdef FEATURE_WLAN_APF @@ -379,6 +388,7 @@ struct sme_context { twt_enable_cb twt_enable_cb; twt_disable_cb twt_disable_cb; twt_add_dialog_cb twt_add_dialog_cb; + twt_del_dialog_cb twt_del_dialog_cb; void *twt_context; #endif #ifdef FEATURE_WLAN_APF diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index 8a958019308a..65f7714f6232 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -14486,6 +14486,42 @@ QDF_STATUS sme_add_dialog_cmd(mac_handle_t mac_handle, return status; } +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, + void *context) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + void *wma_handle; + + SME_ENTER(); + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.twt_del_dialog_cb = del_dialog_cb; + mac->sme.twt_context = context; + sme_release_global_lock(&mac->sme); + } + + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("failed to register del dlg callback"); + return status; + } + + status = wma_twt_process_del_dialog(twt_params); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("failed to call wma_twt_process_del_dialog"); + + SME_EXIT(); + return status; +} + #endif QDF_STATUS sme_set_smps_cfg(uint32_t vdev_id, uint32_t param_id, diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h index 5537f01b4b70..f5646c87952d 100644 --- a/core/wma/inc/wma_internal.h +++ b/core/wma/inc/wma_internal.h @@ -1738,6 +1738,17 @@ int wma_twt_disable_comp_event_handler(void *handle, uint8_t *event, */ int wma_twt_add_dialog_complete_event_handler(void *handle, uint8_t *event, uint32_t len); + +/** + * wma_twt_del_dialog_complete_event_handler - TWT del dlg complete evt handler + * @handle: wma handle + * @event: buffer with event + * @len: buffer length + * + * Return: 0 on success + */ +int wma_twt_del_dialog_complete_event_handler(void *handle, + uint8_t *event, uint32_t len); #endif /** diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index 13d74d5700e3..f4dd0c14d116 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -3369,6 +3369,11 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc, wmi_twt_add_dialog_complete_event_id, wma_twt_add_dialog_complete_event_handler, WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler + (wma_handle->wmi_handle, + wmi_twt_del_dialog_complete_event_id, + wma_twt_del_dialog_complete_event_handler, + WMA_RX_SERIALIZER_CTX); #endif wma_register_apf_events(wma_handle); diff --git a/core/wma/src/wma_twt.c b/core/wma/src/wma_twt.c index ab92e214e5d0..1cb4d0d84932 100644 --- a/core/wma/src/wma_twt.c +++ b/core/wma/src/wma_twt.c @@ -214,3 +214,37 @@ QDF_STATUS wma_twt_process_del_dialog( return wmi_unified_twt_del_dialog_cmd(wmi_handle, params); } + +int wma_twt_del_dialog_complete_event_handler(void *handle, + uint8_t *event, uint32_t len) +{ + struct wmi_twt_del_dialog_complete_event_param param; + tp_wma_handle wma_handle = handle; + wmi_unified_t wmi_handle; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + int status = -EINVAL; + + if (!wma_handle) { + wma_err("Invalid wma handle for TWT del dialog complete"); + return status; + } + wmi_handle = (wmi_unified_t)wma_handle->wmi_handle; + if (!wmi_handle) { + wma_err("Invalid wma handle for TWT del dialog complete"); + return status; + } + if (!mac) { + wma_err("Invalid MAC context"); + return status; + } + status = wmi_extract_twt_del_dialog_comp_event(wmi_handle, event, + ¶m); + wma_debug("TWT: Extract TWT del dlg comp event, status:%d", status); + + if (mac->sme.twt_del_dialog_cb) + mac->sme.twt_del_dialog_cb(mac->sme.twt_context, ¶m); + + mac->sme.twt_del_dialog_cb = NULL; + + return status; +}