Browse Source

qcacld-3.0: Extend WMI_ROAM_STATS_EVENTID for enhanced roam

Extend WMI_ROAM_STATS_EVENTID event information for
requirement of:
1.Roam trigger related information, include timestamp,
  trigger reason, trigger condition, abort reason,
  scan type, roam status and fail reason.
2.Roam scan related information, include scan channel
  number, dwell type, max dwell time of each channel
  and total scan time.
3.all types of roam related frame information, include
  timestamp and successful or failed status for Preauth,
  Reassoc, EAPOL-M1/M2/M3/M4.
Cache roam information in wlan driver, when qca vendor
cmd of QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS get roam
information, driver send all the cached information to
user space.

change-Id: Ic5e1120b51c20c103100833b4296002c5939b021
CRs-Fixed: 3389269
Jianmin Zhu 2 years ago
parent
commit
f6035ce635

+ 3 - 2
components/mlme/core/inc/wlan_mlme_main.h

@@ -640,7 +640,7 @@ struct eroam_trigger_info {
 	struct roam_trigger_abort_reason abort;
 	enum roam_stats_scan_type roam_scan_type;
 	uint8_t roam_status;
-	uint32_t roam_fail_reason;
+	enum wlan_roam_failure_reason_code roam_fail_reason;
 };
 
 /**
@@ -663,7 +663,7 @@ struct roam_scan_chn {
  */
 struct eroam_scan_info {
 	uint8_t num_channels;
-	struct roam_scan_chn *roam_chn;
+	struct roam_scan_chn roam_chn[MAX_ROAM_SCAN_CHAN];
 	uint32_t total_scan_time;
 };
 
@@ -681,6 +681,7 @@ struct eroam_frame_info {
 	uint64_t timestamp;
 };
 
+/* Key frame num during roaming: PREAUTH/PREASSOC/EAPOL M1-M4 */
 #define ROAM_FRAME_NUM 6
 
 /**

+ 2 - 0
components/mlme/core/src/wlan_mlme_vdev_mgr_interface.c

@@ -1377,6 +1377,7 @@ static void mlme_ext_handler_destroy(struct vdev_mlme_obj *vdev_mlme)
 	mlme_free_sae_auth_retry(vdev_mlme->vdev);
 	mlme_deinit_wait_for_key_timer(&vdev_mlme->ext_vdev_ptr->wait_key_timer);
 	mlme_free_fils_info(&vdev_mlme->ext_vdev_ptr->connect_info);
+	mlme_cm_free_roam_stats_info(vdev_mlme->ext_vdev_ptr);
 	qdf_mem_free(vdev_mlme->ext_vdev_ptr);
 	vdev_mlme->ext_vdev_ptr = NULL;
 }
@@ -1399,6 +1400,7 @@ QDF_STATUS vdevmgr_mlme_ext_hdl_create(struct vdev_mlme_obj *vdev_mlme)
 		return QDF_STATUS_E_NOMEM;
 
 	mlme_init_rate_config(vdev_mlme);
+	mlme_cm_alloc_roam_stats_info(vdev_mlme);
 	vdev_mlme->ext_vdev_ptr->connect_info.fils_con_info = NULL;
 	mlme_init_wait_for_key_timer(vdev_mlme->vdev,
 				     &vdev_mlme->ext_vdev_ptr->wait_key_timer);

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

@@ -1218,6 +1218,50 @@ wlan_cm_roam_set_full_scan_6ghz_on_disc(struct wlan_objmgr_psoc *psoc,
  */
 uint8_t wlan_cm_roam_get_full_scan_6ghz_on_disc(struct wlan_objmgr_psoc *psoc);
 
+#ifdef WLAN_FEATURE_ROAM_INFO_STATS
+/**
+ * mlme_cm_alloc_roam_stats_info() - alloc roam stats info buffer
+ * @vdev_mlme: MLME-private vdev context
+ *
+ * Return: None
+ */
+void mlme_cm_alloc_roam_stats_info(struct vdev_mlme_obj *vdev_mlme);
+
+/**
+ * mlme_cm_free_roam_stats_info() - free roam stats info buffer in
+ * struct mlme_legacy_priv
+ * @ext_hdl: mlme_legacy_priv pointer
+ *
+ * Return: None
+ */
+void mlme_cm_free_roam_stats_info(mlme_vdev_ext_t *ext_hdl);
+
+/**
+ * wlan_cm_roam_stats_info_get() - get vdev roam stats info
+ *
+ * @vdev: pointer to vdev
+ * @roam_info: pointer to buffer to copy roam stats info
+ * @roam_num: pointer to valid roam stats num
+ *
+ * Return: QDF_STATUS
+ */
+
+QDF_STATUS
+wlan_cm_roam_stats_info_get(struct wlan_objmgr_vdev *vdev,
+			    struct enhance_roam_info **roam_info,
+			    uint32_t  *roam_num);
+#else
+static inline
+void mlme_cm_alloc_roam_stats_info(struct vdev_mlme_obj *vdev_mlme)
+{
+}
+
+static inline
+void mlme_cm_free_roam_stats_info(mlme_vdev_ext_t *ext_hdl)
+{
+}
+#endif
+
 #else
 static inline
 void wlan_cm_roam_activate_pcl_per_vdev(struct wlan_objmgr_psoc *psoc,
@@ -1436,6 +1480,16 @@ wlan_cm_roam_get_full_scan_6ghz_on_disc(struct wlan_objmgr_psoc *psoc)
 	return 0;
 }
 
+static inline
+void mlme_cm_alloc_roam_stats_info(struct vdev_mlme_obj *vdev_mlme)
+{
+}
+
+static inline
+void mlme_cm_free_roam_stats_info(mlme_vdev_ext_t *ext_hdl)
+{
+}
+
 #endif /* WLAN_FEATURE_ROAM_OFFLOAD */
 
 #ifdef WLAN_FEATURE_FIPS

+ 47 - 0
components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_ucfg_api.h

@@ -676,4 +676,51 @@ QDF_STATUS
 ucfg_cm_get_empty_scan_refresh_period_global(struct wlan_objmgr_psoc *psoc,
 					     uint16_t *roam_scan_period_global);
 
+#if defined(WLAN_FEATURE_ROAM_OFFLOAD) && defined(WLAN_FEATURE_ROAM_INFO_STATS)
+/**
+ * ucfg_cm_roam_stats_info_get() - get vdev roam stats info
+ *
+ * @vdev: pointer to vdev
+ * @roam_info: pointer to buffer to copy roam stats info
+ * @roam_num: pointer to valid roam stats num
+ *
+ * After use, roam_info must be released by using
+ * ucfg_cm_roam_stats_info_put()
+ *
+ * Return: QDF_STATUS
+ */
+static inline QDF_STATUS
+ucfg_cm_roam_stats_info_get(struct wlan_objmgr_vdev *vdev,
+			    struct enhance_roam_info **roam_info,
+			    uint32_t *roam_num)
+{
+	return wlan_cm_roam_stats_info_get(vdev, roam_info, roam_num);
+}
+
+/**
+ * ucfg_cm_roam_stats_info_put() - put vdev roam stats info
+ *
+ * @roam_info: pointer to buffer of roam stats info
+ *
+ * Return: QDF_STATUS
+ */
+static inline void
+ucfg_cm_roam_stats_info_put(struct enhance_roam_info *roam_info)
+{
+	qdf_mem_free(roam_info);
+}
+#else
+static inline QDF_STATUS
+ucfg_cm_roam_stats_info_get(struct wlan_objmgr_vdev *vdev,
+			    struct enhance_roam_info **roam_info,
+			    uint32_t *roam_num)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline void
+ucfg_cm_roam_stats_info_put(struct enhance_roam_info *roam_info)
+{
+}
+#endif
 #endif /* _WLAN_CM_ROAM_UCFG_API_H_ */

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

@@ -3405,6 +3405,617 @@ log_btm_frames_only:
 				stats_info, 0, i);
 }
 
+#ifdef WLAN_FEATURE_ROAM_INFO_STATS
+void mlme_cm_free_roam_stats_info(mlme_vdev_ext_t *ext_hdl)
+{
+	uint32_t roam_cache_num;
+
+	roam_cache_num = ext_hdl->roam_cache_num;
+
+	if (roam_cache_num == 0) {
+		mlme_debug("enhanced roam disable, no need free memory");
+		return;
+	}
+
+	qdf_mutex_destroy(&ext_hdl->roam_rd_wr_lock);
+	qdf_mem_free(ext_hdl->roam_info);
+	ext_hdl->roam_info = NULL;
+}
+
+void
+mlme_cm_alloc_roam_stats_info(struct vdev_mlme_obj *vdev_mlme)
+{
+	uint32_t cache_num;
+	struct wlan_objmgr_psoc *psoc;
+	QDF_STATUS status;
+
+	if (!vdev_mlme->ext_vdev_ptr) {
+		mlme_err("vdev legacy private object is NULL");
+		return;
+	}
+
+	psoc = wlan_vdev_get_psoc(vdev_mlme->vdev);
+	if (!psoc) {
+		mlme_err("Invalid PSOC");
+		return;
+	}
+	status = wlan_mlme_get_roam_info_stats_num(psoc, &cache_num);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlme_err("failed to get groam_info_stats_num");
+		return;
+	}
+
+	if (cache_num == 0) {
+		mlme_err("groam_info_stats_num = 0, not support enhanced roam");
+		return;
+	}
+
+	if (vdev_mlme->ext_vdev_ptr->roam_info) {
+		mlme_debug("mlme_priv->enhance_roam_info already malloced");
+		return;
+	}
+
+	vdev_mlme->ext_vdev_ptr->roam_info =
+		qdf_mem_malloc(cache_num * sizeof(struct enhance_roam_info));
+	if (!vdev_mlme->ext_vdev_ptr->roam_info)
+		return;
+
+	vdev_mlme->ext_vdev_ptr->roam_cache_num = cache_num;
+	vdev_mlme->ext_vdev_ptr->roam_write_index = 0;
+	qdf_mutex_create(&vdev_mlme->ext_vdev_ptr->roam_rd_wr_lock);
+}
+
+/**
+ * wlan_cm_update_roam_trigger_info() - API to update roam trigger info
+ * @mlme_priv:    Pointer to vdev mlme legacy priv struct
+ * @data:  Data from target_if wmi event
+ *
+ * Get roam trigger info from target_if wmi event and save in vdev mlme ring
+ * buffer, it is required that the mlme_priv->roam_rd_wr_lock be held before
+ * this API is called
+ *
+ * Return: void
+ */
+static void
+wlan_cm_update_roam_trigger_info(struct mlme_legacy_priv *mlme_priv,
+				 struct wmi_roam_trigger_info *data)
+{
+	uint32_t trigger_reason;
+	uint32_t index;
+	struct enhance_roam_info *info;
+
+	if (!mlme_priv->roam_info) {
+		mlme_err("enhance_roam_info buffer is NULL");
+		return;
+	}
+
+	index = mlme_priv->roam_write_index;
+	info = &mlme_priv->roam_info[index];
+
+	info->trigger.timestamp = data->timestamp;
+	info->trigger.trigger_reason = data->trigger_reason;
+	trigger_reason = data->trigger_reason;
+
+	mlme_debug("roam trigger info: timestamp:%llu, reason:%u,index:%u",
+		   info->trigger.timestamp,
+		   info->trigger.trigger_reason, index);
+
+	switch (trigger_reason) {
+	case ROAM_TRIGGER_REASON_PER:
+		info->trigger.condition.roam_per.tx_rate_thresh_percent =
+			data->per_trig_data.tx_rate_thresh_percent;
+		info->trigger.condition.roam_per.tx_rate_thresh_percent =
+			data->per_trig_data.tx_rate_thresh_percent;
+		mlme_debug("roam per: rx:%u, tx:%u",
+			   data->per_trig_data.rx_rate_thresh_percent,
+			   data->per_trig_data.tx_rate_thresh_percent);
+		break;
+	case ROAM_TRIGGER_REASON_BMISS:
+		info->trigger.condition.roam_bmiss.final_bmiss_cnt =
+			data->bmiss_trig_data.final_bmiss_cnt;
+		info->trigger.condition.roam_bmiss.consecutive_bmiss_cnt =
+			data->bmiss_trig_data.consecutive_bmiss_cnt;
+		info->trigger.condition.roam_bmiss.qos_null_success =
+			data->bmiss_trig_data.qos_null_success;
+		mlme_debug("roam bmiss: final:%u, consecutive:%u, qos:%u",
+			   data->bmiss_trig_data.final_bmiss_cnt,
+			   data->bmiss_trig_data.consecutive_bmiss_cnt,
+			   data->bmiss_trig_data.qos_null_success);
+		break;
+	case ROAM_TRIGGER_REASON_LOW_RSSI:
+		info->trigger.condition.roam_poor_rssi.current_rssi =
+			data->low_rssi_trig_data.current_rssi;
+		info->trigger.condition.roam_poor_rssi.roam_rssi_threshold =
+			data->low_rssi_trig_data.roam_rssi_threshold;
+		info->trigger.condition.roam_poor_rssi.rx_linkspeed_status =
+			data->low_rssi_trig_data.rx_linkspeed_status;
+		mlme_debug("roam low rssi: rssi:%d, rssi_th:%d, rx_linkspeed:%u",
+			   data->low_rssi_trig_data.current_rssi,
+			   data->low_rssi_trig_data.roam_rssi_threshold,
+			   data->low_rssi_trig_data.rx_linkspeed_status);
+		break;
+	case ROAM_TRIGGER_REASON_HIGH_RSSI:
+		info->trigger.condition.roam_better_rssi.current_rssi =
+			data->hi_rssi_trig_data.current_rssi;
+		info->trigger.condition.roam_better_rssi.hi_rssi_threshold =
+			data->hi_rssi_trig_data.hirssi_threshold;
+		mlme_debug("roam better rssi: cu_rssi:%d, hi_rssi_th:%d",
+			   data->hi_rssi_trig_data.current_rssi,
+			   data->hi_rssi_trig_data.hirssi_threshold);
+		break;
+	case ROAM_TRIGGER_REASON_PERIODIC:
+		info->trigger.condition.roam_periodic.periodic_timer_ms =
+			data->periodic_trig_data.periodic_timer_ms;
+		mlme_debug("roam periodic: periodic_timer:%u",
+			   data->periodic_trig_data.periodic_timer_ms);
+		break;
+
+	case ROAM_TRIGGER_REASON_DENSE:
+		info->trigger.condition.roam_congestion.rx_tput =
+			data->congestion_trig_data.rx_tput;
+		info->trigger.condition.roam_congestion.tx_tput =
+			data->congestion_trig_data.tx_tput;
+		info->trigger.condition.roam_congestion.roamable_count =
+			data->congestion_trig_data.roamable_count;
+		mlme_debug("roam dense: rx_tput:%u, tx_tput:%u, roamable %u",
+			   data->congestion_trig_data.rx_tput,
+			   data->congestion_trig_data.tx_tput,
+			   data->congestion_trig_data.roamable_count);
+		break;
+	case ROAM_TRIGGER_REASON_BACKGROUND:
+		info->trigger.condition.roam_background.current_rssi =
+			data->background_trig_data.current_rssi;
+		info->trigger.condition.roam_background.data_rssi =
+			data->background_trig_data.data_rssi;
+		info->trigger.condition.roam_background.data_rssi_threshold =
+			data->background_trig_data.data_rssi_threshold;
+		mlme_debug("roam background: rssi:%d, datarssi:%d, rssi_th %d",
+			   data->background_trig_data.current_rssi,
+			   data->background_trig_data.data_rssi,
+			   data->background_trig_data.data_rssi_threshold);
+		break;
+	case ROAM_TRIGGER_REASON_FORCED:
+		info->trigger.condition.roam_user_trigger.invoke_reason =
+			data->user_trig_data.invoke_reason;
+		mlme_debug("roam force: invoke_reason:%u",
+			   data->user_trig_data.invoke_reason);
+		break;
+	case ROAM_TRIGGER_REASON_BTM:
+		info->trigger.condition.roam_btm.btm_request_mode =
+			data->btm_trig_data.btm_request_mode;
+		info->trigger.condition.roam_btm.disassoc_imminent_timer =
+			data->btm_trig_data.disassoc_timer;
+		info->trigger.condition.roam_btm.validity_internal =
+			data->btm_trig_data.validity_interval;
+		info->trigger.condition.roam_btm.candidate_list_count =
+			data->btm_trig_data.candidate_list_count;
+		info->trigger.condition.roam_btm.btm_response_status_code =
+			data->btm_trig_data.btm_resp_status;
+		info->trigger.condition.roam_btm.btm_bss_termination_timeout =
+			data->btm_trig_data.btm_bss_termination_timeout;
+		info->trigger.condition.roam_btm.btm_mbo_assoc_retry_timeout =
+			data->btm_trig_data.btm_mbo_assoc_retry_timeout;
+		info->trigger.condition.roam_btm.btm_req_dialog_token =
+			(uint8_t)data->btm_trig_data.token;
+		mlme_debug("roam btm: %u %u %u %u %u %u %u %u",
+			   data->btm_trig_data.btm_request_mode,
+			   data->btm_trig_data.disassoc_timer,
+			   data->btm_trig_data.validity_interval,
+			   data->btm_trig_data.candidate_list_count,
+			   data->btm_trig_data.btm_resp_status,
+			   data->btm_trig_data.btm_bss_termination_timeout,
+			   data->btm_trig_data.btm_mbo_assoc_retry_timeout,
+			   data->btm_trig_data.token);
+		break;
+	case ROAM_TRIGGER_REASON_BSS_LOAD:
+		info->trigger.condition.roam_bss_load.cu_load =
+			(uint8_t)data->cu_trig_data.cu_load;
+		mlme_debug("roam bss_load: cu_load:%u",
+			   data->cu_trig_data.cu_load);
+		break;
+	case ROAM_TRIGGER_REASON_DEAUTH:
+		info->trigger.condition.roam_disconnection.deauth_type =
+			(uint8_t)data->deauth_trig_data.type;
+		info->trigger.condition.roam_disconnection.deauth_reason =
+			(uint16_t)data->deauth_trig_data.reason;
+		mlme_debug("roam disconnection: type:%u reason:%u",
+			   data->deauth_trig_data.type,
+			   data->deauth_trig_data.reason);
+		break;
+	case ROAM_TRIGGER_REASON_STA_KICKOUT:
+		info->trigger.condition.roam_tx_failures.kickout_threshold =
+			data->tx_failures_trig_data.kickout_threshold;
+		info->trigger.condition.roam_tx_failures.kickout_reason =
+			data->tx_failures_trig_data.kickout_reason;
+		mlme_debug("roam tx_failures: kickout_th:%u kickout_reason:%u",
+			   data->tx_failures_trig_data.kickout_threshold,
+			   data->tx_failures_trig_data.kickout_reason);
+		break;
+	case ROAM_TRIGGER_REASON_IDLE:
+	case ROAM_TRIGGER_REASON_UNIT_TEST:
+	case ROAM_TRIGGER_REASON_MAWC:
+	default:
+		break;
+	}
+
+	info->trigger.roam_scan_type = data->scan_type;
+	info->trigger.roam_status = data->roam_status;
+	mlme_debug("roam trigger info: scan_type:%u,status:%u",
+		   data->scan_type, data->roam_status);
+
+	if (info->trigger.roam_status) {
+		info->trigger.roam_fail_reason = data->fail_reason;
+
+		if (data->abort_reason.abort_reason_code) {
+			info->trigger.abort.abort_reason_code =
+				data->abort_reason.abort_reason_code;
+			info->trigger.abort.data_rssi =
+				data->abort_reason.data_rssi;
+			info->trigger.abort.data_rssi_threshold =
+				data->abort_reason.data_rssi_threshold;
+			info->trigger.abort.rx_linkspeed_status =
+				data->abort_reason.rx_linkspeed_status;
+		}
+
+		mlme_debug("abort:reason:%u,rssi:%d,rssit:%d,status:%u,fail:%u",
+			   info->trigger.abort.abort_reason_code,
+			   info->trigger.abort.data_rssi,
+			   info->trigger.abort.data_rssi_threshold,
+			   info->trigger.abort.rx_linkspeed_status,
+			   info->trigger.roam_fail_reason);
+	}
+}
+
+static uint32_t
+wlan_cm_get_roam_chn_dwell_time(struct wlan_objmgr_vdev *vdev,
+				uint16_t freq,
+				enum roam_scan_dwell_type dwell_type)
+{
+	struct wlan_scan_obj *scan_obj;
+
+	scan_obj = wlan_vdev_get_scan_obj(vdev);
+	if (!scan_obj) {
+		mlme_err("scan_obj is NULL");
+		return 0;
+	}
+
+	if (WLAN_REG_IS_24GHZ_CH_FREQ(freq)) {
+		return scan_obj->scan_def.active_dwell_2g;
+	} else if (WLAN_REG_IS_5GHZ_CH_FREQ(freq)) {
+		if (dwell_type == WLAN_ROAM_DWELL_PASSIVE_TYPE)
+			return scan_obj->scan_def.passive_dwell;
+		else
+			return scan_obj->scan_def.active_dwell;
+	} else if (WLAN_REG_IS_6GHZ_CHAN_FREQ(freq)) {
+		if (dwell_type == WLAN_ROAM_DWELL_PASSIVE_TYPE)
+			return scan_obj->scan_def.passive_dwell_6g;
+		else
+			return scan_obj->scan_def.active_dwell_6g;
+	}
+
+	return 0;
+}
+
+/**
+ * wlan_cm_update_roam_scan_info() - API to update roam scan info
+ * @vdev:    Pointer to vdev
+ * @scan:  Scan data from target_if wmi event
+ *
+ * Get roam scan info from target_if wmi event and save in vdev mlme ring
+ * buffer, it is required that the mlme_priv->roam_rd_wr_lock be held before
+ * this API is called
+ *
+ * Return: void
+ */
+static void
+wlan_cm_update_roam_scan_info(struct wlan_objmgr_vdev *vdev,
+			      struct wmi_roam_scan_data *scan)
+{
+	struct mlme_legacy_priv *mlme_priv;
+	struct enhance_roam_info *info;
+	uint32_t freq;
+	uint32_t dwell_type;
+	uint32_t i, index;
+
+	mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev);
+	if (!mlme_priv) {
+		mlme_err("vdev legacy private object is NULL");
+		return;
+	}
+
+	if (!mlme_priv->roam_info) {
+		mlme_err("enhance_roam_info buffer is NULL");
+		return;
+	}
+
+	index = mlme_priv->roam_write_index;
+	info = &mlme_priv->roam_info[index];
+	info->scan.num_channels = scan->num_chan;
+
+	if (scan->num_chan) {
+		for (i = 0; i < scan->num_chan; i++) {
+			info->scan.roam_chn[i].chan_freq =
+				scan->chan_freq[i];
+			info->scan.roam_chn[i].dwell_type =
+				scan->dwell_type[i];
+
+			freq = scan->chan_freq[i];
+			dwell_type = scan->dwell_type[i];
+			info->scan.roam_chn[i].max_dwell_time =
+				wlan_cm_get_roam_chn_dwell_time(vdev, freq,
+								dwell_type);
+
+			mlme_debug("freq %u dwell_type %u dwell_time:%u",
+				   freq, dwell_type,
+				   info->scan.roam_chn[i].max_dwell_time);
+		}
+
+		info->scan.total_scan_time =
+			scan->scan_complete_timestamp -
+			info->trigger.timestamp;
+
+		mlme_debug("roam scan:chn_num:%u,com:%u,total_time:%u,index:%u",
+			   info->scan.num_channels,
+			   scan->scan_complete_timestamp,
+			   info->scan.total_scan_time, index);
+	}
+}
+
+/**
+ * wlan_cm_roam_frame_subtype() - Convert roam host enum
+ * @frame_type: roam frame type
+ *
+ * Return: Roam frame type defined in host driver
+ */
+static enum eroam_frame_subtype
+wlan_cm_roam_frame_subtype(uint8_t frame_type)
+{
+	switch (frame_type) {
+	case MGMT_SUBTYPE_AUTH:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH;
+	case MGMT_SUBTYPE_REASSOC_RESP:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC;
+	case ROAM_FRAME_SUBTYPE_M1:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1;
+	case ROAM_FRAME_SUBTYPE_M2:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2;
+	case ROAM_FRAME_SUBTYPE_M3:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3;
+	case ROAM_FRAME_SUBTYPE_M4:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4;
+	case ROAM_FRAME_SUBTYPE_GTK_M1:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1;
+	case ROAM_FRAME_SUBTYPE_GTK_M2:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2;
+	default:
+		return WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH;
+	}
+}
+
+static uint8_t
+wlan_cm_get_index_from_frame_type(struct roam_frame_info *frame)
+{
+	uint8_t index;
+
+	if (frame->subtype == MGMT_SUBTYPE_AUTH && frame->is_rsp) {
+		index = WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH - 1;
+	} else if (frame->subtype == MGMT_SUBTYPE_REASSOC_RESP) {
+		index = WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC - 1;
+	} else if (frame->subtype == ROAM_FRAME_SUBTYPE_M1) {
+		index = WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1 - 1;
+	} else if (frame->subtype == ROAM_FRAME_SUBTYPE_M2) {
+		index = WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2 - 1;
+	} else if (frame->subtype == ROAM_FRAME_SUBTYPE_M3) {
+		index = WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3 - 1;
+	} else if (frame->subtype == ROAM_FRAME_SUBTYPE_M4) {
+		index = WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4 - 1;
+	} else {
+		mlme_err("Invalid subtype: %d", frame->subtype);
+		index =  ROAM_FRAME_NUM;
+	}
+
+	return index;
+}
+
+/**
+ * wlan_cm_update_roam_frame_info() - API to update roam frame info
+ * @mlme_priv: Pointer to Pointer to vdev mlme legacy priv struct
+ * @frame_data: Frame data from target_if wmi event
+ *
+ * Get roam frame info from target_if wmi event and save in vdev mlme ring
+ * buffer, it is required that the mlme_priv->roam_rd_wr_lock be held before
+ * this API is called
+ *
+ * Return: void
+ */
+static void
+wlan_cm_update_roam_frame_info(struct mlme_legacy_priv *mlme_priv,
+			       struct roam_frame_stats *frame_data)
+{
+	struct enhance_roam_info *info;
+	uint32_t i, j, index;
+	uint8_t subtype;
+
+	if (!mlme_priv->roam_info) {
+		mlme_err("enhance_roam_info buffer is NULL");
+		return;
+	}
+
+	index = mlme_priv->roam_write_index;
+	info = &mlme_priv->roam_info[index];
+
+	for (i = 0; i < frame_data->num_frame; i++) {
+		j = wlan_cm_get_index_from_frame_type(&frame_data->frame_info[i]);
+
+		if (j == ROAM_FRAME_NUM)
+			continue;
+
+		/*
+		 * fill frame info into roam buffer from zero
+		 * only need preauth/reassoc/EAPOL-M1/M2/M3/M4
+		 * types of frame cache in driver.
+		 */
+		subtype = frame_data->frame_info[i].subtype;
+		info->timestamp[j].frame_type =
+			wlan_cm_roam_frame_subtype(subtype);
+		info->timestamp[j].timestamp =
+			frame_data->frame_info[i].timestamp;
+		info->timestamp[j].status =
+			frame_data->frame_info[i].status_code;
+
+		mlme_debug("frame:subtype %x time %llu status:%u, index:%u",
+			   info->timestamp[j].frame_type,
+			   info->timestamp[j].timestamp,
+			   info->timestamp[j].status, j);
+	}
+}
+
+/**
+ * wlan_cm_update_roam_stats_info() - API to update roam stats info
+ * @psoc:    Pointer to psoc
+ * @stats_info:  Roam stats event
+ * @index:  TLV index in roam stats event
+ *
+ * Get roam stats info from target_if wmi event and save in vdev mlme ring
+ * buffer.
+ *
+ * Return: void
+ */
+static void
+wlan_cm_update_roam_stats_info(struct wlan_objmgr_psoc *psoc,
+			       struct roam_stats_event *stats_info,
+			       uint8_t index)
+{
+	struct wlan_objmgr_vdev *vdev;
+	struct mlme_legacy_priv *mlme_priv;
+
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, stats_info->vdev_id,
+						    WLAN_MLME_SB_ID);
+	if (!vdev) {
+		mlme_err("vdev%d: vdev object is NULL", stats_info->vdev_id);
+		return;
+	}
+
+	mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev);
+	if (!mlme_priv) {
+		mlme_err("vdev legacy private object is NULL");
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID);
+		return;
+	}
+
+	if (mlme_priv->roam_cache_num == 0) {
+		mlme_debug("Enhanced roam stats not supported");
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID);
+		return;
+	}
+
+	qdf_mutex_acquire(&mlme_priv->roam_rd_wr_lock);
+
+	if (stats_info->trigger[index].present) {
+		wlan_cm_update_roam_trigger_info(mlme_priv,
+						 &stats_info->trigger[index]);
+		if (stats_info->scan[index].present)
+			wlan_cm_update_roam_scan_info(vdev,
+						      &stats_info->scan[index]);
+		if (stats_info->frame_stats[index].num_frame)
+			wlan_cm_update_roam_frame_info(mlme_priv,
+						       &stats_info->frame_stats[index]);
+
+		mlme_priv->roam_write_index += 1;
+		if (mlme_priv->roam_write_index == mlme_priv->roam_cache_num)
+			mlme_priv->roam_write_index = 0;
+	}
+	qdf_mutex_release(&mlme_priv->roam_rd_wr_lock);
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID);
+}
+
+QDF_STATUS
+wlan_cm_roam_stats_info_get(struct wlan_objmgr_vdev *vdev,
+			    struct enhance_roam_info **roam_info,
+			    uint32_t *roam_num)
+{
+	struct mlme_legacy_priv *mlme_priv;
+	uint32_t i, src, dst;
+	uint32_t index = 0;
+	uint32_t valid_cache_num;
+	struct enhance_roam_info *eroam_info;
+
+	mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev);
+	if (!mlme_priv) {
+		mlme_err("vdev legacy private object is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (mlme_priv->roam_cache_num == 0) {
+		mlme_debug("Enhanced roam stats not supported");
+		return QDF_STATUS_E_NOSUPPORT;
+	}
+
+	qdf_mutex_acquire(&mlme_priv->roam_rd_wr_lock);
+
+	/* get all roam event info from oldest to
+	 * latest by timestamp.
+	 */
+	valid_cache_num = mlme_priv->roam_cache_num;
+	for (i = 0; i < mlme_priv->roam_cache_num; i++) {
+		if (mlme_priv->roam_info[i].trigger.timestamp == 0) {
+			valid_cache_num--;
+			continue;
+		}
+		if (mlme_priv->roam_info[i].trigger.timestamp <
+		    mlme_priv->roam_info[index].trigger.timestamp)
+			index = i;
+	}
+	mlme_debug("valid num: %d, start index:%u", valid_cache_num, index);
+
+	if (valid_cache_num == 0) {
+		mlme_debug("Enhanced roam stats not existed");
+		qdf_mutex_release(&mlme_priv->roam_rd_wr_lock);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	/* Send all driver cached roam info to user space one time,
+	 * and don't flush them, since they will be cover by
+	 * new roam event info.
+	 */
+	eroam_info =
+		qdf_mem_malloc(valid_cache_num *
+			       sizeof(*eroam_info));
+	if (!eroam_info) {
+		qdf_mutex_release(&mlme_priv->roam_rd_wr_lock);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	dst = 0;
+	src = index;
+	while (dst < valid_cache_num) {
+		if (mlme_priv->roam_info[src].trigger.timestamp == 0) {
+			src++;
+		} else {
+			eroam_info[dst] = mlme_priv->roam_info[src];
+			src++;
+			dst++;
+		}
+		if (src == mlme_priv->roam_cache_num)
+			src = 0;
+	}
+
+	*roam_num = valid_cache_num;
+	*roam_info = eroam_info;
+	qdf_mutex_release(&mlme_priv->roam_rd_wr_lock);
+
+	return QDF_STATUS_SUCCESS;
+}
+#else
+static void
+wlan_cm_update_roam_stats_info(struct wlan_objmgr_psoc *psoc,
+			       struct roam_stats_event *stats_info,
+			       uint8_t index)
+{
+}
+#endif
+
 QDF_STATUS
 cm_roam_stats_event_handler(struct wlan_objmgr_psoc *psoc,
 			    struct roam_stats_event *stats_info)
@@ -3455,6 +4066,8 @@ cm_roam_stats_event_handler(struct wlan_objmgr_psoc *psoc,
 						 &stats_info->scan[i],
 						 stats_info->vdev_id);
 
+		wlan_cm_update_roam_stats_info(psoc, stats_info, i);
+
 		/*
 		 * Print BTM resp TLV info (wmi_roam_btm_response_info) only
 		 * when trigger reason is BTM or WTC_BTM. As for other roam

+ 2 - 2
components/wmi/src/wmi_unified_roam_tlv.c

@@ -2025,8 +2025,8 @@ extract_roam_frame_info_tlv(wmi_unified_t wmi_handle, void *evt_buf,
 	if (!param_buf || !param_buf->roam_frame_info ||
 	    !param_buf->num_roam_frame_info ||
 	    (frame_idx + num_frames) > param_buf->num_roam_frame_info) {
-		wmi_debug("Empty roam_frame_info param buf frame_idx:%d num_frames:%d",
-			  frame_idx, num_frames);
+		wmi_err("Empty roam_frame_info param buf frame_idx:%d num_frames:%d",
+			frame_idx, num_frames);
 		return QDF_STATUS_SUCCESS;
 	}