Parcourir la source

qcacld-3.0: Fill the vendor attributes with the Roam stats

This change gathers the roam event stats from the FW.
New vendor event is used to fill in the vendor attributes
whenever the roam stats are received from Firmware.

Change-Id: I9a2ddef62d26b0b68897891788733df452ac1ceb
CRs-Fixed: 3036518
Srikanth Marepalli il y a 3 ans
Parent
commit
bbeccbc9ce

+ 27 - 0
components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_api.h

@@ -1108,6 +1108,26 @@ wlan_cm_update_roam_rt_stats(struct wlan_objmgr_psoc *psoc,
 uint8_t
 wlan_cm_get_roam_rt_stats(struct wlan_objmgr_psoc *psoc,
 			  enum roam_rt_stats_params stats);
+
+/**
+ * cm_report_roam_rt_stats - Gathers/Sends the roam events stats
+ * @psoc:      Pointer to psoc structure
+ * @vdev_id:   Vdev ID
+ * @events:    Event/Notif type from roam event/roam stats event
+ * @roam_info: Roam stats from the roam stats event
+ * @value:     Notif param value from the roam event
+ * @idx:       TLV index in roam stats event
+ *
+ * Gathers the roam stats from the roam event and the roam stats event and
+ * sends them to hdd for filling the vendor attributes.
+ *
+ * Return: none
+ */
+void cm_report_roam_rt_stats(struct wlan_objmgr_psoc *psoc,
+			     uint8_t vdev_id,
+			     enum roam_rt_stats_type events,
+			     struct roam_stats_event *roam_info,
+			     uint32_t value, uint8_t idx);
 #else
 static inline
 void wlan_cm_roam_activate_pcl_per_vdev(struct wlan_objmgr_psoc *psoc,
@@ -1267,6 +1287,13 @@ wlan_cm_get_roam_rt_stats(struct wlan_objmgr_psoc *psoc,
 {
 	return 0;
 }
+
+static inline void
+cm_report_roam_rt_stats(struct wlan_objmgr_psoc *psoc,
+			uint8_t vdev_id, enum roam_rt_stats_type events,
+			struct roam_stats_event *roam_info,
+			uint32_t value, uint8_t idx)
+{}
 #endif /* WLAN_FEATURE_ROAM_OFFLOAD */
 
 #ifdef WLAN_FEATURE_FIPS

+ 29 - 0
components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_public_struct.h

@@ -1851,6 +1851,30 @@ struct roam_msg_info {
 	uint32_t msg_param2;
 };
 
+/**
+ * struct roam_event_rt_info - Roam event related information
+ * @roam_scan_state: roam scan state notif value
+ * @roam_invoke_fail_reason: roam invoke fail reason
+ */
+struct roam_event_rt_info {
+	uint32_t roam_scan_state;
+	uint32_t roam_invoke_fail_reason;
+};
+
+/**
+ * enum roam_rt_stats_type: different types of params to get roam event stats
+ * for the vdev
+ * @ROAM_RT_STATS_TYPE_SCAN_STATE: Roam Scan Start/End
+ * @ROAM_RT_STATS_TYPE_INVOKE_FAIL_REASON: One of WMI_ROAM_FAIL_REASON_ID for
+ * roam failure in case of forced roam
+ * @ROAM_RT_STATS_TYPE_ROAM_SCAN_INFO: Roam Trigger/Fail/Scan/AP Stats
+ */
+enum roam_rt_stats_type {
+	ROAM_RT_STATS_TYPE_SCAN_STATE,
+	ROAM_RT_STATS_TYPE_INVOKE_FAIL_REASON,
+	ROAM_RT_STATS_TYPE_ROAM_SCAN_INFO,
+};
+
 /**
  * struct roam_frame_info  - Structure to hold the mgmt frame/eapol frame
  * related info exchanged during roaming.
@@ -1958,6 +1982,8 @@ struct roam_invoke_req {
  * @CM_ROAM_NOTIF_DISASSOC_RECV: indicate disassoc received, notif_params to be
 				 sent as reason code, notif_params1 to be sent
 				 as frame length
+ * @CM_ROAM_NOTIF_SCAN_END: indicate roam scan end, notif_params to be sent
+			      as WMI_ROAM_TRIGGER_REASON_ID
  */
 enum cm_roam_notif {
 	CM_ROAM_NOTIF_INVALID = 0,
@@ -1971,6 +1997,7 @@ enum cm_roam_notif {
 	CM_ROAM_NOTIF_SCAN_START,
 	CM_ROAM_NOTIF_DEAUTH_RECV,
 	CM_ROAM_NOTIF_DISASSOC_RECV,
+	CM_ROAM_NOTIF_SCAN_END = 12,
 };
 
 /**
@@ -2126,6 +2153,7 @@ struct roam_offload_roam_event {
  * @btm_rsp: BTM response related data
  * @roam_init_info: Roam initial related data
  * @roam_msg_info: Roam message related information
+ * @roam_event_param: Roam event notif params
  */
 struct roam_stats_event {
 	uint8_t vdev_id;
@@ -2138,6 +2166,7 @@ struct roam_stats_event {
 	struct roam_btm_response_data btm_rsp[MAX_ROAM_SCAN_STATS_TLV];
 	struct roam_initial_data roam_init_info[MAX_ROAM_SCAN_STATS_TLV];
 	struct roam_msg_info *roam_msg_info;
+	struct roam_event_rt_info roam_event_param;
 };
 
 /*

+ 68 - 0
components/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_roam_api.c

@@ -3031,6 +3031,70 @@ cm_roam_stats_print_11kv_info(struct wmi_neighbor_report_data *neigh_rpt,
 	qdf_mem_free(buf);
 }
 
+void cm_report_roam_rt_stats(struct wlan_objmgr_psoc *psoc,
+			     uint8_t vdev_id,
+			     enum roam_rt_stats_type events,
+			     struct roam_stats_event *roam_info,
+			     uint32_t value, uint8_t idx)
+{
+	struct roam_stats_event *roam_event = NULL;
+
+	if (!wlan_cm_get_roam_rt_stats(psoc, ROAM_RT_STATS_ENABLE)) {
+		mlme_err("Roam events stats is disabled");
+		return;
+	}
+
+	switch (events) {
+	case ROAM_RT_STATS_TYPE_SCAN_STATE:
+		roam_event = qdf_mem_malloc(sizeof(*roam_event));
+		if (!roam_event)
+			return;
+
+		if (value == WMI_ROAM_NOTIF_SCAN_START)
+			roam_event->roam_event_param.roam_scan_state =
+					QCA_WLAN_VENDOR_ROAM_SCAN_STATE_START;
+		else if (value == WMI_ROAM_NOTIF_SCAN_END)
+			roam_event->roam_event_param.roam_scan_state =
+					QCA_WLAN_VENDOR_ROAM_SCAN_STATE_END;
+
+		//TO DO: Add a new CB in CM and register the hdd function to it
+		//And call the new CB from here.
+		mlme_debug("Invoke HDD roam events callback for roam "
+			   "scan notif");
+		roam_event->vdev_id = vdev_id;
+		qdf_mem_free(roam_event);
+		break;
+	case ROAM_RT_STATS_TYPE_INVOKE_FAIL_REASON:
+		roam_event = qdf_mem_malloc(sizeof(*roam_event));
+		if (!roam_event)
+			return;
+
+		roam_event->roam_event_param.roam_invoke_fail_reason = value;
+
+		//TO DO: Add a new CB in CM and register the hdd function to it
+		//And call the new CB from here.
+		mlme_debug("Invoke HDD roam events callback for roam "
+			   "invoke fail");
+		roam_event->vdev_id = vdev_id;
+		qdf_mem_free(roam_event);
+		break;
+	case ROAM_RT_STATS_TYPE_ROAM_SCAN_INFO:
+		if (roam_info->scan[idx].present ||
+		    roam_info->trigger[idx].present ||
+		    (roam_info->result[idx].present &&
+		     roam_info->result[idx].fail_reason)) {
+			mlme_debug("Invoke HDD roam events callback for roam "
+				   "stats event");
+			roam_info->vdev_id = vdev_id;
+		//TO DO: Add a new CB in CM and register the hdd function to it
+		//And call the new CB from here.
+		}
+		break;
+	default:
+		break;
+	}
+}
+
 QDF_STATUS
 cm_roam_stats_event_handler(struct wlan_objmgr_psoc *psoc,
 			    struct roam_stats_event *stats_info)
@@ -3111,6 +3175,10 @@ cm_roam_stats_event_handler(struct wlan_objmgr_psoc *psoc,
 						      &stats_info->data_11kv[i],
 						      stats_info->vdev_id);
 		}
+
+		cm_report_roam_rt_stats(psoc, stats_info->vdev_id,
+					ROAM_RT_STATS_TYPE_ROAM_SCAN_INFO,
+					stats_info, 0, i);
 	}
 
 	if (!stats_info->num_tlv) {

+ 3 - 0
components/wmi/src/wmi_unified_roam_tlv.c

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2013-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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 above
@@ -2557,6 +2558,8 @@ wmi_convert_fw_notif_to_cm_notif(uint32_t fw_notif)
 		return CM_ROAM_NOTIF_DEAUTH_RECV;
 	case WMI_ROAM_NOTIF_DISASSOC_RECV:
 		return CM_ROAM_NOTIF_DISASSOC_RECV;
+	case WMI_ROAM_NOTIF_SCAN_END:
+		return CM_ROAM_NOTIF_SCAN_END;
 	default:
 		return CM_ROAM_NOTIF_INVALID;
 	}

+ 4 - 0
core/hdd/src/wlan_hdd_main.c

@@ -15376,6 +15376,9 @@ int hdd_register_cb(struct hdd_context *hdd_ctx)
 	sme_stats_ext2_register_callback(mac_handle,
 					wlan_hdd_cfg80211_stats_ext2_callback);
 
+	sme_roam_events_register_callback(mac_handle,
+					wlan_hdd_cfg80211_roam_events_callback);
+
 	sme_set_rssi_threshold_breached_cb(mac_handle,
 					   hdd_rssi_threshold_breached);
 
@@ -15486,6 +15489,7 @@ void hdd_deregister_cb(struct hdd_context *hdd_ctx)
 		hdd_err("Failed to de-register data stall detect event callback");
 	hdd_thermal_unregister_callbacks(hdd_ctx);
 	sme_deregister_oem_data_rsp_callback(mac_handle);
+	sme_roam_events_deregister_callback(mac_handle);
 
 	hdd_exit();
 }

+ 347 - 0
core/hdd/src/wlan_hdd_stats.c

@@ -3880,6 +3880,353 @@ wlan_hdd_cfg80211_stats_ext2_callback(hdd_handle_t hdd_handle,
 }
 #endif /* End of WLAN_FEATURE_STATS_EXT */
 
+#ifdef WLAN_FEATURE_ROAM_OFFLOAD
+/**
+ * enum roam_event_rt_info_reset - Reset the notif param value of struct
+ * roam_event_rt_info to 0
+ * @ROAM_EVENT_RT_INFO_RESET: Reset the value to 0
+ */
+enum roam_event_rt_info_reset {
+	ROAM_EVENT_RT_INFO_RESET = 0,
+};
+
+/**
+ * struct roam_ap - Roamed/Failed AP info
+ * @num_cand: number of candidate APs
+ * @bssid:    BSSID of roamed/failed AP
+ * rssi:      RSSI of roamed/failed AP
+ * freq:      Frequency of roamed/failed AP
+ */
+struct roam_ap {
+	uint32_t num_cand;
+	struct qdf_mac_addr bssid;
+	int8_t rssi;
+	uint16_t freq;
+};
+
+/**
+ * hdd_get_roam_rt_stats_event_len() - calculate length of skb required for
+ * sending roam events stats.
+ * @roam_stats: pointer to roam_stats_event structure
+ * @idx:          TLV index of roam stats event
+ *
+ * Return: length of skb
+ */
+static uint32_t
+hdd_get_roam_rt_stats_event_len(struct roam_stats_event *roam_stats,
+				uint8_t idx)
+{
+	uint32_t len = 0;
+	uint8_t i = 0, num_cand = 0;
+
+	/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TRIGGER_REASON  */
+	if (roam_stats->trigger[idx].present)
+		len += nla_total_size(sizeof(uint32_t));
+
+	/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_INVOKE_FAIL_REASON */
+	if (roam_stats->roam_event_param.roam_invoke_fail_reason)
+		len += nla_total_size(sizeof(uint32_t));
+
+	/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_STATE */
+	if (roam_stats->roam_event_param.roam_scan_state)
+		len += nla_total_size(sizeof(uint8_t));
+
+	if (roam_stats->scan[idx].present) {
+		if (roam_stats->scan[idx].num_chan &&
+		    !roam_stats->scan[idx].type)
+			for (i = 0; i < roam_stats->scan[idx].num_chan;)
+				i++;
+
+		/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_FREQ_LIST */
+		len += (nla_total_size(sizeof(uint32_t)) * i);
+
+		if (roam_stats->result[idx].present &&
+		    roam_stats->result[idx].fail_reason) {
+			num_cand++;
+		} else if (roam_stats->trigger[idx].present) {
+			for (i = 0; i < roam_stats->scan[idx].num_ap; i++) {
+				if (roam_stats->scan[idx].ap[i].type == 2)
+					num_cand++;
+			}
+		}
+		/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO */
+		len += NLA_HDRLEN;
+		/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_BSSID */
+		len += (nla_total_size(QDF_MAC_ADDR_SIZE) * num_cand);
+		/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_RSSI */
+		len += (nla_total_size(sizeof(int32_t)) * num_cand);
+		/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FREQ */
+		len += (nla_total_size(sizeof(uint32_t)) * num_cand);
+		/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FAIL_REASON */
+		len += (nla_total_size(sizeof(uint32_t)) * num_cand);
+	}
+
+	/* QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TYPE */
+	if (len)
+		len += nla_total_size(sizeof(uint32_t));
+
+	return len;
+}
+
+#define SUBCMD_ROAM_EVENTS_INDEX \
+	QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS_INDEX
+#define ROAM_SCAN_FREQ_LIST \
+	QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_FREQ_LIST
+#define ROAM_INVOKE_FAIL_REASON \
+	QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_INVOKE_FAIL_REASON
+#define ROAM_SCAN_STATE         QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_STATE
+#define ROAM_EVENTS_CANDIDATE   QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO
+#define CANDIDATE_BSSID \
+	QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_BSSID
+#define CANDIDATE_RSSI \
+	QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_RSSI
+#define CANDIDATE_FREQ \
+	QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FREQ
+#define ROAM_FAIL_REASON \
+	QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FAIL_REASON
+
+/**
+ * roam_rt_stats_fill_scan_freq() - Fill the scan frequency list from the
+ * roam stats event.
+ * @vendor_event: pointer to sk_buff structure
+ * @idx:          TLV index of roam stats event
+ * @roam_stats:   pointer to roam_stats_event structure
+ *
+ * Return: none
+ */
+static void
+roam_rt_stats_fill_scan_freq(struct sk_buff *vendor_event, uint8_t idx,
+			     struct roam_stats_event *roam_stats)
+{
+	struct nlattr *nl_attr;
+	uint8_t i;
+
+	nl_attr = nla_nest_start(vendor_event, ROAM_SCAN_FREQ_LIST);
+	if (!nl_attr) {
+		hdd_err("nla nest start fail");
+		kfree_skb(vendor_event);
+		return;
+	}
+	if (roam_stats->scan[idx].num_chan && !roam_stats->scan[idx].type) {
+		for (i = 0; i < roam_stats->scan[idx].num_chan; i++) {
+			if (nla_put_u32(vendor_event, i,
+					roam_stats->scan[idx].chan_freq[i])) {
+				hdd_err("failed to put freq at index %d", i);
+				kfree_skb(vendor_event);
+				return;
+			}
+		}
+	}
+	nla_nest_end(vendor_event, nl_attr);
+}
+
+/**
+ * roam_rt_stats_fill_cand_info() - Fill the roamed/failed AP info from the
+ * roam stats event.
+ * @vendor_event: pointer to sk_buff structure
+ * @idx:          TLV index of roam stats event
+ * @roam_stats:   pointer to roam_stats_event structure
+ *
+ * Return: none
+ */
+static void
+roam_rt_stats_fill_cand_info(struct sk_buff *vendor_event, uint8_t idx,
+			     struct roam_stats_event *roam_stats)
+{
+	struct nlattr *nl_attr, *nl_array;
+	struct roam_ap cand_ap = {0};
+	uint8_t i, num_cand = 0;
+
+	if (roam_stats->result[idx].present &&
+	    roam_stats->result[idx].fail_reason) {
+		num_cand++;
+		for (i = 0; i < roam_stats->scan[idx].num_ap; i++) {
+			if (roam_stats->scan[idx].ap[i].type == 0 &&
+			    qdf_is_macaddr_equal(&roam_stats->
+						 result[idx].fail_bssid,
+						 &roam_stats->
+						 scan[idx].ap[i].bssid)) {
+				qdf_copy_macaddr(&cand_ap.bssid,
+						 &roam_stats->
+						 scan[idx].ap[i].bssid);
+				cand_ap.rssi = roam_stats->scan[idx].ap[i].rssi;
+				cand_ap.freq = roam_stats->scan[idx].ap[i].freq;
+			}
+		}
+	} else if (roam_stats->trigger[idx].present) {
+		for (i = 0; i < roam_stats->scan[idx].num_ap; i++) {
+			if (roam_stats->scan[idx].ap[i].type == 2) {
+				num_cand++;
+				qdf_copy_macaddr(&cand_ap.bssid,
+						 &roam_stats->
+						 scan[idx].ap[i].bssid);
+				cand_ap.rssi = roam_stats->scan[idx].ap[i].rssi;
+				cand_ap.freq = roam_stats->scan[idx].ap[i].freq;
+			}
+		}
+	}
+
+	nl_array = nla_nest_start(vendor_event, ROAM_EVENTS_CANDIDATE);
+	if (!nl_array) {
+		hdd_err("nl array nest start fail");
+		kfree_skb(vendor_event);
+		return;
+	}
+	for (i = 0; i < num_cand; i++) {
+		nl_attr = nla_nest_start(vendor_event, i);
+		if (!nl_attr) {
+			hdd_err("nl attr nest start fail");
+			kfree_skb(vendor_event);
+			return;
+		}
+		if (nla_put(vendor_event, CANDIDATE_BSSID,
+			    sizeof(cand_ap.bssid), cand_ap.bssid.bytes)) {
+			hdd_err("%s put fail",
+				"ROAM_EVENTS_CANDIDATE_INFO_BSSID");
+			kfree_skb(vendor_event);
+			return;
+		}
+		if (nla_put_s32(vendor_event, CANDIDATE_RSSI, cand_ap.rssi)) {
+			hdd_err("%s put fail",
+				"ROAM_EVENTS_CANDIDATE_INFO_RSSI");
+			kfree_skb(vendor_event);
+			return;
+		}
+		if (nla_put_u32(vendor_event, CANDIDATE_FREQ, cand_ap.freq)) {
+			hdd_err("%s put fail",
+				"ROAM_EVENTS_CANDIDATE_INFO_FREQ");
+			kfree_skb(vendor_event);
+			return;
+		}
+		if (roam_stats->result[idx].present &&
+		    roam_stats->result[idx].fail_reason) {
+			if (nla_put_u32(vendor_event, ROAM_FAIL_REASON,
+					roam_stats->result[idx].fail_reason)) {
+				hdd_err("%s put fail",
+					"ROAM_EVENTS_CANDIDATE_FAIL_REASON");
+				kfree_skb(vendor_event);
+				return;
+			}
+		}
+		nla_nest_end(vendor_event, nl_attr);
+	}
+	nla_nest_end(vendor_event, nl_array);
+}
+
+void
+wlan_hdd_cfg80211_roam_events_callback(hdd_handle_t hdd_handle, uint8_t idx,
+				       struct roam_stats_event *roam_stats)
+{
+	struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle);
+	int status;
+	uint32_t data_size, roam_event_type = 0;
+	struct sk_buff *vendor_event;
+	struct hdd_adapter *adapter;
+
+	status = wlan_hdd_validate_context(hdd_ctx);
+	if (status) {
+		hdd_err("Invalid hdd_ctx");
+		return;
+	}
+
+	if (!roam_stats) {
+		hdd_err("msg received here is null");
+		return;
+	}
+
+	adapter = hdd_get_adapter_by_vdev(hdd_ctx,
+					  roam_stats->vdev_id);
+	if (!adapter) {
+		hdd_err("vdev_id %d does not exist with host",
+			roam_stats->vdev_id);
+		return;
+	}
+
+	data_size = hdd_get_roam_rt_stats_event_len(roam_stats, idx);
+	if (!data_size) {
+		hdd_err("No data requested");
+		return;
+	}
+
+	data_size += NLMSG_HDRLEN;
+	vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
+						   &adapter->wdev,
+						   data_size,
+						   SUBCMD_ROAM_EVENTS_INDEX,
+						   GFP_KERNEL);
+
+	if (!vendor_event) {
+		hdd_err("vendor_event_alloc failed for ROAM_EVENTS_STATS");
+		return;
+	}
+
+	if (roam_stats->scan[idx].present && roam_stats->trigger[idx].present) {
+		roam_rt_stats_fill_scan_freq(vendor_event, idx, roam_stats);
+		roam_rt_stats_fill_cand_info(vendor_event, idx, roam_stats);
+	}
+
+	if (roam_stats->roam_event_param.roam_scan_state) {
+		roam_event_type |= QCA_WLAN_VENDOR_ROAM_EVENT_ROAM_SCAN_STATE;
+		if (nla_put_u8(vendor_event, ROAM_SCAN_STATE,
+			       roam_stats->roam_event_param.roam_scan_state)) {
+			hdd_err("%s put fail",
+				"VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_STATE");
+			kfree_skb(vendor_event);
+			return;
+		}
+		roam_stats->roam_event_param.roam_scan_state =
+						ROAM_EVENT_RT_INFO_RESET;
+	}
+	if (roam_stats->trigger[idx].present) {
+		roam_event_type |= QCA_WLAN_VENDOR_ROAM_EVENT_TRIGGER_REASON;
+		if (nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TRIGGER_REASON,
+				roam_stats->trigger[idx].trigger_reason)) {
+			hdd_err("%s put fail",
+				"VENDOR_ATTR_ROAM_EVENTS_TRIGGER_REASON");
+			kfree_skb(vendor_event);
+			return;
+		}
+	}
+	if (roam_stats->roam_event_param.roam_invoke_fail_reason) {
+		roam_event_type |=
+			QCA_WLAN_VENDOR_ROAM_EVENT_INVOKE_FAIL_REASON;
+		if (nla_put_u32(vendor_event, ROAM_INVOKE_FAIL_REASON,
+				roam_stats->
+				roam_event_param.roam_invoke_fail_reason)) {
+			hdd_err("%s put fail",
+				"VENDOR_ATTR_ROAM_EVENTS_INVOKE_FAIL_REASON");
+			kfree_skb(vendor_event);
+			return;
+		}
+		roam_stats->roam_event_param.roam_invoke_fail_reason =
+						ROAM_EVENT_RT_INFO_RESET;
+	}
+	if (roam_stats->result[idx].present &&
+	    roam_stats->result[idx].fail_reason)
+		roam_event_type |= QCA_WLAN_VENDOR_ROAM_EVENT_FAIL_REASON;
+
+	if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TYPE,
+			roam_event_type)) {
+		hdd_err("%s put fail", "QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TYPE");
+			kfree_skb(vendor_event);
+		return;
+	}
+
+	cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+}
+
+#undef SUBCMD_ROAM_EVENTS_INDEX
+#undef ROAM_SCAN_FREQ_LIST
+#undef ROAM_INVOKE_FAIL_REASON
+#undef ROAM_SCAN_STATE
+#undef ROAM_EVENTS_CANDIDATE
+#undef CANDIDATE_BSSID
+#undef CANDIDATE_RSSI
+#undef CANDIDATE_FREQ
+#undef ROAM_FAIL_REASON
+#endif /* End of WLAN_FEATURE_ROAM_OFFLOAD */
+
 #ifdef LINKSPEED_DEBUG_ENABLED
 #define linkspeed_dbg(format, args...) pr_info(format, ## args)
 #else

+ 21 - 0
core/hdd/src/wlan_hdd_stats.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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
@@ -393,6 +394,26 @@ void
 wlan_hdd_cfg80211_stats_ext2_callback(hdd_handle_t hdd_handle,
 				      struct sir_sme_rx_aggr_hole_ind *pmsg);
 
+#ifdef WLAN_FEATURE_ROAM_OFFLOAD
+/**
+ * wlan_hdd_cfg80211_roam_events_callback() - roam_events_callback
+ * @hdd_handle: opaque handle to the hdd context
+ * @idx: TLV index in roam stats event
+ * @roam_stats: roam events stats
+ *
+ * Return: void
+ */
+void
+wlan_hdd_cfg80211_roam_events_callback(hdd_handle_t hdd_handle, uint8_t idx,
+				       struct roam_stats_event *roam_stats);
+#else
+static inline void
+wlan_hdd_cfg80211_roam_events_callback(hdd_handle_t hdd_handle, uint8_t idx,
+				       struct roam_stats_event *roam_stats)
+{
+}
+#endif /* End of WLAN_FEATURE_ROAM_OFFLOAD */
+
 /**
  * wlan_hdd_get_rcpi() - Wrapper to get current RCPI
  * @adapter: adapter upon which the measurement is requested

+ 36 - 0
core/sme/inc/sme_api.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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
@@ -632,6 +633,30 @@ QDF_STATUS sme_roam_set_psk_pmk(mac_handle_t mac_handle,
  */
 QDF_STATUS sme_set_pmk_cache_ft(mac_handle_t mac_handle, uint8_t vdev_id,
 				struct wlan_crypto_pmksa *pmk_cache);
+
+/**
+ * sme_roam_events_register_callback() - Register roam events callback
+ * @mac_handle: Opaque handle to the MAC context
+ * @roam_rt_stats_cb: Function to be invoked for roam events stats
+ *
+ * This function will register a callback for roams events stats.
+ *
+ * Return: void
+ */
+void sme_roam_events_register_callback(mac_handle_t mac_handle,
+				       void (*roam_rt_stats_cb)(
+				hdd_handle_t hdd_handle, uint8_t idx,
+				struct roam_stats_event *roam_stats));
+
+/**
+ * sme_roam_events_deregister_callback() - DeRegister roam events callback
+ * @mac_handle: Opaque handle to the MAC context
+ *
+ * This function will deregister the callback of roams events stats.
+ *
+ * Return: void
+ */
+void sme_roam_events_deregister_callback(mac_handle_t mac_handle);
 #else
 static inline
 void sme_get_pmk_info(mac_handle_t mac_handle, uint8_t session_id,
@@ -666,6 +691,17 @@ QDF_STATUS sme_set_pmk_cache_ft(mac_handle_t mac_handle, uint8_t vdev_id,
 {
 	return QDF_STATUS_SUCCESS;
 }
+
+static inline void
+sme_roam_events_register_callback(mac_handle_t mac_handle,
+				  void (*roam_rt_stats_cb)(
+				hdd_handle_t hdd_handle, uint8_t idx,
+				struct roam_stats_event *roam_stats))
+{}
+
+static inline
+void sme_roam_events_deregister_callback(mac_handle_t mac_handle)
+{}
 #endif
 
 QDF_STATUS sme_get_config_param(mac_handle_t mac_handle,

+ 5 - 0
core/sme/inc/sme_internal.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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
@@ -511,6 +512,10 @@ struct sme_context {
 #if defined(CLD_PM_QOS) && defined(WLAN_FEATURE_LL_MODE)
 	void (*beacon_latency_event_cb)(uint32_t latency_level);
 #endif
+#ifdef WLAN_FEATURE_ROAM_OFFLOAD
+	void (*roam_rt_stats_cb)(hdd_handle_t hdd_handle, uint8_t idx,
+				 struct roam_stats_event *roam_stats);
+#endif
 };
 
 #endif /* #if !defined( __SMEINTERNAL_H ) */

+ 23 - 0
core/sme/src/common/sme_api.c

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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
@@ -16016,3 +16017,25 @@ QDF_STATUS sme_switch_channel(mac_handle_t mac_handle,
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef WLAN_FEATURE_ROAM_OFFLOAD
+void sme_roam_events_register_callback(mac_handle_t mac_handle,
+				       void (*roam_rt_stats_cb)(
+				hdd_handle_t hdd_handle, uint8_t idx,
+				struct roam_stats_event *roam_stats))
+{
+	struct mac_context *mac = MAC_CONTEXT(mac_handle);
+
+	if (!mac) {
+		sme_err("Invalid mac context");
+		return;
+	}
+
+	mac->sme.roam_rt_stats_cb = roam_rt_stats_cb;
+}
+
+void sme_roam_events_deregister_callback(mac_handle_t mac_handle)
+{
+	sme_roam_events_register_callback(mac_handle, NULL);
+}
+#endif
+

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

@@ -1,5 +1,6 @@
  /*
  * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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
@@ -2584,6 +2585,12 @@ void cm_invalid_roam_reason_handler(uint32_t vdev_id, enum cm_roam_notif notif)
 		return;
 	}
 	wma_invalid_roam_reason_handler(wma_handle, vdev_id, notif);
+
+	if (notif == CM_ROAM_NOTIF_SCAN_START ||
+	    notif == CM_ROAM_NOTIF_SCAN_END)
+		cm_report_roam_rt_stats(wma_handle->psoc, vdev_id,
+					ROAM_RT_STATS_TYPE_SCAN_STATE,
+					NULL, notif, 0);
 }
 #endif
 
@@ -2745,6 +2752,9 @@ cm_handle_roam_reason_invoke_roam_fail(uint8_t vdev_id,	uint32_t notif_params,
 	wma_handle_hw_mode_trans_ind(wma_handle, trans_ind);
 	wma_handle_roam_reason_invoke_roam_fail(wma_handle, vdev_id,
 						notif_params);
+	cm_report_roam_rt_stats(wma_handle->psoc, vdev_id,
+				ROAM_RT_STATS_TYPE_INVOKE_FAIL_REASON,
+				NULL, notif_params, 0);
 }
 
 static void