diff --git a/components/mlme/dispatcher/inc/cfg_mlme_generic.h b/components/mlme/dispatcher/inc/cfg_mlme_generic.h index 9fdcbbe3c912..16f427fa5cbb 100644 --- a/components/mlme/dispatcher/inc/cfg_mlme_generic.h +++ b/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") /* diff --git a/components/mlme/dispatcher/inc/wlan_mlme_api.h b/components/mlme/dispatcher/inc/wlan_mlme_api.h index 1edb89386883..8df140b209aa 100644 --- a/components/mlme/dispatcher/inc/wlan_mlme_api.h +++ b/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_ */ diff --git a/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h b/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h index 248b2f42f262..278574f66713 100644 --- a/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h +++ b/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 diff --git a/components/mlme/dispatcher/src/wlan_mlme_api.c b/components/mlme/dispatcher/src/wlan_mlme_api.c index 470527856fd1..4ad779e27a0e 100644 --- a/components/mlme/dispatcher/src/wlan_mlme_api.c +++ b/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"; + } +} diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h index 3f4fe65a61be..1171abacb94e 100644 --- a/core/wma/inc/wma_internal.h +++ b/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 /** diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index ae2423be0e9e..3107322ea6d5 100644 --- a/core/wma/src/wma_main.c +++ b/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, diff --git a/core/wma/src/wma_scan_roam.c b/core/wma/src/wma_scan_roam.c index a0a63137b8a7..d0828230f0bd 100644 --- a/core/wma/src/wma_scan_roam.c +++ b/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