diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 6b750ede9fec..8ff4bb763660 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -5113,6 +5113,10 @@ wlan_hdd_wifi_config_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1] = { .type = NLA_UNSPEC, .len = QDF_MAC_ADDR_SIZE}, [RX_BLOCKSIZE_WINLIMIT] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL] = {.type = NLA_U32 }, }; /** @@ -5438,6 +5442,8 @@ __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, int param_id; uint32_t tx_fail_count; uint32_t ant_div_usrcfg; + uint32_t antdiv_enable, antdiv_chain; + uint32_t antdiv_selftest, antdiv_selftest_intvl; ENTER_DEV(dev); @@ -5840,6 +5846,59 @@ __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, if (ret_val != 0) return ret_val; + if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA]) { + antdiv_enable = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA]); + hdd_debug("antdiv_enable: %d", antdiv_enable); + ret_val = wma_cli_set_command((int)adapter->sessionId, + (int)WMI_PDEV_PARAM_ENA_ANT_DIV, + antdiv_enable, PDEV_CMD); + if (ret_val) { + hdd_err("Failed to set antdiv_enable"); + return ret_val; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN]) { + antdiv_chain = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN]); + hdd_debug("antdiv_chain: %d", antdiv_chain); + ret_val = wma_cli_set_command((int)adapter->sessionId, + (int)WMI_PDEV_PARAM_FORCE_CHAIN_ANT, + antdiv_chain, PDEV_CMD); + if (ret_val) { + hdd_err("Failed to set antdiv_chain"); + return ret_val; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST]) { + antdiv_selftest = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST]); + hdd_debug("antdiv_selftest: %d", antdiv_selftest); + ret_val = wma_cli_set_command((int)adapter->sessionId, + (int)WMI_PDEV_PARAM_ANT_DIV_SELFTEST, + antdiv_selftest, PDEV_CMD); + if (ret_val) { + hdd_err("Failed to set antdiv_selftest"); + return ret_val; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL]) { + antdiv_selftest_intvl = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL]); + hdd_debug("antdiv_selftest_intvl: %d", + antdiv_selftest_intvl); + ret_val = wma_cli_set_command((int)adapter->sessionId, + (int)WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL, + antdiv_selftest_intvl, PDEV_CMD); + if (ret_val) { + hdd_err("Failed to set antdiv_selftest_intvl"); + return ret_val; + } + } + return ret_val; } @@ -10649,6 +10708,184 @@ static inline bool wlan_hdd_is_bt_in_progress(hdd_context_t *hdd_ctx) return false; } +struct chain_rssi_priv { + struct chain_rssi_result chain_rssi; +}; + +/** + * hdd_get_chain_rssi_cb() - Callback function to get chain rssi + * @context: opaque context originally passed to SME. HDD always passes + * a cookie for the request context + * @data: struct for get chain rssi + * + * This function receives the response/data from the lower layer and + * checks to see if the thread is still waiting then post the results to + * upper layer, if the request has timed out then ignore. + * + * Return: None + */ +static void hdd_get_chain_rssi_cb(void *context, + struct chain_rssi_result *data) +{ + struct hdd_request *request; + struct chain_rssi_priv *priv; + + ENTER(); + + request = hdd_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = hdd_request_priv(request); + priv->chain_rssi = *data; + hdd_request_complete(request); + hdd_request_put(request); +} + +/** + * hdd_post_get_chain_rssi_rsp - send rsp to user space + * @hdd_ctx: pointer to hdd context + * @result: chain rssi result + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_post_get_chain_rssi_rsp(hdd_context_t *hdd_ctx, + struct chain_rssi_result *result) +{ + struct sk_buff *skb; + int data_len = sizeof(result->chain_rssi); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + data_len + NLA_HDRLEN + NLMSG_HDRLEN); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -ENOMEM; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI, data_len, + result->chain_rssi)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * __wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * Return: 0 on success; error number otherwise. + */ +static int __wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); + hdd_context_t *hdd_ctx = wiphy_priv(wiphy); + struct get_chain_rssi_req_params req_msg; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + QDF_STATUS status; + int retval; + void *cookie; + struct hdd_request *request; + struct chain_rssi_priv *priv; + static const struct hdd_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + ENTER(); + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return retval; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, NULL)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) { + hdd_err("attr mac addr failed"); + return -EINVAL; + } + if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) != + QDF_MAC_ADDR_SIZE) { + hdd_err("incorrect mac size"); + return -EINVAL; + } + memcpy(&req_msg.peer_macaddr, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), + QDF_MAC_ADDR_SIZE); + req_msg.session_id = adapter->sessionId; + + request = hdd_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = hdd_request_cookie(request); + + status = sme_get_chain_rssi(hdd_ctx->hHal, + &req_msg, + hdd_get_chain_rssi_cb, + cookie); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Unable to get chain rssi"); + retval = qdf_status_to_os_return(status); + } else { + retval = hdd_request_wait_for_response(request); + if (retval) { + hdd_err("Target response timed out"); + } else { + priv = hdd_request_priv(request); + retval = hdd_post_get_chain_rssi_rsp(hdd_ctx, + &priv->chain_rssi); + if (retval) + hdd_err("Failed to post chain rssi"); + } + } + hdd_request_put(request); + + EXIT(); + return retval; +} + +/** + * wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * Return: 0 on success; error number otherwise. + */ +static int wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_cfg80211_get_chain_rssi(wiphy, wdev, data, data_len); + cds_ssr_unprotect(__func__); + + return ret; +} const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { { @@ -11312,6 +11549,15 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { COMMON_VENDOR_COMMANDS #endif FEATURE_11AX_VENDOR_COMMANDS + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_chain_rssi + }, }; #if ((LINUX_VERSION_CODE > KERNEL_VERSION(4, 4, 0)) || \ diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h index e22adfafc94a..4f33b9ccdb5d 100644 --- a/core/mac/inc/sir_api.h +++ b/core/mac/inc/sir_api.h @@ -7452,4 +7452,24 @@ struct sir_rssi_disallow_lst { int8_t expected_rssi; qdf_time_t time_during_rejection; }; + +/** + * struct chain_rssi_result - chain rssi result + * @chain_rssi: chain rssi result as dBm unit + */ +#define CHAIN_RSSI_NUM 8 +struct chain_rssi_result { + int32_t chain_rssi[CHAIN_RSSI_NUM]; +}; + +/** + * struct get_chain_rssi_req_params - get chain rssi req params + * @peer_macaddr: specific peer mac address + * @session_id: session id + */ +struct get_chain_rssi_req_params { + struct qdf_mac_addr peer_macaddr; + uint8_t session_id; +}; + #endif /* __SIR_API_H */ diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h index adcb39103d26..df863820444e 100644 --- a/core/sme/inc/sme_api.h +++ b/core/sme/inc/sme_api.h @@ -1665,4 +1665,21 @@ QDF_STATUS sme_get_peer_info_ext(tHalHandle hal, void sme_set_5g_band_pref(tHalHandle hal_handle, struct sme_5g_band_pref_params *pref_params); +/** + * sme_get_chain_rssi() - Get chain rssi + * @hal: Global HAL handle + * @input: get chain rssi req params + * @callback: Callback function to be called with the result + * @context: Opaque context to be used by the caller to associate the + * request with the response + * + * This function constructs the cds message and fill in message type, + * post the same to WDA. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_get_chain_rssi(tHalHandle hal, + struct get_chain_rssi_req_params *input, + get_chain_rssi_callback callback, + void *context); #endif /* #if !defined( __SME_API_H ) */ diff --git a/core/sme/inc/sme_internal.h b/core/sme/inc/sme_internal.h index f8e23a6b6a4b..dd535b927ef6 100644 --- a/core/sme/inc/sme_internal.h +++ b/core/sme/inc/sme_internal.h @@ -160,6 +160,16 @@ typedef void (*sme_encrypt_decrypt_callback)( void *context, struct sir_encrypt_decrypt_rsp_params *response); +/** + * typedef get_chain_rssi_callback - get chain rssi callback + * @context: Opaque context that the client can use to associate the + * callback with the request + * @data: chain rssi result reported by firmware + */ +struct chain_rssi_result; +typedef void (*get_chain_rssi_callback)(void *context, + struct chain_rssi_result *data); + typedef struct tagSmeStruct { eSmeState state; qdf_mutex_t lkSmeGlobalLock; @@ -258,6 +268,8 @@ typedef struct tagSmeStruct { void (*chip_power_save_fail_cb)(void *, struct chip_pwr_save_fail_detected_params *); void (*bt_activity_info_cb)(void *context, uint32_t bt_activity); + get_chain_rssi_callback get_chain_rssi_cb; + void *get_chain_rssi_context; } tSmeStruct, *tpSmeStruct; #endif /* #if !defined( __SMEINTERNAL_H ) */ diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index 4468c82c69f3..f377a47edb9e 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -16507,3 +16507,26 @@ QDF_STATUS sme_set_bt_activity_info_cb(tHalHandle hal, return status; } + +QDF_STATUS sme_get_chain_rssi(tHalHandle hal, + struct get_chain_rssi_req_params *input, + get_chain_rssi_callback callback, + void *context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal); + + SME_ENTER(); + + if (NULL == input) { + sme_err("Invalid req params"); + return QDF_STATUS_E_INVAL; + } + + mac_ctx->sme.get_chain_rssi_cb = callback; + mac_ctx->sme.get_chain_rssi_context = context; + wma_get_chain_rssi(hal, input); + + SME_EXIT(); + return status; +} diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index 53a80bc70d29..2d8c114ef1a7 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -2586,4 +2586,15 @@ QDF_STATUS wma_configure_smps_params(uint32_t vdev_id, uint32_t param_id, int wma_chip_power_save_failure_detected_handler(void *handle, uint8_t *cmd_param_info, uint32_t len); + +/** + * wma_get_chain_rssi() - send wmi cmd to get chain rssi + * @wma_handle: wma handler + * @req_params: requset params + * + * Return: Return QDF_STATUS + */ +QDF_STATUS wma_get_chain_rssi(tp_wma_handle wma_handle, + struct get_chain_rssi_req_params *req_params); + #endif diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h index aeb64e8f4c94..91eebb8c2767 100644 --- a/core/wma/inc/wma_internal.h +++ b/core/wma/inc/wma_internal.h @@ -1327,4 +1327,19 @@ int wma_rx_aggr_failure_event_handler(void *handle, u_int8_t *event_buf, */ int wma_wlan_bt_activity_evt_handler(void *handle, uint8_t *event, uint32_t len); + +/** + * wma_peer_ant_info_evt_handler - event handler to handle antenna info + * @handle: the wma handle + * @event: buffer with event + * @len: buffer length + * + * This function receives antenna info from firmware and passes the event + * to upper layer + * + * Return: 0 on success + */ +int wma_peer_ant_info_evt_handler(void *handle, u_int8_t *event, + u_int32_t len); + #endif diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index 0ad324dc7bf8..575f2535a056 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -5373,3 +5373,58 @@ int wma_wlan_bt_activity_evt_handler(void *handle, uint8_t *event, uint32_t len) return 0; } + +int wma_peer_ant_info_evt_handler(void *handle, u_int8_t *event, + u_int32_t len) +{ + wmi_peer_antdiv_info *peer_ant_info; + WMI_PEER_ANTDIV_INFO_EVENTID_param_tlvs *param_buf; + wmi_peer_antdiv_info_event_fixed_param *fix_param; + struct chain_rssi_result *chain_rssi_result; + u_int32_t chain_index; + + tpAniSirGlobal pmac = (tpAniSirGlobal)cds_get_context( + QDF_MODULE_ID_PE); + if (!pmac) { + WMA_LOGE("%s: Invalid pmac", __func__); + return -EINVAL; + } + + param_buf = (WMI_PEER_ANTDIV_INFO_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("Invalid peer_ant_info event buffer"); + return -EINVAL; + } + fix_param = param_buf->fixed_param; + peer_ant_info = param_buf->peer_info; + + WMA_LOGD("num_peers=%d\tvdev_id=%d", + fix_param->num_peers, fix_param->vdev_id); + WMA_LOGD("peer_ant_info: %p", peer_ant_info); + + if (!peer_ant_info) { + WMA_LOGE("Invalid peer_ant_info ptr"); + return -EINVAL; + } + + chain_rssi_result = qdf_mem_malloc(sizeof(*chain_rssi_result)); + if (!chain_rssi_result) { + WMA_LOGE("%s: Failed to malloc", __func__); + return -ENOMEM; + } + + for (chain_index = 0; chain_index < CHAIN_RSSI_NUM; chain_index++) + WMA_LOGD("chain%d rssi: %x", chain_index, + peer_ant_info->chain_rssi[chain_index]); + + qdf_mem_copy(chain_rssi_result->chain_rssi, + peer_ant_info->chain_rssi, + sizeof(peer_ant_info->chain_rssi)); + + pmac->sme.get_chain_rssi_cb(pmac->sme.get_chain_rssi_context, + chain_rssi_result); + + qdf_mem_free(chain_rssi_result); + + return 0; +} diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index 11fc5b84a0c4..7ef65a9affb6 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -2707,6 +2707,11 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc, void *cds_context, } wma_ndp_register_all_event_handlers(wma_handle); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + WMI_PEER_ANTDIV_INFO_EVENTID, + wma_peer_ant_info_evt_handler, + WMA_RX_WORK_CTX); + wma_register_debug_callback(); /* Register callback with PMO so PMO can update the vdev pause bitmap*/ @@ -6674,6 +6679,46 @@ QDF_STATUS wma_set_rx_blocksize(tp_wma_handle wma_handle, return QDF_STATUS_SUCCESS; } +QDF_STATUS wma_get_chain_rssi(tp_wma_handle wma_handle, + struct get_chain_rssi_req_params *req_params) +{ + wmi_peer_antdiv_info_req_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len = sizeof(wmi_peer_antdiv_info_req_cmd_fixed_param); + u_int8_t *buf_ptr; + + if (!wma_handle) { + WMA_LOGE(FL("WMA is closed, can not issue cmd")); + return QDF_STATUS_E_INVAL; + } + + wmi_buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!wmi_buf) { + WMA_LOGE(FL("wmi_buf_alloc failed")); + return QDF_STATUS_E_NOMEM; + } + + buf_ptr = (u_int8_t *)wmi_buf_data(wmi_buf); + + cmd = (wmi_peer_antdiv_info_req_cmd_fixed_param *)buf_ptr; + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_peer_antdiv_info_req_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_peer_antdiv_info_req_cmd_fixed_param)); + cmd->vdev_id = req_params->session_id; + WMI_CHAR_ARRAY_TO_MAC_ADDR(req_params->peer_macaddr.bytes, + &cmd->peer_mac_address); + + if (wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, len, + WMI_PEER_ANTDIV_INFO_REQ_CMDID)) { + WMA_LOGE(FL("failed to send get chain rssi command")); + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + /** * wma_mc_process_msg() - process wma messages and call appropriate function. * @cds_context: cds context