diff --git a/Kbuild b/Kbuild index 84280e15c509..a336d9ff5403 100755 --- a/Kbuild +++ b/Kbuild @@ -73,6 +73,7 @@ HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_llstat.o HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_csr.o HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_connect.o HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_offload.o +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_roam.o endif endif diff --git a/core/hdd/inc/wlan_hdd_debugfs_csr.h b/core/hdd/inc/wlan_hdd_debugfs_csr.h index d1b561f8cdc3..b8ca56e0a20f 100644 --- a/core/hdd/inc/wlan_hdd_debugfs_csr.h +++ b/core/hdd/inc/wlan_hdd_debugfs_csr.h @@ -41,6 +41,7 @@ #define DEBUGFS_CONNECT_INFO_BUF_SIZE (4 * 1024) #define DEBUGFS_OFFLOAD_INFO_BUF_SIZE (4 * 1024) +#define DEBUGFS_ROAM_SCAN_STATS_INFO_BUF_SIZE (4 * 1024) /** * struct wlan_hdd_debugfs_buffer_info - Debugfs buffer info @@ -61,6 +62,14 @@ struct wlan_hdd_debugfs_buffer_info { struct hdd_adapter *adapter; }; +/** + * struct hdd_roam_scan_stats_debugfs_priv - private data for request mgr + * @res: pointer to roam scan stats response + */ +struct hdd_roam_scan_stats_debugfs_priv { + struct wmi_roam_scan_stats_res *roam_scan_stats_res; +}; + /** * wlan_hdd_debugfs_csr_init() - Create wifi diagnostic debugfs files * @adapter: pointer to adapter for which debugfs files are to be created @@ -113,6 +122,19 @@ wlan_hdd_debugfs_update_filters_info(struct hdd_context *hdd_ctx, struct hdd_adapter *adapter, uint8_t *buf, ssize_t buf_avail_len); +/** + * wlan_hdd_debugfs_update_roam_stats() - API to get roam scan stats info + * into user buffer + * @buf: output buffer to hold roam scan stats info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +ssize_t +wlan_hdd_debugfs_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len); + #else /** * wlan_hdd_debugfs_csr_init() - Create wifi diagnostic debugfs files @@ -179,6 +201,22 @@ wlan_hdd_debugfs_update_filters_info(struct hdd_context *hdd_ctx, return 0; } +/** + * wlan_hdd_debugfs_update_roam_stats() - API to get roam scan stats info + * into user buffer + * @buf: output buffer to hold roam scan stats info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +static inline ssize_t +wlan_hdd_debugfs_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + return 0; +} + #endif #endif /* _WLAN_HDD_DEBUGFS_CSR_H */ diff --git a/core/hdd/src/wlan_hdd_debugfs_csr.c b/core/hdd/src/wlan_hdd_debugfs_csr.c index 79208dadcf63..f6da1e26127f 100644 --- a/core/hdd/src/wlan_hdd_debugfs_csr.c +++ b/core/hdd/src/wlan_hdd_debugfs_csr.c @@ -81,6 +81,8 @@ wlan_hdd_debugfs_update_csr(struct hdd_context *hdd_ctx, break; case HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO: /* populate roam scan stats info */ + len = wlan_hdd_debugfs_update_roam_stats(hdd_ctx, adapter, + buf, buf_avail_len); break; case HDD_DEBUFS_FILE_ID_OFFLOAD_INFO: /* populate offload info */ @@ -353,6 +355,18 @@ void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter) if (!csr->entry) hdd_err("Failed to create generic_info debugfs file"); } + + csr = &adapter->csr_file[HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO]; + if (!csr->entry) { + strlcpy(csr->name, "roam_stats", max_len); + csr->id = HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO; + csr->buf_max_size = DEBUGFS_ROAM_SCAN_STATS_INFO_BUF_SIZE; + csr->entry = debugfs_create_file(csr->name, 0444, + adapter->debugfs_phy, + csr, &fops_csr_debugfs); + if (!csr->entry) + hdd_err("Failed to create generic_info debugfs file"); + } } void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter) diff --git a/core/hdd/src/wlan_hdd_debugfs_roam.c b/core/hdd/src/wlan_hdd_debugfs_roam.c new file mode 100644 index 000000000000..6b3b2104c1b2 --- /dev/null +++ b/core/hdd/src/wlan_hdd_debugfs_roam.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_roam.c + * + * WLAN Host Device Driver implementation to update + * debugfs with roaming information + */ + +#include +#include +#include +#include +#include "qwlan_version.h" +#include "wmi_unified_param.h" +#include "wlan_osif_request_manager.h" + +/** + * hdd_roam_scan_stats_debugfs_dealloc() - Dealloc objects in hdd request mgr + * @priv: Pointer to private data of hdd request object + * + * Return: None + */ +static void hdd_roam_scan_stats_debugfs_dealloc(void *priv) +{ + struct hdd_roam_scan_stats_debugfs_priv *local_priv = priv; + struct wmi_roam_scan_stats_res *roam_scan_stats_res; + + if (!local_priv) + return; + + roam_scan_stats_res = local_priv->roam_scan_stats_res; + local_priv->roam_scan_stats_res = NULL; + + qdf_mem_free(roam_scan_stats_res); +} + +/** + * hdd_roam_scan_stats_cb() - Call back invoked from roam scan stats evt + * @context: cookie to get request object + * @res: roam scan stats response from firmware + * + * Return: None + */ +static void +hdd_roam_scan_stats_cb(void *context, struct wmi_roam_scan_stats_res *res) +{ + struct osif_request *request; + struct hdd_roam_scan_stats_debugfs_priv *priv; + struct wmi_roam_scan_stats_res *stats_res; + uint32_t total_len; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete roam scan stats request"); + return; + } + + if (!res) { + hdd_err("Invalid response"); + goto end; + } + + priv = osif_request_priv(request); + + total_len = sizeof(*res) + res->num_roam_scans * + sizeof(struct wmi_roam_scan_stats_params); + + stats_res = qdf_mem_malloc(total_len); + if (!stats_res) { + hdd_err("No memory for response"); + goto end; + } + + qdf_mem_copy(stats_res, res, total_len); + priv->roam_scan_stats_res = stats_res; + +end: + osif_request_complete(request); + osif_request_put(request); + + hdd_exit(); +} + +/** + * hdd_get_roam_scan_stats() - Get roam scan stats using request manager + * @hdd_ctx: hdd context + * @adapter: pointer to adapter + * + * Return: Pointer to struct wmi_roam_scan_stats_res which conatins response + * from firmware + */ +static struct +wmi_roam_scan_stats_res *hdd_get_roam_scan_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct wmi_roam_scan_stats_res *res; + struct wmi_roam_scan_stats_res *stats_res = NULL; + void *context; + struct osif_request *request; + struct hdd_roam_scan_stats_debugfs_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_FW_ROAM_STATS, + .dealloc = hdd_roam_scan_stats_debugfs_dealloc, + }; + QDF_STATUS status; + int ret; + uint32_t total_len; + + hdd_enter(); + + if (!wlan_hdd_validate_modules_state(hdd_ctx)) + return NULL; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return NULL; + } + + context = osif_request_cookie(request); + + status = sme_get_roam_scan_stats(hdd_ctx->mac_handle, + hdd_roam_scan_stats_cb, + context, adapter->session_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("roam scan stats request failed"); + goto cleanup; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("roam scan stats response time out"); + goto cleanup; + } + + priv = osif_request_priv(request); + res = priv->roam_scan_stats_res; + if (!res) { + hdd_err("Failure of roam scan stats response retrieval"); + goto cleanup; + } + + total_len = sizeof(*res) + res->num_roam_scans * + sizeof(struct wmi_roam_scan_stats_params); + + stats_res = qdf_mem_malloc(total_len); + if (!stats_res) { + hdd_err("No memory for response"); + goto cleanup; + } + + qdf_mem_copy(stats_res, res, total_len); + +cleanup: + osif_request_put(request); + hdd_exit(); + + return stats_res; +} + +/** + * hdd_roam_scan_trigger_to_str() - Get string for + * enum WMI_ROAM_TRIGGER_REASON_ID + * @roam_scan_trigger: roam scan trigger ID + * + * Return: Meaningful string from enum WMI_ROAM_TRIGGER_REASON_ID + */ +static char *hdd_roam_scan_trigger_to_str(uint32_t roam_scan_trigger) +{ + switch (roam_scan_trigger) { + case WMI_ROAM_TRIGGER_REASON_PER: + return "PER"; + case WMI_ROAM_TRIGGER_REASON_BMISS: + return "BEACON MISS"; + case WMI_ROAM_TRIGGER_REASON_LOW_RSSI: + return "LOW RSSI"; + case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI: + return "HIGH RSSI"; + case WMI_ROAM_TRIGGER_REASON_PERIODIC: + return "PERIODIC SCAN"; + case WMI_ROAM_TRIGGER_REASON_MAWC: + return "WMI_ROAM_TRIGGER_REASON_MAWC"; + case WMI_ROAM_TRIGGER_REASON_DENSE: + return "DENSE ENVIRONMENT"; + case WMI_ROAM_TRIGGER_REASON_BACKGROUND: + return "BACKGROUND SCAN"; + case WMI_ROAM_TRIGGER_REASON_FORCED: + return "FORCED SCAN"; + case WMI_ROAM_TRIGGER_REASON_BTM: + return "BTM TRIGGER"; + case WMI_ROAM_TRIGGER_REASON_UNIT_TEST: + return "TEST COMMMAND"; + default: + return "UNKNOWN REASON"; + } + return "UNKNOWN REASON"; +} + +/** + * hdd_roam_scan_trigger_value_to_str() - Get trigger value string for + * enum WMI_ROAM_TRIGGER_REASON_ID + * @roam_scan_trigger: roam scan trigger ID + * @bool: output pointer to hold whether to print trigger value + * + * Return: Meaningful string from trigger value + */ +static char *hdd_roam_scan_trigger_value(uint32_t roam_scan_trigger, + bool *print) +{ + *print = true; + + switch (roam_scan_trigger) { + case WMI_ROAM_TRIGGER_REASON_PER: + return "percentage"; + case WMI_ROAM_TRIGGER_REASON_LOW_RSSI: + return "dB"; + case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI: + return "dB"; + case WMI_ROAM_TRIGGER_REASON_PERIODIC: + return "ms"; + case WMI_ROAM_TRIGGER_REASON_DENSE: + return "(1 - Rx, 2 - Tx)"; + default: + *print = false; + return NULL; + } +} + +/** + * hdd_client_id_to_str() - Helper func to get meaninful string from client id + * @client_id: Id of the client which triggered roam scan in firmware + * + * Return: Meaningful string from enum WMI_SCAN_CLIENT_ID + */ +static char *hdd_client_id_to_str(uint32_t client_id) +{ + switch (client_id) { + case WMI_SCAN_CLIENT_NLO: + return "PNO"; + case WMI_SCAN_CLIENT_EXTSCAN: + return "GSCAN"; + case WMI_SCAN_CLIENT_ROAM: + return "ROAM"; + case WMI_SCAN_CLIENT_P2P: + return "P2P"; + case WMI_SCAN_CLIENT_LPI: + return "LPI"; + case WMI_SCAN_CLIENT_NAN: + return "NAN"; + case WMI_SCAN_CLIENT_ANQP: + return "ANQP"; + case WMI_SCAN_CLIENT_OBSS: + return "OBSS"; + case WMI_SCAN_CLIENT_PLM: + return "PLM"; + case WMI_SCAN_CLIENT_HOST: + return "HOST"; + default: + return "UNKNOWN"; + } + return "UNKNOWN"; +} + +/** + * hdd_roam_scan_trigger() - Print roam scan trigger info into buffer + * @scan: roam scan event data + * @buf: buffer to write roam scan trigger info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +hdd_roam_scan_trigger(struct wmi_roam_scan_stats_params *scan, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret; + char *str; + bool print_trigger_value; + + ret = scnprintf(buf, buf_avail_len, + "Trigger reason is %s\n", + hdd_roam_scan_trigger_to_str(scan->trigger_id)); + if (ret <= 0) + return length; + + length = ret; + + str = hdd_roam_scan_trigger_value(scan->trigger_id, + &print_trigger_value); + if (!print_trigger_value || !str) + return length; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + return length; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "Trigger value is: %u %s\n", + scan->trigger_value, str); + if (ret <= 0) + return length; + + length += ret; + return length; +} + +/** + * hdd_roam_scan_chan() - Print roam scan chan freq info into buffer + * @scan: roam scan event data + * @buf: buffer to write roam scan freq info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +hdd_roam_scan_chan(struct wmi_roam_scan_stats_params *scan, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length = 0; + uint32_t i; + int ret; + + ret = scnprintf(buf, buf_avail_len, + "Num of scan channels: %u\n" + "scan channel list:", + scan->num_scan_chans); + if (ret <= 0) + return length; + + length = ret; + + for (i = 0; i < scan->num_scan_chans; i++) { + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + return length; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "%u ", scan->scan_freqs[i]); + if (ret <= 0) + return length; + + length += ret; + } + + return length; +} + +/** + * wlan_hdd_update_roam_stats() - Internal function to get roam scan stats + * @hdd_ctx: hdd context + * @adapter: pointer to adapter + * @buf: buffer to hold the stats + * @len: maximum available length in response buffer + * + * Return: Size of formatted roam scan response stats + */ +static ssize_t +wlan_hdd_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length = 0; + struct wmi_roam_scan_stats_res *roam_stats; + struct wmi_roam_scan_stats_params *scan; + int ret; + int rsi; /* roam scan iterator */ + int rci; /* roam candidate iterator */ + + roam_stats = hdd_get_roam_scan_stats(hdd_ctx, adapter); + if (!roam_stats) { + hdd_err("Couldn't get roam stats"); + ret = scnprintf(buf, buf_avail_len, + "Failed to fetch roam stats\n"); + if (ret <= 0) + return length; + length += ret; + return length; + } + + ret = scnprintf(buf, buf_avail_len, + "\n\nStats of last %u roam scans\n", + roam_stats->num_roam_scans); + if (ret <= 0) + goto free_mem; + length += ret; + + for (rsi = 0; rsi < roam_stats->num_roam_scans; rsi++) { + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + scan = &roam_stats->roam_scan[rsi]; + ret = scnprintf(buf + length, buf_avail_len - length, + "\nRoam scan[%u] details\n", rsi); + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "This scan is triggered by \"%s\" scan client\n", + hdd_client_id_to_str(scan->client_id)); + + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + length += hdd_roam_scan_trigger(scan, buf + length, + buf_avail_len - length); + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + length += hdd_roam_scan_chan(scan, buf + length, + buf_avail_len - length); + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + if (scan->is_roam_successful) { + ret = scnprintf(buf + length, + buf_avail_len - length, + "\nSTA roamed from " + MAC_ADDRESS_STR " to " + MAC_ADDRESS_STR "\n", + MAC_ADDR_ARRAY(scan->old_bssid), + MAC_ADDR_ARRAY(scan->new_bssid)); + } else { + ret = scnprintf(buf + length, + buf_avail_len - length, + "\nSTA is connected to " MAC_ADDRESS_STR + " before and after scan, not roamed\n", + MAC_ADDR_ARRAY(scan->old_bssid)); + } + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "Roam candidate details\n"); + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + " BSSID FREQ SCORE RSSI\n"); + if (ret <= 0) + goto free_mem; + length += ret; + + for (rci = 0; rci < scan->num_roam_candidates; rci++) { + uint8_t *bssid = scan->cand[rci].bssid; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, + buf_avail_len - length, + MAC_ADDRESS_STR " %4u %3u %3u\n", + MAC_ADDR_ARRAY(bssid), + scan->cand[rci].freq, + scan->cand[rci].score, + scan->cand[rci].rssi); + if (ret <= 0) + goto free_mem; + length += ret; + } + } + +free_mem: + qdf_mem_free(roam_stats); + return length; +} + +ssize_t +wlan_hdd_debugfs_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t len = 0; + int ret_val; + + hdd_enter(); + + len = wlan_hdd_current_time_info_debugfs(buf, buf_avail_len - len); + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + if (adapter->device_mode != QDF_STA_MODE) { + ret_val = scnprintf(buf + len, buf_avail_len - len, + "Interface is not in STA Mode\n"); + if (ret_val <= 0) + return len; + + len += ret_val; + return len; + } + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + len += wlan_hdd_update_roam_stats(hdd_ctx, adapter, buf + len, + buf_avail_len - len); + + hdd_exit(); + + return len; +} diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h index 709260e48384..f18836634d57 100644 --- a/core/mac/inc/sir_api.h +++ b/core/mac/inc/sir_api.h @@ -7187,6 +7187,21 @@ struct sir_limit_off_chan { bool skip_dfs_chans; }; +typedef void (*roam_scan_stats_cb)(void *context, + struct wmi_roam_scan_stats_res *res); + +/** + * struct sir_roam_scan_stats - Stores roam scan context + * @vdev_id: vdev id + * @cb: callback to be invoked for roam scan stats response + * @context: context of callback + */ +struct sir_roam_scan_stats { + uint32_t vdev_id; + roam_scan_stats_cb cb; + void *context; +}; + /** * struct sae_info - SAE info used for commit/confirm messages * @msg_type: Message type diff --git a/core/mac/src/include/sir_params.h b/core/mac/src/include/sir_params.h index 67f93a26c9c1..d037f8a9ab34 100644 --- a/core/mac/src/include/sir_params.h +++ b/core/mac/src/include/sir_params.h @@ -686,6 +686,7 @@ struct sir_mgmt_msg { #define SIR_HAL_OBSS_COLOR_COLLISION_INFO (SIR_HAL_ITC_MSG_TYPES_BEGIN + 397) #define SIR_HAL_SEND_ADDBA_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 398) +#define SIR_HAL_GET_ROAM_SCAN_STATS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 399) #define SIR_HAL_MSG_TYPES_END (SIR_HAL_MSG_TYPES_BEGIN + 0x1FF) /* CFG message types */ diff --git a/core/mac/src/sys/legacy/src/utils/src/mac_trace.c b/core/mac/src/sys/legacy/src/utils/src/mac_trace.c index df0301f732fa..4f7553725252 100644 --- a/core/mac/src/sys/legacy/src/utils/src/mac_trace.c +++ b/core/mac/src/sys/legacy/src/utils/src/mac_trace.c @@ -651,6 +651,7 @@ uint8_t *mac_trace_get_wma_msg_string(uint16_t wma_msg) CASE_RETURN_STRING(WMA_SET_PER_ROAM_CONFIG_CMD); CASE_RETURN_STRING(WMA_GET_RCPI_REQ); CASE_RETURN_STRING(WMA_SET_DBS_SCAN_SEL_CONF_PARAMS); + CASE_RETURN_STRING(WMA_GET_ROAM_SCAN_STATS); default: return (uint8_t *) "UNKNOWN"; break; diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h index 8c8c17bcda17..81aef5893715 100644 --- a/core/sme/inc/sme_api.h +++ b/core/sme/inc/sme_api.h @@ -2775,4 +2775,18 @@ QDF_STATUS sme_deregister_twt_disable_complete_cb(mac_handle_t mac_handle) * false - if not has the session */ bool sme_find_session_by_bssid(tHalHandle hal, uint8_t *bssid); + +/** + * sme_get_roam_scan_stats() - Send roam scan stats cmd to wma + * @hal: handle returned by mac_open + * @cb: call-back invoked for roam scan stats response + * @context: context of callback + * @vdev_id: vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_get_roam_scan_stats(tHalHandle hal, roam_scan_stats_cb cb, void *context, + uint32_t vdev_id); + #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 8ebd3ad570e5..da3c8da647d8 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -16117,3 +16117,43 @@ bool sme_find_session_by_bssid(tHalHandle hal, uint8_t *bssid) return ret; } + +QDF_STATUS +sme_get_roam_scan_stats(tHalHandle hal, roam_scan_stats_cb cb, void *context, + uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + tpAniSirGlobal mac = PMAC_STRUCT(hal); + struct scheduler_msg msg = {0}; + struct sir_roam_scan_stats *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + sme_err("Failed allocate memory for roam scan stats req"); + return QDF_STATUS_E_NOMEM; + } + + req->vdev_id = vdev_id; + req->cb = cb; + req->context = context; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + msg.bodyptr = req; + msg.type = WMA_GET_ROAM_SCAN_STATS; + msg.reserved = 0; + status = scheduler_post_msg(QDF_MODULE_ID_WMA, &msg); + sme_release_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("post roam scan stats req failed")); + status = QDF_STATUS_E_FAILURE; + qdf_mem_free(req); + } + } else { + sme_err("sme_acquire_global_lock failed"); + qdf_mem_free(req); + } + + return status; +} diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index 01997972435f..438864464054 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -835,6 +835,7 @@ struct roam_synch_frame_ind { * @vdev_stop_wakelock: wakelock to protect vdev stop op with firmware * @vdev_set_key_wakelock: wakelock to protect vdev set key op with firmware * @channel: channel + * @roam_scan_stats_req: cached roam scan stats request * * It stores parameters per vdev in wma. */ @@ -924,6 +925,7 @@ struct wma_txrx_node { struct roam_synch_frame_ind roam_synch_frame_ind; bool is_waiting_for_key; uint8_t channel; + struct sir_roam_scan_stats *roam_scan_stats_req; }; /** diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h index 431c395fde2e..bd7064d24a15 100644 --- a/core/wma/inc/wma_internal.h +++ b/core/wma/inc/wma_internal.h @@ -1418,4 +1418,25 @@ int wma_vdev_bss_color_collision_info_handler(void *handle, int wma_twt_en_complete_event_handler(void *handle, uint8_t *event, uint32_t len); +/** + * wma_get_roam_scan_stats() - Get roam scan stats request + * @handle: wma handle + * @req: request details + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_get_roam_scan_stats(WMA_HANDLE handle, + struct sir_roam_scan_stats *req); + +/** + * wma_roam_scan_stats_event_handler() - roam scan stats event handler + * @handle: wma handle + * @event: event data + * @len: length of data + * + * Return: Success or Failure status + */ +int wma_roam_scan_stats_event_handler(void *handle, uint8_t *event, + uint32_t len); + #endif diff --git a/core/wma/inc/wma_types.h b/core/wma/inc/wma_types.h index bede100a0790..05c4e1089d2a 100644 --- a/core/wma/inc/wma_types.h +++ b/core/wma/inc/wma_types.h @@ -455,6 +455,8 @@ #define WMA_OBSS_COLOR_COLLISION_REQ SIR_HAL_OBSS_COLOR_COLLISION_REQ #define WMA_OBSS_COLOR_COLLISION_INFO SIR_HAL_OBSS_COLOR_COLLISION_INFO +#define WMA_GET_ROAM_SCAN_STATS SIR_HAL_GET_ROAM_SCAN_STATS + /* Bit 6 will be used to control BD rate for Management frames */ #define HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME 0x40 diff --git a/core/wma/src/wma_dev_if.c b/core/wma/src/wma_dev_if.c index 4ec710b5e740..b73d8c7b28e7 100644 --- a/core/wma/src/wma_dev_if.c +++ b/core/wma/src/wma_dev_if.c @@ -466,6 +466,14 @@ static void wma_vdev_detach_callback(void *ctx) } } + if (iface->roam_scan_stats_req) { + struct sir_roam_scan_stats *roam_scan_stats_req = + iface->roam_scan_stats_req; + + iface->roam_scan_stats_req = NULL; + qdf_mem_free(roam_scan_stats_req); + } + wma_vdev_deinit(iface); qdf_mem_zero(iface, sizeof(*iface)); wma_vdev_init(iface); @@ -5508,6 +5516,14 @@ void wma_delete_bss_ho_fail(tp_wma_handle wma, tpDeleteBssParams params) qdf_mem_free(rcpi_req); } + if (iface->roam_scan_stats_req) { + struct sir_roam_scan_stats *roam_scan_stats_req = + iface->roam_scan_stats_req; + + iface->roam_scan_stats_req = NULL; + qdf_mem_free(roam_scan_stats_req); + } + qdf_mem_zero(&iface->ns_offload_req, sizeof(iface->ns_offload_req)); qdf_mem_zero(&iface->arp_offload_req, @@ -5676,6 +5692,14 @@ void wma_delete_bss(tp_wma_handle wma, tpDeleteBssParams params) qdf_mem_free(rcpi_req); } + if (iface->roam_scan_stats_req) { + struct sir_roam_scan_stats *roam_scan_stats_req = + iface->roam_scan_stats_req; + + iface->roam_scan_stats_req = NULL; + qdf_mem_free(roam_scan_stats_req); + } + if (wlan_op_mode_ibss == cdp_get_opmode(soc, txrx_vdev)) wma->ibss_started = 0; diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index 6c9cde85166d..e052386ba84a 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -2997,6 +2997,14 @@ void wma_vdev_deinit(struct wma_txrx_node *vdev) vdev->rcpi_req = NULL; } + if (vdev->roam_scan_stats_req) { + struct sir_roam_scan_stats *req; + + req = vdev->roam_scan_stats_req; + vdev->roam_scan_stats_req = NULL; + qdf_mem_free(req); + } + if (vdev->roam_synch_frame_ind.bcn_probe_rsp) { qdf_mem_free(vdev->roam_synch_frame_ind.bcn_probe_rsp); vdev->roam_synch_frame_ind.bcn_probe_rsp = NULL; @@ -3428,6 +3436,12 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc, wmi_update_vdev_rate_stats_event_id, wma_link_status_event_handler, WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_scan_stats_event_id, + wma_roam_scan_stats_event_handler, + WMA_RX_SERIALIZER_CTX); + #ifdef WLAN_FEATURE_LINK_LAYER_STATS /* Register event handler for processing Link Layer Stats * response from the FW @@ -8506,6 +8520,10 @@ static QDF_STATUS wma_mc_process_msg(struct scheduler_msg *msg) wma_process_obss_color_collision_req(wma_handle, msg->bodyptr); qdf_mem_free(msg->bodyptr); break; + case WMA_GET_ROAM_SCAN_STATS: + wma_get_roam_scan_stats(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; default: WMA_LOGD("Unhandled WMA message of type %d", msg->type); if (msg->bodyptr) diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c index 9d5812dcb8da..c296d6e9ecbb 100644 --- a/core/wma/src/wma_utils.c +++ b/core/wma/src/wma_utils.c @@ -4426,7 +4426,7 @@ QDF_STATUS wma_get_rcpi_req(WMA_HANDLE handle, } int wma_rcpi_event_handler(void *handle, uint8_t *cmd_param_info, - uint32_t len) + uint32_t len) { struct rcpi_res res = {0}; struct sme_rcpi_req *rcpi_req; @@ -4609,3 +4609,116 @@ int wma_chip_power_save_failure_detected_handler(void *handle, WMA_LOGD("%s: Invoke HDD pwr_save_fail callback", __func__); return 0; } + +int wma_roam_scan_stats_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma_handle; + wmi_unified_t wmi_handle; + struct sir_roam_scan_stats *roam_scan_stats_req = NULL; + struct wma_txrx_node *iface = NULL; + struct wmi_roam_scan_stats_res *res = NULL; + int ret = 0; + uint32_t vdev_id; + QDF_STATUS status; + + wma_handle = handle; + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return -EINVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return -EINVAL; + } + + status = wmi_extract_roam_scan_stats_res_evt(wmi_handle, event, + &vdev_id, + &res); + + /* vdev_id can be invalid though status is success, hence validate */ + if (vdev_id >= wma_handle->max_bssid) { + WMA_LOGE(FL("Received invalid vdev_id: %d"), vdev_id); + ret = -EINVAL; + goto free_res; + } + + /* Get interface for valid vdev_id */ + iface = &wma_handle->interfaces[vdev_id]; + if (!iface) { + WMI_LOGE(FL("Interface not available for vdev_id: %d"), + vdev_id); + ret = -EINVAL; + goto free_res; + } + + roam_scan_stats_req = iface->roam_scan_stats_req; + iface->roam_scan_stats_req = NULL; + if (!roam_scan_stats_req) { + WMI_LOGE(FL("No pending request vdev_id: %d"), vdev_id); + ret = -EINVAL; + goto free_res; + } + + if (!QDF_IS_STATUS_SUCCESS(status) || + !roam_scan_stats_req->cb || + roam_scan_stats_req->vdev_id != vdev_id) { + WMI_LOGE(FL("roam_scan_stats buffer not available")); + ret = -EINVAL; + goto free_roam_scan_stats_req; + } + + roam_scan_stats_req->cb(roam_scan_stats_req->context, res); + +free_roam_scan_stats_req: + qdf_mem_free(roam_scan_stats_req); + roam_scan_stats_req = NULL; + +free_res: + qdf_mem_free(res); + res = NULL; + + return ret; +} + +QDF_STATUS wma_get_roam_scan_stats(WMA_HANDLE handle, + struct sir_roam_scan_stats *req) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + struct wmi_roam_scan_stats_req cmd = {0}; + struct wma_txrx_node *iface; + struct sir_roam_scan_stats *node_req = NULL; + + WMA_LOGD("%s: Enter", __func__); + iface = &wma_handle->interfaces[req->vdev_id]; + /* command is in progress */ + if (iface->roam_scan_stats_req) { + WMA_LOGE(FL("previous roam scan stats req is pending")); + return QDF_STATUS_SUCCESS; + } + + node_req = qdf_mem_malloc(sizeof(*node_req)); + if (!node_req) { + WMA_LOGE("Failed to allocate memory for roam scan stats req"); + return QDF_STATUS_E_NOMEM; + } + + *node_req = *req; + iface->roam_scan_stats_req = node_req; + cmd.vdev_id = req->vdev_id; + + if (wmi_unified_send_roam_scan_stats_cmd(wma_handle->wmi_handle, + &cmd)) { + WMA_LOGE("%s: Failed to send WMI_REQUEST_ROAM_SCAN_STATS_CMDID", + __func__); + iface->roam_scan_stats_req = NULL; + qdf_mem_free(node_req); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s: Exit", __func__); + + return QDF_STATUS_SUCCESS; +}