浏览代码

qcacld-3.0: Add debugfs support for roam scan stats

Add debugfs entry to get last five roam scan stats info which includes
old bssid, new bssid, roaming candidates etc., for STA interface.

Change-Id: Ied66beb270d97b6e17a6116c1be0e82783094519
CRs-Fixed: 2203885
Rajeev Kumar Sirasanagandla 7 年之前
父节点
当前提交
4f20b67e7a

+ 1 - 0
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
 

+ 38 - 0
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 */

+ 14 - 0
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)

+ 571 - 0
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 <wlan_hdd_debugfs_csr.h>
+#include <wlan_hdd_main.h>
+#include <cds_sched.h>
+#include <wma_api.h>
+#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(&params);
+	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;
+}

+ 15 - 0
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

+ 1 - 0
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 */

+ 1 - 0
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;

+ 14 - 0
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 ) */

+ 40 - 0
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;
+}

+ 2 - 0
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;
 };
 
 /**

+ 21 - 0
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

+ 2 - 0
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
 

+ 24 - 0
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;
 

+ 18 - 0
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)

+ 114 - 1
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;
+}