Kaynağa Gözat

qcacld-3.0: Handle WMI_ROAM_STATS_EVENTID from firmware

The event WMI_ROAM_STATS_EVENTID is received after
every roam, once the roam synch complete is sent by the host.
This event contains details regarding the roam trigger reason,
values associated with the trigger, roam scan candidate info,
roam status, roam failure reason.

This helps in debugging/understanding the scenario when roam
failure happens.

WMI_ROAM_STATS_EVENTID Format:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
roam_stats_event_fixed_param |-> Contains vdev id and number of
                             |   roams detail.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
roam_trigger data TLV        |-> Has details on the roam trigger
                             |   reason. Number of roam triggers
                             |   TLV detail is present in the
                             |   fixed param. Maximum 5 roam
                             |   per event is supported
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wmi_roam_scan_info TLV       |-> Has details on the roam scan -
                             |   scan type, rssi threshold,
                             |   reason.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wmi_roam_scan_channel_info   |-> Has details on the roam scan
TLV                          |   channels. Number of channels
                             |   per trigger is found on the
                             |   wmi_roam_scan_info TLV.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wmi_roam_ap_info TLV         |-> Has details on Roam candidate
                             |   APs found during scan. Number
                             |   of roam candidate APs per
                             |   trigger is found on the
                             |   on the wmi_roam_scan_info tlv.
                             |   Max 8 candidates are allowed
                             |   per roam scan.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wmi_roam_result TLV          |-> Info on roam success or failure.
                             |   Fail reason, if roaming failed.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wmi_roam_neighbor_report_info|->After roaming, firmware sends
TLV                          |  neighbor/btm report to roamed AP
                             |  to get list of roam candidates.
                             |  This tlv provides info on this.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wmi_roam_neighbor_channel_info|-> This has the info on the
                              |   channel list received as part
                              |   of BTM/Neighbor report.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Print the info received as part of this event into kmsg.
Featurize all the function defines for this roam logging under
WLAN_FEATURE_ROAM_OFFLOAD flag.

Change-Id: Iddc70b572f2e3dc47f08fd1c3c3597cb44981a17
CRs-Fixed: 2576222
Pragaspathi Thilagaraj 5 yıl önce
ebeveyn
işleme
5292e0ab83

+ 2 - 2
components/mlme/dispatcher/inc/cfg_mlme_generic.h

@@ -582,7 +582,7 @@
  * gRemoveTimeStampSyncCmd - Enable/Disable to remove time stamp sync cmd
  * @Min: 0
  * @Max: 1
- * @Default: 1
+ * @Default: 0
  *
  * This ini is used to enable/disable the removal of time stamp sync cmd
  *
@@ -592,7 +592,7 @@
  */
 #define CFG_REMOVE_TIME_STAMP_SYNC_CMD CFG_INI_BOOL( \
 	"gRemoveTimeStampSyncCmd", \
-	1, \
+	0, \
 	"Enable to remove time stamp sync cmd")
 
 /*

+ 37 - 0
components/mlme/dispatcher/inc/wlan_mlme_api.h

@@ -2258,4 +2258,41 @@ QDF_STATUS mlme_set_tgt_wpa3_roam_cap(struct wlan_objmgr_psoc *psoc,
 QDF_STATUS
 wlan_mlme_get_ignore_fw_reg_offload_ind(struct wlan_objmgr_psoc *psoc,
 					bool *disabled);
+
+/**
+ * mlme_get_roam_trigger_str() - Get the string for enum
+ * WMI_ROAM_TRIGGER_REASON_ID reason.
+ * @roam_scan_trigger: roam scan trigger ID
+ *
+ *  Return: Meaningful string from enum WMI_ROAM_TRIGGER_REASON_ID
+ */
+char *mlme_get_roam_trigger_str(uint32_t roam_scan_trigger);
+
+/**
+ * mlme_get_converted_timestamp() - Return time of the day
+ * from timestamp
+ * @timestamp:    Timestamp value in milliseconds
+ * @time:         Output buffer to fill time into
+ *
+ * Return: Time of the day in [HH:MM:SS.uS]
+ */
+void mlme_get_converted_timestamp(uint32_t timestamp, char *time);
+
+/**
+ * mlme_get_roam_fail_reason_str() - Get fail string from enum
+ * WMI_ROAM_FAIL_REASON_ID
+ * @result:   Roam fail reason
+ *
+ * Return: Meaningful string from enum
+ */
+char *mlme_get_roam_fail_reason_str(uint32_t result);
+
+/**
+ * mlme_get_sub_reason_str() - Get roam trigger sub reason from enum
+ * WMI_ROAM_TRIGGER_SUB_REASON_ID
+ * @sub_reason: Sub reason value
+ *
+ * Return: Meaningful string from enum WMI_ROAM_TRIGGER_SUB_REASON_ID
+ */
+char *mlme_get_sub_reason_str(uint32_t sub_reason);
 #endif /* _WLAN_MLME_API_H_ */

+ 22 - 0
components/mlme/dispatcher/inc/wlan_mlme_public_struct.h

@@ -41,6 +41,14 @@
 #define CFG_VHT_TX_MCS_MAP_STAMAX    0xFFFF
 #define CFG_VHT_TX_MCS_MAP_STADEF    0xFFFE
 
+/* Roam debugging related macro defines */
+#define MAX_ROAM_DEBUG_BUF_SIZE    250
+#define MAX_ROAM_EVENTS_SUPPORTED  5
+#define ROAM_FAILURE_BUF_SIZE      40
+#define TIME_STRING_LEN            24
+
+#define ROAM_CHANNEL_BUF_SIZE      300
+#define LINE_STR "========================================="
 /*
  * MLME_CFG_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED_FW_DEF + 1 is
  * assumed to be the default fw supported BF antennas, if fw
@@ -2280,4 +2288,18 @@ struct wlan_mlme_cfg {
 	struct roam_trigger_min_rssi trig_min_rssi[NUM_OF_ROAM_TRIGGERS];
 };
 
+/**
+ * struct mlme_roam_debug_info - Roam debug information storage structure.
+ * @trigger:            Roam trigger related data
+ * @scan:               Roam scan related data structure.
+ * @result:             Roam result parameters.
+ * @data_11kv:          Neighbor report/BTM parameters.
+ */
+struct mlme_roam_debug_info {
+	struct wmi_roam_trigger_info trigger;
+	struct wmi_roam_scan_data scan;
+	struct wmi_roam_result result;
+	struct wmi_neighbor_report_data data_11kv;
+};
+
 #endif

+ 100 - 0
components/mlme/dispatcher/src/wlan_mlme_api.c

@@ -3545,3 +3545,103 @@ wlan_mlme_get_ignore_fw_reg_offload_ind(struct wlan_objmgr_psoc *psoc,
 	*disabled = mlme_obj->cfg.reg.ignore_fw_reg_offload_ind;
 	return QDF_STATUS_SUCCESS;
 }
+
+char *mlme_get_roam_trigger_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 "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";
+	case WMI_ROAM_TRIGGER_REASON_BSS_LOAD:
+		return "HIGH BSS LOAD";
+	case WMI_ROAM_TRIGGER_REASON_DEAUTH:
+		return "DEAUTH RECEIVED";
+	case WMI_ROAM_TRIGGER_REASON_IDLE:
+		return "IDLE STATE SCAN";
+	case WMI_ROAM_TRIGGER_REASON_NONE:
+		return "NONE";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+void mlme_get_converted_timestamp(uint32_t timestamp, char *time)
+{
+	uint32_t hr, mins, secs;
+
+	secs = timestamp / 1000;
+	mins = secs / 60;
+	hr = mins / 60;
+	qdf_snprintf(time, TIME_STRING_LEN, "[%02d:%02d:%02d.%06u]",
+		     (hr % 24), (mins % 60), (secs % 60),
+		     (timestamp % 1000) * 1000);
+}
+
+char *mlme_get_roam_fail_reason_str(uint32_t result)
+{
+	switch (result) {
+	case WMI_ROAM_FAIL_REASON_NO_SCAN_START:
+		return "SCAN NOT STARTED";
+	case WMI_ROAM_FAIL_REASON_NO_AP_FOUND:
+		return "NO AP FOUND";
+	case WMI_ROAM_FAIL_REASON_NO_CAND_AP_FOUND:
+		return "NO CANDIDATE FOUND";
+	case WMI_ROAM_FAIL_REASON_HOST:
+		return "HOST ABORTED";
+	case WMI_ROAM_FAIL_REASON_AUTH_SEND:
+		return "Send AUTH Failed";
+	case WMI_ROAM_FAIL_REASON_AUTH_RECV:
+		return "Received AUTH with FAILURE Status";
+	case WMI_ROAM_FAIL_REASON_NO_AUTH_RESP:
+		return "No Auth response from AP";
+	case WMI_ROAM_FAIL_REASON_REASSOC_SEND:
+		return "Send Re-assoc request failed";
+	case WMI_ROAM_FAIL_REASON_REASSOC_RECV:
+		return "Received Re-Assoc resp with Failure status";
+	case WMI_ROAM_FAIL_REASON_NO_REASSOC_RESP:
+		return "No Re-assoc response from AP";
+	case WMI_ROAM_FAIL_REASON_EAPOL_TIMEOUT:
+		return "EAPOL timed out";
+	case WMI_ROAM_FAIL_REASON_MLME:
+		return "MLME error";
+	case WMI_ROAM_FAIL_REASON_INTERNAL_ABORT:
+		return "Target aborted roam";
+	default:
+		return "NONE";
+	}
+}
+
+char *mlme_get_sub_reason_str(uint32_t sub_reason)
+{
+	switch (sub_reason) {
+	case WMI_ROAM_TRIGGER_SUB_REASON_PERIODIC_TIMER:
+		return "PERIODIC TIMER";
+	case WMI_ROAM_TRIGGER_SUB_REASON_INACTIVITY_TIMER:
+		return "INACTIVITY TIMER";
+	case WMI_ROAM_TRIGGER_SUB_REASON_BTM_DI_TIMER:
+		return "BTM DISASSOC TIMER";
+	case WMI_ROAM_TRIGGER_SUB_REASON_FULL_SCAN:
+		return "FULL SCAN";
+	default:
+		return "NONE";
+	}
+}

+ 23 - 0
core/wma/inc/wma_internal.h

@@ -195,6 +195,22 @@ int wma_roam_synch_event_handler(void *handle, uint8_t *event,
 int wma_roam_auth_offload_event_handler(WMA_HANDLE handle, uint8_t *event,
 					uint32_t len);
 
+/**
+ * wma_roam_stats_event_handler() - Handle the WMI_ROAM_STATS_EVENTID
+ * from target
+ * @handle: wma_handle
+ * @event:  roam debug stats event data pointer
+ * @len: length of the data
+ *
+ * This function handles the roam debug stats from the target and logs it
+ * to kmsg. This WMI_ROAM_STATS_EVENTID event is received whenever roam
+ * scan trigger happens or when neighbor report is sent by the firmware.
+ *
+ * Return: Success or Failure status
+ */
+int wma_roam_stats_event_handler(WMA_HANDLE handle, uint8_t *event,
+				 uint32_t len);
+
 /**
  * wma_mlme_roam_synch_event_handler_cb() - roam synch event handler
  * @handle: wma handle
@@ -228,6 +244,13 @@ static inline int wma_mlme_roam_synch_event_handler_cb(void *handle,
 {
 	return 0;
 }
+
+static inline int
+wma_roam_stats_event_handler(WMA_HANDLE handle, uint8_t *event,
+			     uint32_t len)
+{
+	return 0;
+}
 #endif
 
 /**

+ 5 - 0
core/wma/src/wma_main.c

@@ -3243,6 +3243,11 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc,
 					   wmi_roam_auth_offload_event_id,
 					   wma_roam_auth_offload_event_handler,
 					   WMA_RX_SERIALIZER_CTX);
+
+	wmi_unified_register_event_handler(wma_handle->wmi_handle,
+					   wmi_roam_stats_event_id,
+					   wma_roam_stats_event_handler,
+					   WMA_RX_SERIALIZER_CTX);
 #endif /* WLAN_FEATURE_ROAM_OFFLOAD */
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 				wmi_rssi_breach_event_id,

+ 406 - 0
core/wma/src/wma_scan_roam.c

@@ -3276,6 +3276,412 @@ int wma_roam_auth_offload_event_handler(WMA_HANDLE handle, uint8_t *event,
 	return 0;
 }
 
+#ifdef WLAN_FEATURE_ROAM_OFFLOAD
+static void
+wma_get_trigger_detail_str(struct wmi_roam_trigger_info roam_info, char *buf)
+{
+	uint16_t buf_cons, buf_left = MAX_ROAM_DEBUG_BUF_SIZE;
+	char *temp = buf;
+
+	buf_cons = qdf_snprint(temp, buf_left, "Reason: \"%s\" ",
+			mlme_get_roam_trigger_str(roam_info.trigger_reason));
+	temp += buf_cons;
+	buf_left -= buf_cons;
+
+	if (roam_info.trigger_sub_reason) {
+		buf_cons = qdf_snprint(
+			    temp, buf_left, "Sub-Reason: %s",
+			    mlme_get_sub_reason_str(roam_info.trigger_sub_reason));
+		temp += buf_cons;
+		buf_left -= buf_cons;
+	}
+
+	switch (roam_info.trigger_reason) {
+	case WMI_ROAM_TRIGGER_REASON_PER:
+	case WMI_ROAM_TRIGGER_REASON_BMISS:
+	case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI:
+	case WMI_ROAM_TRIGGER_REASON_PERIODIC:
+	case WMI_ROAM_TRIGGER_REASON_MAWC:
+	case WMI_ROAM_TRIGGER_REASON_DENSE:
+	case WMI_ROAM_TRIGGER_REASON_BACKGROUND:
+	case WMI_ROAM_TRIGGER_REASON_IDLE:
+	case WMI_ROAM_TRIGGER_REASON_FORCED:
+	case WMI_ROAM_TRIGGER_REASON_UNIT_TEST:
+		return;
+	case WMI_ROAM_TRIGGER_REASON_BTM:
+		buf_cons = qdf_snprint(temp, buf_left,
+				       "Req_mode: %d Disassoc_timer: %d",
+				       roam_info.btm_trig_data.btm_request_mode,
+				       roam_info.btm_trig_data.disassoc_timer);
+		temp += buf_cons;
+		buf_left -= buf_cons;
+
+		buf_cons = qdf_snprint(temp, buf_left,
+			    "validity_interval: %d candidate_list_cnt: %d resp_status: %d",
+			    roam_info.btm_trig_data.validity_interval,
+			    roam_info.btm_trig_data.candidate_list_count,
+			    roam_info.btm_trig_data.btm_resp_status);
+		buf_left -= buf_cons;
+		temp += buf_cons;
+		return;
+	case WMI_ROAM_TRIGGER_REASON_BSS_LOAD:
+		buf_cons = qdf_snprint(temp, buf_left, "CU: %d %% ",
+				       roam_info.cu_trig_data.cu_load);
+		temp += buf_cons;
+		buf_left -= buf_cons;
+		return;
+	case WMI_ROAM_TRIGGER_REASON_DEAUTH:
+		buf_cons = qdf_snprint(temp, buf_left, "Type: %d Reason: %d ",
+				       roam_info.deauth_trig_data.type,
+				       roam_info.deauth_trig_data.reason);
+		temp += buf_cons;
+		buf_left -= buf_cons;
+		return;
+	case WMI_ROAM_TRIGGER_REASON_LOW_RSSI:
+		buf_cons = qdf_snprint(temp, buf_left, "Low_rssi_threshold: %d",
+				       roam_info.rssi_trig_data.threshold);
+		temp += buf_cons;
+		buf_left -= buf_cons;
+		return;
+	default:
+		return;
+	}
+}
+
+static void
+wma_rso_print_trigger_info(struct wmi_roam_trigger_info data, uint8_t vdev_id)
+{
+	char *buf;
+	char time[TIME_STRING_LEN];
+
+	buf = qdf_mem_malloc(MAX_ROAM_DEBUG_BUF_SIZE);
+	if (!buf)
+		return;
+
+	wma_get_trigger_detail_str(data, buf);
+	mlme_get_converted_timestamp(data.timestamp, time);
+	WMA_LOGI("[ROAM_DEBUG] %s [ROAM_TRIGGER]: VDEV[%d] Current_rssi: %d dBm %s",
+		 time, vdev_id, data.current_rssi, buf);
+
+	qdf_mem_free(buf);
+}
+
+static void
+wma_log_roam_scan_candidates(struct wmi_roam_candidate_info *ap,
+			     uint8_t num_entries)
+{
+	uint16_t i;
+	char time[TIME_STRING_LEN];
+
+	WMA_LOGI("%40s%40s%40s", LINE_STR, LINE_STR, LINE_STR);
+	WMA_LOGI("%13s %20s %11s %6s %12s %8s %12s %8s %10s %13s",
+		 "AP BSSID", "TIMESTAMP", "CHANNEL", "TYPE", "ETP", "RSSI",
+		 "RSSI_SCORE", "CU_LOAD", "CU_SCORE", "TOTAL_SCORE");
+	WMA_LOGI("%40s%40s%40s", LINE_STR, LINE_STR, LINE_STR);
+
+	if (num_entries > MAX_ROAM_CANDIDATE_AP)
+		num_entries = MAX_ROAM_CANDIDATE_AP;
+
+	for (i = 0; i < num_entries; i++) {
+		mlme_get_converted_timestamp(ap->timestamp, time);
+		WMA_LOGI(QDF_MAC_ADDR_STR " %20s %5d  %-12s %4d(Mbps) %3d(dBm) %6d %7d %% %10d %12d",
+			 QDF_MAC_ADDR_ARRAY(ap->bssid.bytes), time, ap->freq,
+			 ((ap->type == 0) ? "CANDIDATE_AP" :
+			  ((ap->type == 2) ? "ROAMED_AP" : "PREVIOUS_AP")),
+			 ap->etp, ap->rssi, ap->rssi_score, ap->cu_load,
+			 ap->cu_score, ap->total_score);
+		ap++;
+	}
+}
+
+static void
+wma_rso_print_scan_info(struct wmi_roam_scan_data scan, uint8_t vdev_id,
+			uint32_t trigger, uint32_t timestamp)
+{
+	uint16_t num_ch = scan.num_chan;
+	uint16_t buf_cons = 0, buf_left = ROAM_CHANNEL_BUF_SIZE;
+	uint8_t i;
+	char *buf, *buf1, *tmp;
+	char time[TIME_STRING_LEN];
+
+	buf = qdf_mem_malloc(ROAM_CHANNEL_BUF_SIZE);
+	if (!buf)
+		return;
+
+	tmp = buf;
+	/* For partial scans, print the channel info */
+	if (!scan.type) {
+		buf_cons = qdf_snprint(tmp, buf_left, "{");
+		buf_left -= buf_cons;
+		tmp += buf_cons;
+
+		for (i = 0; i < num_ch; i++) {
+			buf_cons = qdf_snprint(tmp, buf_left, "%d ",
+					       scan.chan_freq[i]);
+			buf_left -= buf_cons;
+			tmp += buf_cons;
+		}
+		buf_cons = qdf_snprint(tmp, buf_left, "}");
+		buf_left -= buf_cons;
+		tmp += buf_cons;
+	}
+
+	buf1 = qdf_mem_malloc(ROAM_FAILURE_BUF_SIZE);
+	if (!buf1) {
+		qdf_mem_free(buf);
+		return;
+	}
+
+	if (WMI_ROAM_TRIGGER_REASON_LOW_RSSI == trigger) {
+		qdf_snprint(buf1, ROAM_FAILURE_BUF_SIZE,
+			    "next_rssi_threshold: %d dBm",
+			    scan.next_rssi_threshold);
+	}
+
+	mlme_get_converted_timestamp(timestamp, time);
+	WMA_LOGI("[ROAM_DEBUG] %s [ROAM_SCAN]: VDEV[%d] Scan_type: %s %s %s",
+		 time, vdev_id, (scan.type ? "FULL" : "PARTIAL"),
+		 buf1, buf);
+	wma_log_roam_scan_candidates(scan.ap, scan.num_ap);
+
+	qdf_mem_free(buf);
+	qdf_mem_free(buf1);
+}
+
+static void
+wma_rso_print_roam_result(struct wmi_roam_result res,
+			  uint8_t vdev_id)
+{
+	char *buf;
+	char time[TIME_STRING_LEN];
+
+	buf = qdf_mem_malloc(ROAM_FAILURE_BUF_SIZE);
+	if (!buf)
+		return;
+
+	if (!res.status)
+		qdf_snprint(buf, ROAM_FAILURE_BUF_SIZE, "Fail-Reason: %s",
+			    mlme_get_roam_fail_reason_str(res.fail_reason));
+
+	mlme_get_converted_timestamp(res.timestamp, time);
+	WMA_LOGI("[ROAM_DEBUG] %s [ROAM_RESULT]: VDEV[%d] status:%s %s",
+		 time, vdev_id, (res.status) ? "SUCCESS" : "FAILED", buf);
+
+	qdf_mem_free(buf);
+}
+
+static void
+wma_rso_print_11kv_info(struct wmi_neighbor_report_data neigh_rpt,
+			uint8_t vdev_id, bool is_roam_success)
+{
+	char time[TIME_STRING_LEN], time1[TIME_STRING_LEN];
+	char *buf, *tmp;
+	uint8_t type = neigh_rpt.req_type, i;
+	uint16_t buf_left = ROAM_CHANNEL_BUF_SIZE, buf_cons;
+	uint8_t num_ch = neigh_rpt.num_freq;
+
+	if (!is_roam_success)
+		return;
+
+	if (!type) {
+		WMA_LOGI("[ROAM_DEBUG] AP doesn't support neighbor rpt/BTM");
+		return;
+	}
+
+	buf = qdf_mem_malloc(ROAM_CHANNEL_BUF_SIZE);
+	if (!buf)
+		return;
+
+	tmp = buf;
+	for (i = 0; i < num_ch; i++) {
+		buf_cons = qdf_snprint(tmp, buf_left, "{ ");
+		buf_left -= buf_cons;
+		tmp += buf_cons;
+
+		buf_cons = qdf_snprint(tmp, buf_left, "%d ",
+				       neigh_rpt.freq[i]);
+		buf_left -= buf_cons;
+		tmp += buf_cons;
+
+		buf_cons = qdf_snprint(tmp, buf_left, "}");
+		buf_left -= buf_cons;
+		tmp += buf_cons;
+	}
+	mlme_get_converted_timestamp(neigh_rpt.req_time, time);
+	WMA_LOGI("[ROAM_DEBUG] %s [%s] RX: VDEV[%d] %s", time,
+		 (type == 1) ? "BTM" : "NEIGH_RPT", vdev_id, buf);
+
+	mlme_get_converted_timestamp(neigh_rpt.resp_time, time1);
+	WMA_LOGI("[ROAM_DEBUG] %s [%s] TX: VDEV[%d]", time1,
+		 (type == 1) ? "BTM" : "NEIGH_RPT", vdev_id);
+	qdf_mem_free(buf);
+}
+
+int wma_roam_stats_event_handler(WMA_HANDLE handle, uint8_t *event,
+				 uint32_t len)
+{
+	tp_wma_handle wma = (tp_wma_handle) handle;
+	WMI_ROAM_STATS_EVENTID_param_tlvs *param_buf;
+	wmi_roam_stats_event_fixed_param *fixed_param;
+	struct mlme_roam_debug_info *roam_info = NULL;
+	uint8_t vdev_id, i;
+	uint8_t num_tlv = 0, num_chan = 0, num_ap = 0, num_rpt = 0;
+	uint32_t rem_len;
+	QDF_STATUS status;
+
+	param_buf = (WMI_ROAM_STATS_EVENTID_param_tlvs *)event;
+	if (!param_buf) {
+		wma_err_rl("NULL event received from target");
+		goto err;
+	}
+
+	fixed_param = param_buf->fixed_param;
+	if (!fixed_param) {
+		wma_err_rl(" NULL fixed param");
+		goto err;
+	}
+
+	vdev_id = fixed_param->vdev_id;
+	if (vdev_id >= wma->max_bssid) {
+		wma_err_rl("Invalid vdev_id %d", vdev_id);
+		goto err;
+	}
+
+	num_tlv = fixed_param->roam_scan_trigger_count;
+	if (num_tlv != param_buf->num_roam_scan_info ||
+	    num_tlv != param_buf->num_roam_trigger_reason ||
+	    num_tlv != param_buf->num_roam_result ||
+	    num_tlv != param_buf->num_roam_neighbor_report_info) {
+		wma_err_rl("Invalid roam stats num_tlv:%d num_scan:%d num_res:%d num_rept:%d",
+			   num_tlv, param_buf->num_roam_scan_info,
+			   param_buf->num_roam_result,
+			   param_buf->num_roam_neighbor_report_info);
+		goto err;
+	}
+
+	rem_len = WMI_SVC_MSG_MAX_SIZE - sizeof(*fixed_param);
+	if (rem_len < num_tlv * sizeof(wmi_roam_trigger_reason)) {
+		wma_err_rl("Invalid roam trigger data");
+		goto err;
+	}
+
+	rem_len -= num_tlv * sizeof(wmi_roam_trigger_reason);
+	if (rem_len < num_tlv * sizeof(wmi_roam_scan_info)) {
+		wma_err_rl("Invalid roam scan data");
+		goto err;
+	}
+
+	rem_len -= num_tlv * sizeof(wmi_roam_scan_info);
+	if (rem_len < num_tlv * sizeof(wmi_roam_result)) {
+		wma_err_rl("Invalid roam result data");
+		goto err;
+	}
+
+	rem_len -= num_tlv * sizeof(wmi_roam_result);
+	if (rem_len < (num_tlv * sizeof(wmi_roam_neighbor_report_info))) {
+		wma_err_rl("Invalid roam neighbor report data");
+		goto err;
+	}
+
+	rem_len -= num_tlv * sizeof(wmi_roam_neighbor_report_info);
+	if (rem_len < (param_buf->num_roam_scan_chan_info *
+		       sizeof(wmi_roam_scan_channel_info))) {
+		wma_err_rl("Invalid roam chan data num_tlv:%d",
+			   param_buf->num_roam_scan_chan_info);
+		goto err;
+	}
+
+	rem_len -= param_buf->num_roam_scan_chan_info *
+		   sizeof(wmi_roam_scan_channel_info);
+
+	if (rem_len < (param_buf->num_roam_ap_info *
+		       sizeof(wmi_roam_ap_info))) {
+		wma_err_rl("Invalid roam ap data num_tlv:%d",
+			   param_buf->num_roam_ap_info);
+		goto err;
+	}
+
+	rem_len -= param_buf->num_roam_ap_info * sizeof(wmi_roam_ap_info);
+	if (rem_len < (param_buf->num_roam_neighbor_report_chan_info *
+		       sizeof(wmi_roam_neighbor_report_channel_info))) {
+		wma_err_rl("Invalid roam neigb rpt chan data num_tlv:%d",
+			   param_buf->num_roam_neighbor_report_chan_info);
+		goto err;
+	}
+
+	for (i = 0; i < num_tlv; i++) {
+		roam_info = qdf_mem_malloc(sizeof(*roam_info));
+		if (!roam_info)
+			return -ENOMEM;
+
+		/*
+		 * Roam Trigger id and that specific roam trigger related
+		 * details.
+		 */
+		status = wmi_unified_extract_roam_trigger_stats(
+				wma->wmi_handle, event,
+				&roam_info->trigger, i);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			WMA_LOGE("%s: Extract roam trigger stats failed vdev%d",
+				 __func__, vdev_id);
+			qdf_mem_free(roam_info);
+			return -EINVAL;
+		}
+
+		/* Roam scan related details - Scan channel, scan type .. */
+		status = wmi_unified_extract_roam_scan_stats(
+				wma->wmi_handle, event, &roam_info->scan, i,
+				num_chan, num_ap);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			WMA_LOGE("%s: Roam scan stats extract failed vdev %d",
+				 __func__, vdev_id);
+			qdf_mem_free(roam_info);
+			return -EINVAL;
+		}
+		num_chan += roam_info->scan.num_chan;
+		num_ap += roam_info->scan.num_ap;
+
+		/* Roam result - Success/Failure status, failure reason */
+		status = wmi_unified_extract_roam_result_stats(
+						wma->wmi_handle, event,
+						&roam_info->result, i);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			WMA_LOGE("%s: Roam result stats extract failed vdev %d",
+				 __func__, vdev_id);
+			qdf_mem_free(roam_info);
+			return -EINVAL;
+		}
+
+		/* BTM req/resp or Neighbor report/response info */
+		status = wmi_unified_extract_roam_11kv_stats(
+				wma->wmi_handle, event,
+				&roam_info->data_11kv, i, num_rpt);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			WMA_LOGE("%s: Roam 11kv stats extract failed vdev %d",
+				 __func__, vdev_id);
+			qdf_mem_free(roam_info);
+			return -EINVAL;
+		}
+		num_rpt += roam_info->data_11kv.num_freq;
+
+		wma_rso_print_trigger_info(roam_info->trigger, vdev_id);
+		wma_rso_print_scan_info(roam_info->scan, vdev_id,
+					roam_info->trigger.trigger_reason,
+					roam_info->trigger.timestamp);
+		wma_rso_print_roam_result(roam_info->result, vdev_id);
+		wma_rso_print_11kv_info(roam_info->data_11kv, vdev_id,
+					roam_info->result.status);
+
+		qdf_mem_free(roam_info);
+	}
+
+	return 0;
+
+err:
+	return -EINVAL;
+}
+#endif
+
 #define RSN_CAPS_SHIFT               16
 /**
  * wma_roam_scan_fill_self_caps() - fill capabilities