diff --git a/Kbuild b/Kbuild index 9427b1d5c03c..91bf27c9a61a 100644 --- a/Kbuild +++ b/Kbuild @@ -2733,6 +2733,11 @@ ifeq ($(CONFIG_CONVERGED_P2P_ENABLE), y) cppflags-$(CONFIG_FEATURE_P2P_LISTEN_OFFLOAD) += -DFEATURE_P2P_LISTEN_OFFLOAD endif +#Enable support to get ANI value +ifeq ($(CONFIG_ANI_LEVEL_REQUEST), y) +cppflags-y += -DFEATURE_ANI_LEVEL_REQUEST +endif + #Flags to enable/disable WMI APIs cppflags-$(CONFIG_WMI_ROAM_SUPPORT) += -DWMI_ROAM_SUPPORT cppflags-$(CONFIG_WMI_CONCURRENCY_SUPPORT) += -DWMI_CONCURRENCY_SUPPORT diff --git a/configs/default_defconfig b/configs/default_defconfig index 6063bf4c75af..4cbadc873e99 100644 --- a/configs/default_defconfig +++ b/configs/default_defconfig @@ -992,3 +992,6 @@ CONFIG_6G_SCAN_CHAN_SORT_ALGO := y endif CONFIG_SAP_DHCP_FW_IND := y + +#Enable support to get ANI level +CONFIG_ANI_LEVEL_REQUEST := y diff --git a/core/hdd/inc/wlan_hdd_power.h b/core/hdd/inc/wlan_hdd_power.h index 764ee50d06b2..0ff09176b964 100644 --- a/core/hdd/inc/wlan_hdd_power.h +++ b/core/hdd/inc/wlan_hdd_power.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2012, 2014-2019 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 @@ -127,6 +127,18 @@ struct pkt_filter_cfg { #endif +#ifdef FEATURE_ANI_LEVEL_REQUEST +/** + * ani_priv - structure to store the priv data for get ani request + * @num_freq: number of freq received from the FW + * @ani: data received from the FW + */ +struct ani_priv { + uint32_t num_freq; + struct wmi_host_ani_level_event *ani; +}; +#endif + /** * enum suspend_resume_state - Suspend resume state * @HDD_WLAN_EARLY_SUSPEND: Early suspend state. @@ -551,4 +563,19 @@ static inline int wlan_hdd_rx_thread_suspend(struct hdd_context *hdd_ctx) } #endif +#ifdef FEATURE_ANI_LEVEL_REQUEST +/** + * wlan_hdd_get_ani_level() - Wrapper to call API to fetch ani level + * @adapter: pointer to HDD adapter + * @ani: pointer to structure storing ani level for channels + * @parsed_freqs: parsed freqs from the get ani command + * @num_freqs: number of parsed channels + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_hdd_get_ani_level(struct hdd_adapter *adapter, + struct wmi_host_ani_level_event *ani, + uint32_t *parsed_freqs, + uint8_t num_freqs); +#endif /* FEATURE_ANI_LEVEL_REQUEST */ #endif /* __WLAN_HDD_POWER_H */ diff --git a/core/hdd/src/wlan_hdd_ioctl.c b/core/hdd/src/wlan_hdd_ioctl.c index ef2baa251a1f..f1e1c95ed2f4 100644 --- a/core/hdd/src/wlan_hdd_ioctl.c +++ b/core/hdd/src/wlan_hdd_ioctl.c @@ -7562,6 +7562,175 @@ static int drv_cmd_get_disable_chan_list(struct hdd_adapter *adapter, return 0; } #endif + +#ifdef FEATURE_ANI_LEVEL_REQUEST +static int drv_cmd_get_ani_level(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + char *extra; + int copied_length = 0, j, temp_int, ret = 0; + uint8_t *param, num_freqs, num_recv_channels; + uint32_t parsed_freqs[MAX_NUM_FREQS_FOR_ANI_LEVEL]; + struct wmi_host_ani_level_event ani[MAX_NUM_FREQS_FOR_ANI_LEVEL]; + size_t user_size = priv_data->total_len; + + hdd_debug("Received Command to get ANI level"); + + param = strnchr(command, strlen(command), ' '); + + /* No argument after the command */ + if (!param) + return -EINVAL; + + /* No space after the command */ + else if (SPACE_ASCII_VALUE != *param) + return -EINVAL; + + param++; + + /* Removing empty spaces*/ + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + /*no argument followed by spaces */ + if ('\0' == *param) + return -EINVAL; + + /* Getting the first argument ie the number of channels */ + if (sscanf(param, "%d ", &temp_int) != 1) { + hdd_err("Cannot get number of freq from input"); + return -EINVAL; + } + + if (temp_int < 0 || temp_int > MAX_NUM_FREQS_FOR_ANI_LEVEL) { + hdd_err("Invalid Number of channel received"); + return -EINVAL; + } + + hdd_debug("Number of freq to fetch ANI level are: %d", temp_int); + + if (!temp_int) + return 0; + + num_freqs = temp_int; + + for (j = 0; j < num_freqs; j++) { + /* + * Param pointing to the beginning of first space + * after number of channels. + */ + param = strpbrk(param, " "); + /*no channel list after the number of channels argument*/ + if (!param) { + hdd_err("Invalid No of freq provided in the list"); + ret = -EINVAL; + goto parse_failed; + } + + param++; + + /* Removing empty space */ + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + if ('\0' == *param) { + hdd_err("No freq is provided in the list"); + ret = -EINVAL; + goto parse_failed; + } + + if (sscanf(param, "%d ", &temp_int) != 1) { + hdd_err("Cannot read freq number"); + ret = -EINVAL; + goto parse_failed; + } + + hdd_debug("channel_freq[%d] = %d", j, temp_int); + parsed_freqs[j] = temp_int; + } + + /* Extra arguments check */ + param = strpbrk(param, " "); + if (param) { + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + if ('\0' != *param) { + hdd_err("Invalid argument received"); + ret = -EINVAL; + goto parse_failed; + } + } + + qdf_mem_zero(ani, sizeof(ani)); + hdd_debug("num_freq: %d", num_freqs); + if (QDF_IS_STATUS_ERROR(wlan_hdd_get_ani_level(adapter, ani, + parsed_freqs, + num_freqs))) { + hdd_err("Unable to retrieve ani level"); + return -EINVAL; + } + + extra = qdf_mem_malloc(user_size); + if (!extra) { + hdd_err("memory allocation failed"); + ret = -ENOMEM; + goto parse_failed; + } + + /* + * Find the number of channels that are populated. If freq is not + * filled then stop count there + */ + for (num_recv_channels = 0; + (num_recv_channels < num_freqs && + ani[num_recv_channels].chan_freq); num_recv_channels++) + ; + + for (j = 0; j < num_recv_channels; j++) { + /* Sanity check for ANI level validity */ + if (ani[j].ani_level > MAX_ANI_LEVEL) + continue; + + copied_length += scnprintf(extra + copied_length, + user_size - copied_length, "%d:%d\n", + ani[j].chan_freq, ani[j].ani_level); + } + + if (copied_length == 0) { + hdd_err("ANI level not fetched"); + ret = -EINVAL; + goto free; + } + + hdd_debug("data: %s", extra); + + if (copy_to_user(priv_data->buf, extra, copied_length + 1)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + goto free; + } + +free: + qdf_mem_free(extra); + +parse_failed: + return ret; +} + +#else +static int drv_cmd_get_ani_level(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} +#endif /* * The following table contains all supported WLAN HDD * IOCTL driver commands and the handler for each of them. @@ -7679,6 +7848,7 @@ static const struct hdd_drv_cmd hdd_drv_cmds[] = { {"GETANTENNAMODE", drv_cmd_get_antenna_mode, false}, {"SET_DISABLE_CHANNEL_LIST", drv_cmd_set_disable_chan_list, true}, {"GET_DISABLE_CHANNEL_LIST", drv_cmd_get_disable_chan_list, false}, + {"GET_ANI_LEVEL", drv_cmd_get_ani_level, false}, {"STOP", drv_cmd_dummy, false}, /* Deprecated commands */ {"RXFILTER-START", drv_cmd_dummy, false}, diff --git a/core/hdd/src/wlan_hdd_power.c b/core/hdd/src/wlan_hdd_power.c index 8260495c72ad..7d36c8843c15 100644 --- a/core/hdd/src/wlan_hdd_power.c +++ b/core/hdd/src/wlan_hdd_power.c @@ -80,6 +80,7 @@ #include #include "wlan_p2p_ucfg_api.h" #include "wlan_mlme_ucfg_api.h" +#include "wlan_osif_request_manager.h" /* Preprocessor definitions and constants */ #ifdef QCA_WIFI_NAPIER_EMULATION @@ -2245,10 +2246,118 @@ int wlan_hdd_cfg80211_set_txpower(struct wiphy *wiphy, } static void wlan_hdd_get_tx_power(struct hdd_adapter *adapter, int *dbm) + { wlan_cfg80211_mc_cp_stats_get_tx_power(adapter->vdev, dbm); } +#ifdef FEATURE_ANI_LEVEL_REQUEST +static void hdd_get_ani_level_cb(struct wmi_host_ani_level_event *ani, + uint8_t num, void *context) +{ + struct osif_request *request; + struct ani_priv *priv; + uint8_t min_recv_freqs = QDF_MIN(num, MAX_NUM_FREQS_FOR_ANI_LEVEL); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + /* propagate response back to requesting thread */ + priv = osif_request_priv(request); + priv->ani = qdf_mem_malloc(min_recv_freqs * + sizeof(struct wmi_host_ani_level_event)); + if (!priv->ani) + goto complete; + + priv->num_freq = min_recv_freqs; + qdf_mem_copy(priv->ani, ani, + min_recv_freqs * sizeof(struct wmi_host_ani_level_event)); + +complete: + osif_request_complete(request); + osif_request_put(request); +} + +/** + * wlan_hdd_get_ani_level_dealloc() - Dealloc mem allocated in priv data + * @priv: the priv data + * + * Return: None + */ +static void wlan_hdd_get_ani_level_dealloc(void *priv) +{ + struct ani_priv *ani = priv; + + if (ani->ani) + qdf_mem_free(ani->ani); +} + +QDF_STATUS wlan_hdd_get_ani_level(struct hdd_adapter *adapter, + struct wmi_host_ani_level_event *ani, + uint32_t *parsed_freqs, + uint8_t num_freqs) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + QDF_STATUS status; + void *cookie; + struct osif_request *request; + struct ani_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = 1000, + .dealloc = wlan_hdd_get_ani_level_dealloc, + }; + + if (!hdd_ctx) { + hdd_err("Invalid HDD context"); + return QDF_STATUS_E_INVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + cookie = osif_request_cookie(request); + + status = sme_get_ani_level(hdd_ctx->mac_handle, parsed_freqs, + num_freqs, hdd_get_ani_level_cb, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to retrieve ani level"); + goto complete; + } else { + /* request was sent -- wait for the response */ + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving ANI level"); + status = QDF_STATUS_E_TIMEOUT; + goto complete; + } + } + + priv = osif_request_priv(request); + + qdf_mem_copy(ani, priv->ani, sizeof(struct wmi_host_ani_level_event) * + priv->num_freq); + +complete: + /* + * either we never sent a request, we sent a request and + * received a response or we sent a request and timed out. + * regardless we are done with the request. + */ + osif_request_put(request); + + hdd_exit(); + return status; +} +#endif + /** * __wlan_hdd_cfg80211_get_txpower() - get TX power * @wiphy: Pointer to wiphy diff --git a/core/mac/inc/ani_global.h b/core/mac/inc/ani_global.h index 280c8e463225..b7f67e824de2 100644 --- a/core/mac/inc/ani_global.h +++ b/core/mac/inc/ani_global.h @@ -745,6 +745,14 @@ struct mgmt_beacon_probe_filter { uint8_t sap_channel[WLAN_MAX_VDEVS]; }; +#ifdef FEATURE_ANI_LEVEL_REQUEST +struct ani_level_params { + void (*ani_level_cb)(struct wmi_host_ani_level_event *ani, uint8_t num, + void *context); + void *context; +}; +#endif + /** * struct mac_context - Global MAC context */ @@ -824,6 +832,9 @@ struct mac_context { bool bcn_reception_stats; csr_session_close_cb session_close_cb; csr_roam_complete_cb session_roam_complete_cb; +#ifdef FEATURE_ANI_LEVEL_REQUEST + struct ani_level_params ani_params; +#endif }; #ifdef FEATURE_WLAN_TDLS diff --git a/core/sme/inc/csr_api.h b/core/sme/inc/csr_api.h index e0eea3874ed5..8df6941a259a 100644 --- a/core/sme/inc/csr_api.h +++ b/core/sme/inc/csr_api.h @@ -1483,4 +1483,7 @@ QDF_STATUS csr_update_owe_info(struct mac_context *mac, QDF_STATUS csr_send_roam_offload_init_msg(struct mac_context *mac, uint32_t vdev_id, bool enable); + +typedef void (*csr_ani_callback)(int8_t *ani, void *context); + #endif diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h index c9e6abb6cae4..72e0bf1e8563 100644 --- a/core/sme/inc/sme_api.h +++ b/core/sme/inc/sme_api.h @@ -3962,4 +3962,22 @@ QDF_STATUS sme_get_full_roam_scan_period(mac_handle_t mac_handle, */ QDF_STATUS sme_check_for_duplicate_session(mac_handle_t mac_handle, uint8_t *peer_addr); +#ifdef FEATURE_ANI_LEVEL_REQUEST +/* + * sme_get_ani_level() - + * A wrapper function that client calls to register a callback to get ani level + * + * @mac_handle - pointer to mac handle + * @freqs - frequencies for which ANI level has to be fetched + * @num_freqs - number of frequencies + * @callback - SME sends back the ani level using the callback + * @context - user context to be passed back along with the callback + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_ani_level(mac_handle_t mac_handle, uint32_t *freqs, + uint8_t num_freqs, void (*callback)( + struct wmi_host_ani_level_event *ani, uint8_t num, + void *context), void *context); +#endif /* FEATURE_ANI_LEVEL_REQUEST */ #endif /* #if !defined( __SME_API_H ) */ diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index c29b0572244c..d30d6632ac23 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -16240,3 +16240,27 @@ QDF_STATUS sme_check_for_duplicate_session(mac_handle_t mac_handle, return status; } + +#ifdef FEATURE_ANI_LEVEL_REQUEST +QDF_STATUS sme_get_ani_level(mac_handle_t mac_handle, uint32_t *freqs, + uint8_t num_freqs, void (*callback)( + struct wmi_host_ani_level_event *ani, uint8_t num, + void *context), void *context) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mac->ani_params.ani_level_cb = callback; + mac->ani_params.context = context; + + status = wma_send_ani_level_request(wma_handle, freqs, num_freqs); + return status; +} +#endif /* FEATURE_ANI_LEVEL_REQUEST */ diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index 91758a36fb1c..86a044cfe0f5 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -2683,4 +2683,17 @@ QDF_STATUS wma_pre_vdev_start_setup(uint8_t vdev_id, */ void wma_release_pending_vdev_refs(void); +#ifdef FEATURE_ANI_LEVEL_REQUEST +/** + * wma_send_ani_level_request() - Send get ani level cmd to WMI + * @wma_handle: wma handle. + * @freqs: pointer to channels for which ANI level has to be retrieved + * @num_freqs: number of channels in the above parameter + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_send_ani_level_request(tp_wma_handle wma_handle, + uint32_t *freqs, uint8_t num_freqs); +#endif /* FEATURE_ANI_LEVEL_REQUEST */ #endif + diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h index 0277a23ce0f0..3f4fe65a61be 100644 --- a/core/wma/inc/wma_internal.h +++ b/core/wma/inc/wma_internal.h @@ -1888,4 +1888,18 @@ int wma_cold_boot_cal_event_handler(void *wma_ctx, uint8_t *event_buff, */ QDF_STATUS wma_set_roam_triggers(tp_wma_handle wma_handle, struct roam_triggers *triggers); + +/** + * wma_get_ani_level_evt_handler - event handler to fetch ani level + * @handle: the wma handle + * @event_buf: buffer with event + * @len: buffer length + * + * This function receives ani level from firmware and passes the event + * to upper layer + * + * Return: 0 on success + */ +int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, + uint32_t len); #endif diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index 0673e83911d3..b9c362540fe9 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -5445,3 +5445,50 @@ int wma_vdev_bss_color_collision_info_handler(void *handle, return 0; } + +#ifdef FEATURE_ANI_LEVEL_REQUEST +int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle)handle; + struct wmi_host_ani_level_event *ani = NULL; + uint32_t num_freqs = 0; + QDF_STATUS status; + struct mac_context *pmac; + int ret = 0; + + pmac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + if (!pmac || !wma) { + WMA_LOGE(FL("Invalid pmac or wma")); + return -EINVAL; + } + + status = wmi_unified_extract_ani_level(wma->wmi_handle, event_buf, + &ani, &num_freqs); + + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to extract ani level", __func__); + return -EINVAL; + } + + if (!pmac->ani_params.ani_level_cb) { + WMA_LOGE(FL("Invalid ani_level_cb")); + ret = -EINVAL; + goto free; + } + + pmac->ani_params.ani_level_cb(ani, num_freqs, + pmac->ani_params.context); + +free: + qdf_mem_free(ani); + return ret; +} +#else +int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + return 0; +} +#endif + diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index 83c00efc85bc..f2c22e2b4976 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -3298,6 +3298,10 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc, wma_pdev_div_info_evt_handler, WMA_RX_WORK_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_get_ani_level_event_id, + wma_get_ani_level_evt_handler, + WMA_RX_WORK_CTX); wma_register_debug_callback(); wifi_pos_register_get_phy_mode_cb(wma_handle->psoc, @@ -9383,3 +9387,12 @@ QDF_STATUS wma_get_rx_chainmask(uint8_t pdev_id, uint32_t *chainmask_2g, return QDF_STATUS_SUCCESS; } + +#ifdef FEATURE_ANI_LEVEL_REQUEST +QDF_STATUS wma_send_ani_level_request(tp_wma_handle wma_handle, + uint32_t *freqs, uint8_t num_freqs) +{ + return wmi_unified_ani_level_cmd_send(wma_handle->wmi_handle, freqs, + num_freqs); +} +#endif