/* * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2022-2024 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 copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* * DOC: sme_rrm.c * * Implementation for SME RRM APIs */ #include "ani_global.h" #include "sme_inside.h" #include "sme_api.h" #ifdef FEATURE_WLAN_DIAG_SUPPORT #include "host_diag_core_event.h" #include "host_diag_core_log.h" #endif /* FEATURE_WLAN_DIAG_SUPPORT */ #include "csr_inside_api.h" #include "rrm_global.h" #include #include #include #include #include <../../core/src/wlan_cm_vdev_api.h> #include "rrm_api.h" #include "wlan_cp_stats_mc_ucfg_api.h" /* Roam score for a neighbor AP will be calculated based on the below * definitions. The calculated roam score will be used to select the * roamable candidate from neighbor AP list */ #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_REACHABILITY 0 /* When we support 11r over the DS, this should have a non-zero value */ #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_SECURITY 10 #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_KEY_SCOPE 20 #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_SPECTRUM_MGMT 0 /* Not used */ #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_QOS 5 #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_APSD 3 #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_RRM 8 #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_DELAYED_BA 0 /* We dont support delayed BA */ #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_IMMEDIATE_BA 3 #define RRM_ROAM_SCORE_NEIGHBOR_REPORT_MOBILITY_DOMAIN 30 #ifdef FEATURE_WLAN_ESE #define RRM_ROAM_SCORE_NEIGHBOR_IAPP_LIST 30 #endif /* RRM SCAN DWELL TIME */ #define RRM_SCAN_MIN_DWELL_TIME 20 uint64_t rrm_scan_timer; /** * rrm_ll_purge_neighbor_cache() -Purges all the entries in the neighbor cache * * @mac: Pointer to the Hal Handle. * @pList: Pointer the List that should be purged. * * This function purges all the entries in the neighbor cache and frees up all * the internal nodes * * Return: void */ static void rrm_ll_purge_neighbor_cache(struct mac_context *mac, tDblLinkList *pList) { tListElem *pEntry; tRrmNeighborReportDesc *pNeighborReportDesc; csr_ll_lock(pList); while ((pEntry = csr_ll_remove_head(pList, LL_ACCESS_NOLOCK)) != NULL) { pNeighborReportDesc = GET_BASE_ADDR(pEntry, tRrmNeighborReportDesc, List); qdf_mem_free(pNeighborReportDesc->pNeighborBssDescription); qdf_mem_free(pNeighborReportDesc); } csr_ll_unlock(pList); } /** * rrm_indicate_neighbor_report_result() -calls the callback registered for * neighbor report * @mac: Pointer to the Hal Handle. * @qdf_status - QDF_STATUS_SUCCESS/QDF_STATUS_FAILURE based on whether a valid * report is received or neighbor timer expired * * This function calls the callback register by the caller while requesting for * neighbor report. This function gets invoked if a neighbor report is received * from an AP or neighbor response wait timer expires. * * Return: void */ static void rrm_indicate_neighbor_report_result(struct mac_context *mac, QDF_STATUS qdf_status) { NeighborReportRspCallback callback; void *callbackContext; /* Reset the neighbor response pending status */ mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX]. neighborReqControlInfo.isNeighborRspPending = false; /* Stop the timer if it is already running. * The timer should be running only in the SUCCESS case. */ if (QDF_TIMER_STATE_RUNNING == qdf_mc_timer_get_current_state(&mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX]. neighborReqControlInfo. neighborRspWaitTimer)) { sme_debug("No entry in neighbor report cache"); qdf_mc_timer_stop(&mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX]. neighborReqControlInfo.neighborRspWaitTimer); } callback = mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. neighborRspCallbackInfo.neighborRspCallback; callbackContext = mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. neighborRspCallbackInfo.neighborRspCallbackContext; /* Reset the callback and the callback context before calling the * callback. It is very likely that there may be a registration in * callback itself. */ mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. neighborRspCallbackInfo.neighborRspCallback = NULL; mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. neighborRspCallbackInfo.neighborRspCallbackContext = NULL; /* Call the callback with the status received from caller */ if (callback) callback(callbackContext, qdf_status); } /** * sme_RrmBeaconReportXmitInd () - Send beacon report * @mac_ctx Pointer to mac context * @measurement_index: Measurement index * @result_arr scan results * @msrmnt_status flag to indicate that the measurement is done. * @bss_count bss count * * Create and send the beacon report Xmit ind message to PE. * * Return: status */ static QDF_STATUS sme_rrm_send_beacon_report_xmit_ind(struct mac_context *mac_ctx, uint8_t measurement_index, tCsrScanResultInfo **result_arr, uint8_t msrmnt_status, uint8_t bss_count) { struct bss_description *bss_desc = NULL; tpSirBeaconReportXmitInd beacon_rep; uint16_t length; uint32_t size; uint8_t i = 0, j = 0, counter = 0; tCsrScanResultInfo *cur_result = NULL; QDF_STATUS status = QDF_STATUS_E_FAILURE; tpRrmSMEContext rrm_ctx = &mac_ctx->rrm.rrmSmeContext[measurement_index]; struct bss_description *tmp_bss_desc[SIR_BCN_REPORT_MAX_BSS_DESC] = {0}; if (!result_arr && !msrmnt_status) { sme_err("Beacon report xmit Ind to PE Failed"); return QDF_STATUS_E_FAILURE; } if (result_arr) cur_result = result_arr[j]; do { length = sizeof(tSirBeaconReportXmitInd); beacon_rep = qdf_mem_malloc(length); if (!beacon_rep) return QDF_STATUS_E_NOMEM; beacon_rep->messageType = eWNI_SME_BEACON_REPORT_RESP_XMIT_IND; beacon_rep->length = length; beacon_rep->measurement_idx = measurement_index; beacon_rep->uDialogToken = rrm_ctx->token; beacon_rep->duration = rrm_ctx->duration[0]; beacon_rep->regClass = rrm_ctx->regClass; qdf_mem_copy(beacon_rep->bssId, rrm_ctx->sessionBssId.bytes, QDF_MAC_ADDR_SIZE); i = 0; while (cur_result) { bss_desc = &cur_result->BssDescriptor; if (!bss_desc) break; size = bss_desc->length + sizeof(bss_desc->length); beacon_rep->pBssDescription[i] = qdf_mem_malloc(size); if (NULL == beacon_rep->pBssDescription[i]) break; qdf_mem_copy(beacon_rep->pBssDescription[i], bss_desc, size); tmp_bss_desc[i] = beacon_rep->pBssDescription[i]; sme_debug("RRM Result Bssid = " QDF_MAC_ADDR_FMT " freq= %d, rssi = -%d", QDF_MAC_ADDR_REF( beacon_rep->pBssDescription[i]->bssId), beacon_rep->pBssDescription[i]->chan_freq, beacon_rep->pBssDescription[i]->rssi * (-1)); beacon_rep->numBssDesc++; if (++i >= SIR_BCN_REPORT_MAX_BSS_DESC) break; if (i + j >= bss_count) break; cur_result = result_arr[j + i]; } j += i; if (!result_arr || (!cur_result) || (j >= bss_count)) { cur_result = NULL; sme_debug("Reached to max/last BSS in cur_result list"); } else { cur_result = result_arr[j]; sme_debug("Move to the next BSS set in cur_result list"); } beacon_rep->fMeasureDone = (cur_result) ? false : msrmnt_status; sme_debug("SME Sending BcnRepXmit to PE numBss %d i %d j %d", beacon_rep->numBssDesc, i, j); status = umac_send_mb_message_to_mac(beacon_rep); if (status != QDF_STATUS_SUCCESS) for (counter = 0; counter < i; ++counter) qdf_mem_free(tmp_bss_desc[counter]); } while (cur_result); return status; } #ifdef FEATURE_WLAN_ESE /** * sme_ese_send_beacon_req_scan_results () - Send beacon report * @mac_ctx: Pointer to mac context * @measurement_index: Measurement request index * @session_id: session id * @freq: channel frequency * @result_arr: scan results * @msrmnt_status: flag to indicate that the measurement is done. * @bss_count: number of bss found * * This function sends up the scan results received as a part of * beacon request scanning. * This function is called after receiving the scan results per channel * Due to the limitation on the size of the IWEVCUSTOM buffer, we send * 3 BSSIDs of beacon report information in one custom event; * * Return: status */ static QDF_STATUS sme_ese_send_beacon_req_scan_results( struct mac_context *mac_ctx, uint8_t measurement_index, uint32_t session_id, uint32_t freq, tCsrScanResultInfo **result_arr, uint8_t msrmnt_status, uint8_t bss_count) { QDF_STATUS status = QDF_STATUS_E_FAILURE; QDF_STATUS fill_ie_status; struct bss_description *bss_desc = NULL; uint32_t ie_len = 0; uint32_t out_ie_len = 0; uint8_t bss_counter = 0; tCsrScanResultInfo *cur_result = NULL; tpRrmSMEContext rrm_ctx = &mac_ctx->rrm.rrmSmeContext[measurement_index]; struct csr_roam_info *roam_info; struct ese_bcn_report_rsp bcn_rpt_rsp; struct ese_bcn_report_rsp *bcn_report = &bcn_rpt_rsp; tpCsrEseBeaconReqParams cur_meas_req = NULL; uint8_t i = 0, j = 0; tBcnReportFields *bcn_rpt_fields; if (!rrm_ctx) { sme_err("rrm_ctx is NULL"); return QDF_STATUS_E_FAILURE; } if (!result_arr && !msrmnt_status) { sme_err("Beacon report xmit Ind to HDD Failed"); return QDF_STATUS_E_FAILURE; } roam_info = qdf_mem_malloc(sizeof(*roam_info)); if (!roam_info) return QDF_STATUS_E_NOMEM; if (result_arr) cur_result = result_arr[bss_counter]; do { cur_meas_req = NULL; /* memset bcn_rpt_rsp for each iteration */ qdf_mem_zero(&bcn_rpt_rsp, sizeof(bcn_rpt_rsp)); for (i = 0; i < rrm_ctx->eseBcnReqInfo.numBcnReqIe; i++) { if (rrm_ctx->eseBcnReqInfo.bcnReq[i].ch_freq == freq) { cur_meas_req = &rrm_ctx->eseBcnReqInfo.bcnReq[i]; break; } } if (cur_meas_req) bcn_report->measurementToken = cur_meas_req->measurementToken; sme_debug("freq: %d MeasToken: %d", freq, bcn_report->measurementToken); j = 0; while (cur_result) { bss_desc = &cur_result->BssDescriptor; if (!bss_desc) { cur_result = NULL; break; } ie_len = GET_IE_LEN_IN_BSS(bss_desc->length); bcn_rpt_fields = &bcn_report->bcnRepBssInfo[j].bcnReportFields; bcn_rpt_fields->ChanNum = wlan_reg_freq_to_chan( mac_ctx->pdev, bss_desc->chan_freq); bcn_report->bcnRepBssInfo[j].bcnReportFields.Spare = 0; if (cur_meas_req) bcn_rpt_fields->MeasDuration = cur_meas_req->measurementDuration; bcn_rpt_fields->PhyType = bss_desc->nwType; bcn_rpt_fields->RecvSigPower = bss_desc->rssi; bcn_rpt_fields->ParentTsf = bss_desc->parentTSF; bcn_rpt_fields->TargetTsf[0] = bss_desc->timeStamp[0]; bcn_rpt_fields->TargetTsf[1] = bss_desc->timeStamp[1]; bcn_rpt_fields->BcnInterval = bss_desc->beaconInterval; bcn_rpt_fields->CapabilityInfo = bss_desc->capabilityInfo; qdf_mem_copy(bcn_rpt_fields->Bssid, bss_desc->bssId, sizeof(tSirMacAddr)); fill_ie_status = sir_beacon_ie_ese_bcn_report(mac_ctx, (uint8_t *) bss_desc->ieFields, ie_len, &(bcn_report->bcnRepBssInfo[j]. pBuf), &out_ie_len); if (QDF_STATUS_E_FAILURE == fill_ie_status) continue; bcn_report->bcnRepBssInfo[j].ieLen = out_ie_len; sme_debug("Bssid"QDF_MAC_ADDR_FMT" Freq:%d Rssi:%d", QDF_MAC_ADDR_REF(bss_desc->bssId), bss_desc->chan_freq, (-1) * bss_desc->rssi); bcn_report->numBss++; if (++j >= SIR_BCN_REPORT_MAX_BSS_DESC) break; if ((bss_counter + j) >= bss_count) break; cur_result = result_arr[bss_counter + j]; } bss_counter += j; if (!result_arr || !cur_result || (bss_counter >= bss_count)) { cur_result = NULL; sme_err("Reached to the max/last BSS in cur_result list"); } else { cur_result = result_arr[bss_counter]; sme_err("Move to the next BSS set in cur_result list"); } bcn_report->flag = (msrmnt_status << 1) | ((cur_result) ? true : false); sme_debug("SME Sending BcnRep to HDD numBss: %d j: %d bss_counter: %d flag: %d", bcn_report->numBss, j, bss_counter, bcn_report->flag); roam_info->pEseBcnReportRsp = bcn_report; status = csr_roam_call_callback(mac_ctx, session_id, roam_info, eCSR_ROAM_ESE_BCN_REPORT_IND, 0); /* Free the memory allocated to IE */ for (i = 0; i < j; i++) if (bcn_report->bcnRepBssInfo[i].pBuf) qdf_mem_free(bcn_report->bcnRepBssInfo[i].pBuf); } while (cur_result); qdf_mem_free(roam_info); return status; } static inline void sme_reset_ese_bcn_req_in_progress(tpRrmSMEContext sme_rrm_ctx) { if (sme_rrm_ctx) sme_rrm_ctx->eseBcnReqInProgress = false; } #else static inline void sme_reset_ese_bcn_req_in_progress(tpRrmSMEContext sme_rrm_ctx) {} #endif /* FEATURE_WLAN_ESE */ /** * sme_rrm_send_scan_result() - to get scan result and send the beacon report * @mac_ctx: pointer to mac context * @measurement_index: Measurement request number * @num_chan: number of channels * @freq_list: list of channel frequencies to fetch the result from * @measurementdone: Flag to indicate measurement done or no * * This function is called to get the scan result from CSR and send the beacon * report xmit ind message to PE * * Return: QDF_STATUS */ static QDF_STATUS sme_rrm_send_scan_result(struct mac_context *mac_ctx, uint8_t measurement_index, uint8_t num_chan, uint32_t *freq_list, uint8_t measurementdone) { struct scan_filter *filter; tScanResultHandle result_handle; tCsrScanResultInfo *scan_results, *next_result; tCsrScanResultInfo **scanresults_arr = NULL; struct scan_result_list *result_list; QDF_STATUS status; uint32_t num_scan_results, counter = 0; tpRrmSMEContext rrm_ctx = &mac_ctx->rrm.rrmSmeContext[measurement_index]; uint32_t session_id; tSirScanType scan_type; struct qdf_mac_addr bss_peer_mac; filter = qdf_mem_malloc(sizeof(*filter)); if (!filter) return QDF_STATUS_E_NOMEM; if (qdf_is_macaddr_zero(filter->bssid_list) || qdf_is_macaddr_group(filter->bssid_list)) { filter->num_of_bssid = 0; } else { /* update filter to get scan result with just target BSSID */ filter->num_of_bssid = 1; qdf_mem_copy(filter->bssid_list[0].bytes, rrm_ctx->bssId, sizeof(struct qdf_mac_addr)); } if (rrm_ctx->ssId.length) { filter->num_of_ssid = 1; filter->ssid_list[0].length = rrm_ctx->ssId.length; if (filter->ssid_list[0].length > WLAN_SSID_MAX_LEN) filter->ssid_list[0].length = WLAN_SSID_MAX_LEN; qdf_mem_copy(filter->ssid_list[0].ssid, rrm_ctx->ssId.ssId, filter->ssid_list[0].length); } filter->num_of_channels = num_chan; if (filter->num_of_channels > NUM_CHANNELS) filter->num_of_channels = NUM_CHANNELS; qdf_mem_copy(filter->chan_freq_list, freq_list, filter->num_of_channels * sizeof(filter->chan_freq_list[0])); filter->rrm_measurement_filter = true; if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource || eRRM_MSG_SOURCE_LEGACY_ESE == rrm_ctx->msgSource) scan_type = rrm_ctx->measMode[rrm_ctx->currentIndex]; else scan_type = rrm_ctx->measMode[0]; if (scan_type == eSIR_BEACON_TABLE) filter->age_threshold = wlan_scan_get_aging_time(mac_ctx->psoc); /* * In case this is beacon report request from last AP (before roaming) * following call to csr_roam_get_session_id_from_bssid will fail, * hence use current session ID instead of one stored in SME rrm context */ if (QDF_STATUS_E_FAILURE == csr_roam_get_session_id_from_bssid(mac_ctx, &rrm_ctx->sessionBssId, &session_id)) { sme_debug("BSSID mismatch, using current session_id"); session_id = mac_ctx->roam.roamSession->vdev_id; } status = csr_scan_get_result(mac_ctx, filter, &result_handle); qdf_mem_free(filter); sme_debug("RRM Measurement Done %d for index:%d", measurementdone, measurement_index); if (!result_handle) { /* * no scan results * Spec. doesn't say anything about such condition * Since section 7.4.6.2 (IEEE802.11k-2008) says-rrm report * frame should contain one or more report IEs. It probably * means dont send any response if no matching BSS found. * Moreover, there is no flag or field in measurement report * IE(7.3.2.22) OR beacon report IE(7.3.2.22.6) that can be set * to indicate no BSS found on a given channel. If we finished * measurement on all the channels, we still need to send a * xmit indication with moreToFollow set to MEASURMENT_DONE so * that PE can clean any context allocated. */ if (!measurementdone) return status; #ifdef FEATURE_WLAN_ESE if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource) status = sme_ese_send_beacon_req_scan_results(mac_ctx, measurement_index, session_id, freq_list[0], NULL, measurementdone, 0); else #endif /* FEATURE_WLAN_ESE */ status = sme_rrm_send_beacon_report_xmit_ind(mac_ctx, measurement_index, NULL, measurementdone, 0); return status; } scan_results = csr_scan_result_get_first(mac_ctx, result_handle); if (!scan_results && measurementdone) { #ifdef FEATURE_WLAN_ESE if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource) { status = sme_ese_send_beacon_req_scan_results(mac_ctx, measurement_index, session_id, freq_list[0], NULL, measurementdone, 0); } else #endif /* FEATURE_WLAN_ESE */ status = sme_rrm_send_beacon_report_xmit_ind(mac_ctx, measurement_index, NULL, measurementdone, 0); } result_list = (struct scan_result_list *)result_handle; num_scan_results = csr_ll_count(&result_list->List); if (!num_scan_results) { sme_err("num_scan_results is %d", num_scan_results); status = QDF_STATUS_E_FAILURE; goto rrm_send_scan_results_done; } sme_debug("num_scan_results %d", num_scan_results); scanresults_arr = qdf_mem_malloc(num_scan_results * sizeof(next_result)); if (!scanresults_arr) { status = QDF_STATUS_E_NOMEM; goto send_scan_results; } status = wlan_mlme_get_bssid_vdev_id(mac_ctx->pdev, session_id, &bss_peer_mac); if (QDF_IS_STATUS_ERROR(status)) { sme_err("BSSID not found for vdev: %d", session_id); status = QDF_STATUS_E_FAILURE; goto send_scan_results; } if (!cm_is_vdevid_connected(mac_ctx->pdev, session_id)) { sme_err("vdev:%d is not connected", session_id); status = QDF_STATUS_E_FAILURE; goto send_scan_results; } while (scan_results) { /* * Connected AP beacon is offloaded to firmware. * Firmware will discard connected AP beacon except that * special IE exists Connected AP beacon will not be sent * to host. Hence, timer of connected AP in scan results is * not updated and can not meet * "pScanResult->timer >= RRM_scan_timer". */ uint8_t is_conn_bss_found = false; uint8_t is_nontx_of_conn_bss = false; if (!qdf_mem_cmp(scan_results->BssDescriptor.bssId, bss_peer_mac.bytes, sizeof(struct qdf_mac_addr))) { is_conn_bss_found = true; sme_debug("Connected BSS in scan results"); } if (scan_results->BssDescriptor.mbssid_info.profile_num) { if (!qdf_mem_cmp(scan_results->BssDescriptor. mbssid_info.trans_bssid, bss_peer_mac.bytes, QDF_MAC_ADDR_SIZE)) { is_nontx_of_conn_bss = true; sme_debug("Non Tx BSS of Conn AP in results"); } } next_result = csr_scan_result_get_next(mac_ctx, result_handle); sme_debug("Scan res timer:%lu, rrm scan timer:%llu", scan_results->timer, rrm_scan_timer); if ((scan_results->timer >= rrm_scan_timer) || (is_conn_bss_found == true) || is_nontx_of_conn_bss) scanresults_arr[counter++] = scan_results; scan_results = next_result; if (counter >= num_scan_results) break; } send_scan_results: /* * The beacon report should be sent whether the counter is zero or * non-zero. There might be a few scan results in the cache but not * actually are a result of this scan. During that scenario, the * counter will be zero. The report should be sent and LIM will further * cleanup the RRM to accept the further incoming requests * In case the counter is Zero, the pScanResultsArr will be NULL. * The next level routine does a check for the measurementDone to * determine whether to send a report or not. */ sme_debug("Number of BSS Desc with RRM Scan %d", counter); if (counter || measurementdone) { #ifdef FEATURE_WLAN_ESE if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource) status = sme_ese_send_beacon_req_scan_results(mac_ctx, measurement_index, session_id, freq_list[0], scanresults_arr, measurementdone, counter); else #endif /* FEATURE_WLAN_ESE */ status = sme_rrm_send_beacon_report_xmit_ind(mac_ctx, measurement_index, scanresults_arr, measurementdone, counter); } rrm_send_scan_results_done: if (scanresults_arr) qdf_mem_free(scanresults_arr); csr_scan_result_purge(mac_ctx, result_handle); return status; } /** * sme_rrm_scan_request_callback() -Sends the beacon report xmit to PE * @mac_handle: Opaque handle to the MAC context * @pSmeRrmContext: SME rrm context for measurement request * @sessionId: session id * @scanId: Scan ID. * @status: CSR Status. * * The sme module calls this callback function once it finish the scan request * and this function send the beacon report xmit to PE and starts a timer of * random interval to issue next request. * * Return : 0 for success, non zero for failure */ static QDF_STATUS sme_rrm_scan_request_callback(struct mac_context *mac, tpRrmSMEContext pSmeRrmContext, uint8_t sessionId, uint32_t scanId, eCsrScanStatus status) { uint8_t num_chan; uint32_t *freq_list; QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; /* * Even if RRM scan response is received after roaming to different AP * the messege shall be posted to PE for rrm cleanup. */ freq_list = pSmeRrmContext->channelList.freq_list; if (!freq_list) { sme_err("[802.11 RRM]: Global freq list is null"); pSmeRrmContext->channelList.numOfChannels = 0; qdf_status = QDF_STATUS_E_FAILURE; goto end; } /* Sending the beacon report xmit for the all the valid channel * scan results within randomization interval. */ num_chan = pSmeRrmContext->channelList.numOfChannels; sme_rrm_send_scan_result(mac, pSmeRrmContext->measurement_idx, num_chan, &freq_list[0], true); /* Clean up all context and send a * message to PE with measurement done flag set. */ qdf_mem_free(pSmeRrmContext->channelList.freq_list); pSmeRrmContext->channelList.freq_list = NULL; end: pSmeRrmContext->channelList.numOfChannels = 0; sme_reset_ese_bcn_req_in_progress(pSmeRrmContext); return qdf_status; } /** * sme_rrm_send_chan_load_report_xmit_ind() -Sends the chan load report xmit * to PE * @mac: global mac context * @rrm_sme_ctx: SME rrm context for measurement request * @vdev_id: vdev id * @is_report_success: need to send failure report or not * * The sme module calls this function once it finish the scan request * and this function send the chan load report xmit to PE. * * Return : None */ static void sme_rrm_send_chan_load_report_xmit_ind(struct mac_context *mac, tpRrmSMEContext rrm_sme_ctx, uint8_t vdev_id, bool is_report_success) { uint8_t measurement_index = rrm_sme_ctx->measurement_idx; struct chan_load_xmit_ind *chan_load_resp; uint16_t length; tpRrmSMEContext rrm_ctx = &mac->rrm.rrmSmeContext[measurement_index]; const struct bonded_channel_freq *range; uint8_t chan_load = 0, temp_chan_load; qdf_freq_t start_freq, end_freq, op_freq; enum phy_ch_width req_chan_width; length = sizeof(struct chan_load_xmit_ind); chan_load_resp = qdf_mem_malloc(length); if (!chan_load_resp) return; chan_load_resp->messageType = eWNI_SME_CHAN_LOAD_REPORT_RESP_XMIT_IND; chan_load_resp->is_report_success = is_report_success; chan_load_resp->length = length; chan_load_resp->measurement_idx = measurement_index; chan_load_resp->dialog_token = rrm_ctx->token; chan_load_resp->duration = rrm_ctx->duration[0]; chan_load_resp->op_class = rrm_ctx->regClass; chan_load_resp->channel = rrm_ctx->chan_load_req_info.channel; chan_load_resp->rrm_scan_tsf = rrm_ctx->chan_load_req_info.rrm_scan_tsf; op_freq = rrm_ctx->chan_load_req_info.req_freq; req_chan_width = rrm_ctx->chan_load_req_info.req_chan_width; if (req_chan_width == CH_WIDTH_INVALID) { sme_debug("Invalid scanned_ch_width"); return; } if (req_chan_width == CH_WIDTH_20MHZ) { start_freq = op_freq; end_freq = op_freq; sme_debug("cw %d: start_freq %d, end_freq %d", req_chan_width, start_freq, end_freq); } else if (req_chan_width == CH_WIDTH_320MHZ) { if (!rrm_ctx->chan_load_req_info.bw_ind.is_bw_ind_element) { sme_debug("is_bw_ind_element is false"); return; } qdf_mem_copy(&chan_load_resp->bw_ind, &rrm_ctx->chan_load_req_info.bw_ind, sizeof(chan_load_resp->bw_ind)); range = wlan_reg_get_bonded_chan_entry(op_freq, rrm_ctx->chan_load_req_info.bw_ind.channel_width, rrm_ctx->chan_load_req_info.bw_ind.center_freq); if (!range) { sme_debug("vdev %d : range is null for freq %d", vdev_id, op_freq); return; } start_freq = range->start_freq; end_freq = range->end_freq; sme_debug("cw %d: start_freq %d, end_freq %d", req_chan_width, start_freq, end_freq); } else { if (rrm_ctx->chan_load_req_info.wide_bw.is_wide_bw_chan_switch) { qdf_mem_copy(&chan_load_resp->wide_bw, &rrm_ctx->chan_load_req_info.wide_bw, sizeof(chan_load_resp->wide_bw)); req_chan_width = rrm_ctx->chan_load_req_info.wide_bw.channel_width; sme_debug("Fill wide_bw_chan_switch IE for cw:%d", req_chan_width); } range = wlan_reg_get_bonded_chan_entry(op_freq, req_chan_width, 0); if (!range) { sme_debug("range is NULL for freq %d, ch_width %d", op_freq, req_chan_width); return; } start_freq = range->start_freq; end_freq = range->end_freq; sme_debug("cw %d: start_freq %d, end_freq %d", req_chan_width, start_freq, end_freq); } for (; start_freq <= end_freq;) { temp_chan_load = wlan_cp_stats_get_rx_clear_count(mac->psoc, vdev_id, start_freq); if (chan_load < temp_chan_load) chan_load = temp_chan_load; start_freq += BW_20_MHZ; } chan_load_resp->chan_load = chan_load; sme_debug("SME Sending CHAN_LOAD_REPORT_RESP_XMIT_IND to PE"); umac_send_mb_message_to_mac(chan_load_resp); } static void sme_rrm_scan_event_callback(struct wlan_objmgr_vdev *vdev, struct scan_event *event, void *arg) { struct mac_context *mac_ctx; uint32_t scan_id; uint8_t vdev_id, i; eCsrScanStatus scan_status = eCSR_SCAN_FAILURE; bool success = false; tpRrmSMEContext smerrmctx; mac_ctx = (struct mac_context *)arg; if (!mac_ctx) { sme_err("invalid mac_ctx"); return; } vdev_id = wlan_vdev_get_id(vdev); scan_id = event->scan_id; qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_SME, event->type, event->vdev_id, event->scan_id); if (!util_is_scan_completed(event, &success)) return; if (success) scan_status = eCSR_SCAN_SUCCESS; for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { smerrmctx = &mac_ctx->rrm.rrmSmeContext[i]; if (smerrmctx->scan_id == scan_id) break; if (i == (MAX_MEASUREMENT_REQUEST - 1)) return; } sme_debug("vdev: %d : Scan completed for scan_id:%d idx:%d, type:%d", vdev_id, scan_id, smerrmctx->measurement_idx, smerrmctx->measurement_type); switch (smerrmctx->measurement_type) { case RRM_CHANNEL_LOAD: sme_rrm_send_chan_load_report_xmit_ind(mac_ctx, smerrmctx, vdev_id, true); break; case RRM_BEACON_REPORT: sme_rrm_scan_request_callback(mac_ctx, smerrmctx, vdev_id, scan_id, scan_status); break; default: sme_err("Unknown measurement_type: %d", smerrmctx->measurement_type); break; } } #define RRM_CHAN_WEIGHT_CHAR_LEN 5 #define RRM_MAX_CHAN_TO_PRINT 39 QDF_STATUS sme_rrm_issue_scan_req(struct mac_context *mac_ctx, uint8_t idx) { QDF_STATUS status = QDF_STATUS_SUCCESS; tpRrmSMEContext sme_rrm_ctx = &mac_ctx->rrm.rrmSmeContext[idx]; uint32_t session_id; tSirScanType scan_type; uint8_t ch_idx; uint32_t *freq_list; status = csr_roam_get_session_id_from_bssid(mac_ctx, &sme_rrm_ctx->sessionBssId, &session_id); if (status != QDF_STATUS_SUCCESS) { sme_err("sme session ID not found for bssid= "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(sme_rrm_ctx->sessionBssId.bytes)); status = QDF_STATUS_E_FAILURE; goto send_ind; } if ((sme_rrm_ctx->currentIndex) >= sme_rrm_ctx->channelList.numOfChannels) { sme_rrm_send_beacon_report_xmit_ind(mac_ctx, idx, NULL, true, 0); sme_debug("done with the complete ch lt. finish and fee now"); goto free_ch_lst; } if (eRRM_MSG_SOURCE_ESE_UPLOAD == sme_rrm_ctx->msgSource || eRRM_MSG_SOURCE_LEGACY_ESE == sme_rrm_ctx->msgSource) scan_type = sme_rrm_ctx->measMode[sme_rrm_ctx->currentIndex]; else scan_type = sme_rrm_ctx->measMode[0]; if ((eSIR_ACTIVE_SCAN == scan_type) || (eSIR_PASSIVE_SCAN == scan_type)) { uint32_t max_chan_time, buff_len, buff_num = 0, chan_count = 0; uint64_t current_time; struct scan_start_request *req; struct wlan_objmgr_vdev *vdev; uint16_t i; char *chan_buff = NULL; if (!sme_rrm_ctx->channelList.numOfChannels || !sme_rrm_ctx->channelList.freq_list) { sme_err("[802.11 RRM]: Global freq list is null"); status = QDF_STATUS_E_FAILURE; goto send_ind; } req = qdf_mem_malloc(sizeof(*req)); if (!req) { status = QDF_STATUS_E_NOMEM; goto send_ind; } vdev = wlan_objmgr_get_vdev_by_id_from_psoc( mac_ctx->psoc, session_id, WLAN_LEGACY_SME_ID); if (!vdev) { sme_err("VDEV is null %d", session_id); status = QDF_STATUS_E_INVAL; qdf_mem_free(req); goto send_ind; } wlan_scan_init_default_params(vdev, req); req->scan_req.scan_id = wlan_scan_get_scan_id(mac_ctx->psoc); sme_rrm_ctx->scan_id = req->scan_req.scan_id; sme_debug("RRM_SCN: rrm_idx:%d scan_id:%d", sme_rrm_ctx->measurement_idx, sme_rrm_ctx->scan_id); req->scan_req.scan_f_passive = (scan_type == eSIR_ACTIVE_SCAN) ? false : true; req->scan_req.vdev_id = wlan_vdev_get_id(vdev); req->scan_req.scan_req_id = sme_rrm_ctx->req_id; qdf_mem_copy(&req->scan_req.bssid_list[0], sme_rrm_ctx->bssId, QDF_MAC_ADDR_SIZE); req->scan_req.num_bssid = 1; if (sme_rrm_ctx->ssId.length) { req->scan_req.num_ssids = 1; qdf_mem_copy(&req->scan_req.ssid[0].ssid, sme_rrm_ctx->ssId.ssId, sme_rrm_ctx->ssId.length); req->scan_req.ssid[0].length = sme_rrm_ctx->ssId.length; } /* * set min and max channel time * sme_rrm_ctx->duration; Dont use min timeout. */ if (eRRM_MSG_SOURCE_ESE_UPLOAD == sme_rrm_ctx->msgSource || eRRM_MSG_SOURCE_LEGACY_ESE == sme_rrm_ctx->msgSource) max_chan_time = sme_rrm_ctx->duration[sme_rrm_ctx->currentIndex]; else max_chan_time = sme_rrm_ctx->duration[0]; /* * Use max_chan_time if max_chan_time is more than def value * depending on type of scan. */ if (req->scan_req.scan_f_passive) { if (max_chan_time >= RRM_SCAN_MIN_DWELL_TIME) { req->scan_req.dwell_time_passive = max_chan_time; req->scan_req.dwell_time_passive_6g = max_chan_time; } sme_debug("Passive Max Dwell Time(%d)", req->scan_req.dwell_time_passive); } else { if (max_chan_time >= RRM_SCAN_MIN_DWELL_TIME) { req->scan_req.dwell_time_active = max_chan_time; req->scan_req.dwell_time_active_2g = max_chan_time; req->scan_req.dwell_time_active_6g = max_chan_time; } sme_debug("Active Max Dwell Time(%d) 2G Dwell time %d", req->scan_req.dwell_time_active, req->scan_req.dwell_time_active_2g); } req->scan_req.adaptive_dwell_time_mode = SCAN_DWELL_MODE_STATIC; /* * For RRM scans timing is very important especially when the * request is for limited channels. There is no need for * firmware to rest for about 100-200 ms on the home channel. * Instead, it can start the scan right away which will make the * host to respond with the beacon report as quickly as * possible. Ensure that the scan requests are not back to back * and hence there is a check to see if the requests are atleast * 1 second apart. */ current_time = (uint64_t)qdf_mc_timer_get_system_time(); sme_debug("prev scan triggered before %llu ms, totalchannels %d", current_time - rrm_scan_timer, sme_rrm_ctx->channelList.numOfChannels); if ((abs(current_time - rrm_scan_timer) > 1000) && (sme_rrm_ctx->channelList.numOfChannels == 1)) { req->scan_req.max_rest_time = 1; req->scan_req.min_rest_time = 1; req->scan_req.idle_time = 1; } rrm_scan_timer = (uint64_t)qdf_mc_timer_get_system_time(); /* set requestType to full scan */ req->scan_req.chan_list.num_chan = sme_rrm_ctx->channelList.numOfChannels; buff_len = (QDF_MIN(req->scan_req.chan_list.num_chan, RRM_MAX_CHAN_TO_PRINT) * RRM_CHAN_WEIGHT_CHAR_LEN) + 1; chan_buff = qdf_mem_malloc(buff_len); if (!chan_buff) { status = QDF_STATUS_E_NOMEM; goto send_ind; } sme_debug("Number of chan %d active duration %d passive %d", req->scan_req.chan_list.num_chan, req->scan_req.dwell_time_active, req->scan_req.dwell_time_passive); for (i = 0; i < req->scan_req.chan_list.num_chan; i++) { req->scan_req.chan_list.chan[i].freq = (qdf_freq_t)sme_rrm_ctx->channelList.freq_list[i]; buff_num += qdf_scnprintf(chan_buff + buff_num, buff_len - buff_num, " %d", req->scan_req.chan_list.chan[i].freq); chan_count++; if (chan_count >= RRM_MAX_CHAN_TO_PRINT) { sme_debug("RRM Scan Req for channels: %s", chan_buff); buff_num = 0; chan_count = 0; } } if (buff_num) sme_debug("RRM Freq scan req channels: %s", chan_buff); qdf_mem_free(chan_buff); /* * Fill RRM scan type for these requests. This is done * because in scan concurrency update params we update the * dwell time active which was not the expectation. * So doing a check of RRM scan request, we would not * update the dwell time. */ req->scan_req.scan_type = SCAN_TYPE_RRM; status = wlan_scan_start(req); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); if (QDF_IS_STATUS_ERROR(status)) goto send_ind; return status; } else if (eSIR_BEACON_TABLE == scan_type) { /* * In beacon table mode, scan results are taken directly from * scan cache without issuing any scan request. So, it is not * proper to update rrm_scan_timer with latest time and hence * made it to zero to satisfy * pScanResult->timer >= rrm_scan_timer */ rrm_scan_timer = 0; freq_list = sme_rrm_ctx->channelList.freq_list; if (!freq_list) { sme_err("[802.11 RRM]: Global freq list is null"); sme_reset_ese_bcn_req_in_progress(sme_rrm_ctx); status = QDF_STATUS_E_FAILURE; goto send_ind; } ch_idx = sme_rrm_ctx->currentIndex; for (; ch_idx < sme_rrm_ctx->channelList.numOfChannels; ch_idx++) { if ((ch_idx + 1) < sme_rrm_ctx->channelList.numOfChannels) { sme_rrm_send_scan_result(mac_ctx, idx, 1, &freq_list[ch_idx], false); /* Advance the current index. */ sme_rrm_ctx->currentIndex++; } else { /* * Done with the measurement. Clean up all * context and send a message to PE with * measurement done flag set. */ sme_rrm_send_scan_result(mac_ctx, idx, 1, &freq_list[ch_idx], true); sme_reset_ese_bcn_req_in_progress(sme_rrm_ctx); goto free_ch_lst; } } } sme_err("Unknown beacon report req mode(%d)", scan_type); /* * Indicate measurement completion to PE * If this is not done, pCurrentReq pointer will not be freed * and PE will not handle subsequent Beacon requests */ send_ind: sme_rrm_send_beacon_report_xmit_ind(mac_ctx, idx, NULL, true, 0); free_ch_lst: qdf_mem_free(sme_rrm_ctx->channelList.freq_list); sme_rrm_ctx->channelList.freq_list = NULL; sme_rrm_ctx->channelList.numOfChannels = 0; return status; } static QDF_STATUS sme_rrm_fill_scan_channels(struct mac_context *mac, uint8_t *country, tpRrmSMEContext sme_rrm_context, uint8_t op_class, uint32_t num_channels) { uint32_t num_chan = 0; uint32_t i; uint32_t *freq_list; bool found; freq_list = sme_rrm_context->channelList.freq_list; found = false; for (i = 0; i < num_channels; i++) { found = wlan_reg_is_freq_in_country_opclass(mac->pdev, country, op_class, freq_list[i]); if (found) { freq_list[num_chan] = freq_list[i]; num_chan++; } found = false; } sme_rrm_context->channelList.numOfChannels = num_chan; if (sme_rrm_context->channelList.numOfChannels == 0) { qdf_mem_free(sme_rrm_context->channelList.freq_list); sme_rrm_context->channelList.freq_list = NULL; sme_err_rl("No channels populated with requested operation class and current country, Hence abort the rrm operation"); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } static uint8_t *sme_rrm_get_meas_mode_string(uint8_t meas_mode) { switch (meas_mode) { CASE_RETURN_STRING(eSIR_PASSIVE_SCAN); CASE_RETURN_STRING(eSIR_ACTIVE_SCAN); CASE_RETURN_STRING(eSIR_BEACON_TABLE); default: return (uint8_t *)"UNKNOWN"; break; } } /** * sme_rrm_fill_freq_list_for_channel_load() : Trigger wide band scan request * to measure channel load * @mac_ctx: global mac structure * @sme_rrm_ctx: pointer to sme rrm context structure * @vdev: vdev common object * @req: scan request config * * Return : QDF_STATUS */ static QDF_STATUS sme_rrm_fill_freq_list_for_channel_load(struct mac_context *mac_ctx, tpRrmSMEContext sme_rrm_ctx, struct wlan_objmgr_vdev *vdev, struct scan_start_request *req) { struct bw_ind_element *bw_ind; struct wide_bw_chan_switch *wide_bw; uint16_t c_space = 0; uint8_t country_code[REG_ALPHA2_LEN + 1]; enum phy_ch_width chan_width = CH_WIDTH_INVALID; qdf_freq_t scan_freq; uint8_t channel = sme_rrm_ctx->chan_load_req_info.channel; qdf_freq_t cen320_freq = 0; rrm_get_country_code_from_connected_profile(mac_ctx, wlan_vdev_get_id(vdev), country_code); scan_freq = wlan_reg_country_chan_opclass_to_freq(mac_ctx->pdev, country_code, channel, sme_rrm_ctx->regClass, false); if (!scan_freq) { sme_debug("invalid scan freq"); return QDF_STATUS_E_INVAL; } bw_ind = &sme_rrm_ctx->chan_load_req_info.bw_ind; wide_bw = &sme_rrm_ctx->chan_load_req_info.wide_bw; if (!bw_ind->is_bw_ind_element && !wide_bw->is_wide_bw_chan_switch) { if (wlan_reg_is_6ghz_op_class(mac_ctx->pdev, sme_rrm_ctx->regClass)) c_space = wlan_reg_get_op_class_width(mac_ctx->pdev, sme_rrm_ctx->regClass, true); else c_space = wlan_reg_dmn_get_chanwidth_from_opclass_auto( country_code, channel, sme_rrm_ctx->regClass); sme_debug("op: %d, ch: %d freq:%d, cc %c%c 0x%x, c_space:%d", sme_rrm_ctx->regClass, channel, scan_freq, country_code[0], country_code[1], country_code[2], c_space); switch (c_space) { case 320: fallthrough; case 160: chan_width = CH_WIDTH_160MHZ; break; case 80: chan_width = CH_WIDTH_80MHZ; break; case 40: chan_width = CH_WIDTH_40MHZ; break; case 20: case 25: chan_width = CH_WIDTH_20MHZ; break; default: sme_err("invalid chan_space: %d", c_space); return QDF_STATUS_E_FAILURE; } } else { if (bw_ind->is_bw_ind_element) { chan_width = bw_ind->channel_width; cen320_freq = bw_ind->center_freq; } if (wide_bw->is_wide_bw_chan_switch) chan_width = wide_bw->channel_width; } sme_rrm_ctx->chan_load_req_info.req_chan_width = chan_width; return mlme_update_freq_in_scan_start_req(vdev, req, chan_width, scan_freq, cen320_freq); } /** * sme_rrm_issue_chan_load_measurement_scan() : Trigger wide band scan request * to measure channel load * @mac_ctx: global mac structure * @idx: measurement request index * * Return : QDF_STATUS */ static QDF_STATUS sme_rrm_issue_chan_load_measurement_scan(struct mac_context *mac_ctx, uint8_t idx) { QDF_STATUS status = QDF_STATUS_SUCCESS; tpRrmSMEContext sme_rrm_ctx = &mac_ctx->rrm.rrmSmeContext[idx]; uint32_t max_chan_time; struct scan_start_request *req; struct wlan_objmgr_vdev *vdev; uint32_t vdev_id; status = csr_roam_get_session_id_from_bssid(mac_ctx, &sme_rrm_ctx->sessionBssId, &vdev_id); if (QDF_IS_STATUS_ERROR(status)) { sme_err("sme session ID not found for bssid "QDF_MAC_ADDR_FMT"", QDF_MAC_ADDR_REF(sme_rrm_ctx->sessionBssId.bytes)); goto send_ind; } vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id, WLAN_LEGACY_SME_ID); if (!vdev) { sme_err("VDEV is null %d", vdev_id); status = QDF_STATUS_E_INVAL; goto send_ind; } req = qdf_mem_malloc(sizeof(*req)); if (!req) { status = QDF_STATUS_E_NOMEM; goto error_handle; } status = wlan_scan_init_default_params(vdev, req); if (QDF_IS_STATUS_ERROR(status)) goto error_handle; req->scan_req.scan_id = wlan_scan_get_scan_id(mac_ctx->psoc); sme_rrm_ctx->scan_id = req->scan_req.scan_id; req->scan_req.vdev_id = wlan_vdev_get_id(vdev); req->scan_req.scan_req_id = sme_rrm_ctx->req_id; req->scan_req.scan_f_passive = true; max_chan_time = sme_rrm_ctx->duration[0]; req->scan_req.dwell_time_passive = max_chan_time; req->scan_req.dwell_time_passive_6g = max_chan_time; req->scan_req.adaptive_dwell_time_mode = SCAN_DWELL_MODE_STATIC; req->scan_req.scan_type = SCAN_TYPE_RRM; req->scan_req.scan_f_wide_band = true; /* * FW report CCA busy for each possible 20Mhz subbands of the * wideband scan channel if below flag is true */ req->scan_req.scan_f_report_cca_busy_for_each_20mhz = true; status = sme_rrm_fill_freq_list_for_channel_load(mac_ctx, sme_rrm_ctx, vdev, req); if (QDF_IS_STATUS_ERROR(status)) goto error_handle; sme_debug("vdev:%d, rrm_idx:%d scan_id:%d num chan: %d dwell_time: %d", req->scan_req.vdev_id, sme_rrm_ctx->measurement_idx, sme_rrm_ctx->scan_id, req->scan_req.chan_list.num_chan, req->scan_req.dwell_time_passive); /* store jiffies to send it in channel load report */ sme_rrm_ctx->chan_load_req_info.rrm_scan_tsf = (uint32_t)qdf_system_ticks(); status = wlan_scan_start(req); if (QDF_IS_STATUS_ERROR(status)) goto error_handle; wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); return status; error_handle: qdf_mem_free(req); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); send_ind: sme_rrm_send_chan_load_report_xmit_ind(mac_ctx, sme_rrm_ctx, vdev_id, false); rrm_cleanup(mac_ctx, idx); return status; } /** * sme_rrm_process_chan_load_req_ind() -Process beacon report request * @mac: Global Mac structure * @msg_buf: a pointer to a buffer that maps to various structures base * on the message type.The beginning of the buffer can always * map to tSirSmeRsp. * * This is called to process the channel load report request from peer AP * forwarded through PE . * * Return : QDF_STATUS_SUCCESS - Validation is successful. */ static QDF_STATUS sme_rrm_process_chan_load_req_ind(struct mac_context *mac, void *msg_buf) { struct ch_load_ind *chan_load; tpRrmSMEContext sme_rrm_ctx; tpRrmPEContext rrm_ctx; struct channel_load_req_info *req_info; chan_load = (struct ch_load_ind *)msg_buf; sme_rrm_ctx = &mac->rrm.rrmSmeContext[chan_load->measurement_idx]; rrm_ctx = &mac->rrm.rrmPEContext; qdf_mem_copy(sme_rrm_ctx->sessionBssId.bytes, chan_load->peer_addr.bytes, sizeof(struct qdf_mac_addr)); sme_rrm_ctx->token = chan_load->dialog_token; sme_rrm_ctx->regClass = chan_load->op_class; sme_rrm_ctx->randnIntvl = QDF_MAX(chan_load->randomization_intv, mac->rrm.rrmConfig.max_randn_interval); sme_rrm_ctx->currentIndex = 0; qdf_mem_copy((uint8_t *)&sme_rrm_ctx->duration, (uint8_t *)&chan_load->meas_duration, SIR_ESE_MAX_MEAS_IE_REQS); sme_rrm_ctx->measurement_type = RRM_CHANNEL_LOAD; req_info = &sme_rrm_ctx->chan_load_req_info; req_info->channel = chan_load->channel; req_info->req_freq = chan_load->req_freq; qdf_mem_copy(&req_info->bw_ind, &chan_load->bw_ind, sizeof(req_info->bw_ind)); qdf_mem_copy(&req_info->wide_bw, &chan_load->wide_bw, sizeof(req_info->wide_bw)); sme_debug("idx:%d, token: %d randnIntvl: %d meas_duration %d, rrm_ctx dur %d reg_class: %d, type: %d, channel: %d, freq: %d, [bw_ind present: %d, cw: %d, ccfs0: %d], [wide_bw present: %d, cw: %d]", chan_load->measurement_idx, sme_rrm_ctx->token, sme_rrm_ctx->randnIntvl, chan_load->meas_duration, sme_rrm_ctx->duration[0], sme_rrm_ctx->regClass, sme_rrm_ctx->measurement_type, req_info->channel, req_info->req_freq, req_info->bw_ind.is_bw_ind_element, req_info->bw_ind.channel_width, req_info->bw_ind.center_freq, req_info->wide_bw.is_wide_bw_chan_switch, req_info->wide_bw.channel_width); return sme_rrm_issue_chan_load_measurement_scan(mac, chan_load->measurement_idx); } /** * sme_rrm_process_beacon_report_req_ind() -Process beacon report request * @mac:- Global Mac structure * @msg_buf:- a pointer to a buffer that maps to various structures base * on the message type.The beginning of the buffer can always * map to tSirSmeRsp. * * This is called to process the Beacon * report request from peer AP forwarded through PE . * * Return : QDF_STATUS_SUCCESS - Validation is successful. */ QDF_STATUS sme_rrm_process_beacon_report_req_ind(struct mac_context *mac, void *msg_buf) { tpSirBeaconReportReqInd beacon_req = (tpSirBeaconReportReqInd)msg_buf; tpRrmSMEContext sme_rrm_ctx; uint32_t len = 0, i = 0, j = 0; uint8_t country[WNI_CFG_COUNTRY_CODE_LEN]; uint32_t session_id; struct csr_roam_session *session; QDF_STATUS status; uint32_t num_chan, local_num_channel; bool chan_valid; uint32_t *rrm_freq_list, *local_rrm_freq_list; uint32_t bcn_chan_freq, local_bcn_chan_freq; tpRrmPEContext rrm_ctx; sme_rrm_ctx = &mac->rrm.rrmSmeContext[beacon_req->measurement_idx]; rrm_ctx = &mac->rrm.rrmPEContext; status = csr_roam_get_session_id_from_bssid(mac, (struct qdf_mac_addr *) beacon_req->bssId, &session_id); if (QDF_IS_STATUS_ERROR(status)) { sme_err("sme session ID not found for bssid"); goto cleanup; } session = CSR_GET_SESSION(mac, session_id); if (!session) { sme_err("Invalid session id %d", session_id); status = QDF_STATUS_E_FAILURE; goto cleanup; } rrm_get_country_code_from_connected_profile(mac, session_id, country); if (wlan_reg_is_6ghz_op_class(mac->pdev, beacon_req->channel_info.reg_class)) country[2] = OP_CLASS_GLOBAL; sme_debug("RRM_SCN: Index:%d Request Reg class %d, AP's country code %c%c 0x%x, channel = %d", beacon_req->measurement_idx, beacon_req->channel_info.reg_class, country[0], country[1], country[2], beacon_req->channel_info.chan_num); if (beacon_req->channel_list.num_channels > SIR_ESE_MAX_MEAS_IE_REQS) { sme_err("Beacon report request numChannels:%u exceeds max num channels", beacon_req->channel_list.num_channels); status = QDF_STATUS_E_INVAL; goto cleanup; } /* section 11.10.8.1 (IEEE Std 802.11k-2008) */ /* channel 0 and 255 has special meaning. */ if ((beacon_req->channel_info.chan_num == 0) || ((beacon_req->channel_info.chan_num == 255) && (beacon_req->channel_list.num_channels == 0))) { /* Add all the channel in the regulatory domain. */ len = mac->mlme_cfg->reg.valid_channel_list_num; if (sme_rrm_ctx->channelList.freq_list) { qdf_mem_free(sme_rrm_ctx->channelList.freq_list); sme_rrm_ctx->channelList.freq_list = NULL; } sme_rrm_ctx->channelList.freq_list = qdf_mem_malloc(sizeof(uint32_t) * len); if (!sme_rrm_ctx->channelList.freq_list) { status = QDF_STATUS_E_NOMEM; sme_rrm_ctx->channelList.numOfChannels = 0; goto cleanup; } csr_get_cfg_valid_channels( mac, sme_rrm_ctx->channelList.freq_list, &len); if (beacon_req->channel_info.reg_class) { if (sme_rrm_fill_scan_channels( mac, country, sme_rrm_ctx, beacon_req->channel_info.reg_class, len) != QDF_STATUS_SUCCESS) goto cleanup; } else { sme_rrm_ctx->channelList.numOfChannels = len; } } else { len = 0; sme_rrm_ctx->channelList.numOfChannels = 0; num_chan = 0; /* If valid channel is present. We first Measure on the given * channel and if there are additional channels present in * APchannelreport, measure on these also. */ if (beacon_req->channel_info.chan_num != 255) len = 1; len += beacon_req->channel_list.num_channels; if (sme_rrm_ctx->channelList.freq_list) { qdf_mem_free(sme_rrm_ctx->channelList.freq_list); sme_rrm_ctx->channelList.freq_list = NULL; } sme_rrm_ctx->channelList.freq_list = qdf_mem_malloc(sizeof(uint32_t) * len); if (!sme_rrm_ctx->channelList.freq_list) { sme_rrm_ctx->channelList.numOfChannels = 0; status = QDF_STATUS_E_NOMEM; goto cleanup; } rrm_freq_list = sme_rrm_ctx->channelList.freq_list; bcn_chan_freq = beacon_req->channel_info.chan_freq; if (beacon_req->channel_info.chan_num != 255) { chan_valid = wlan_roam_is_channel_valid(&mac->mlme_cfg->reg, bcn_chan_freq); if (chan_valid) { rrm_freq_list[num_chan] = bcn_chan_freq; num_chan++; } else { sme_err("Invalid channel: %d", beacon_req->channel_info.chan_num); } } for (i = 0; i < beacon_req->channel_list.num_channels; i++) { bcn_chan_freq = beacon_req->channel_list.chan_freq_lst[i]; chan_valid = wlan_roam_is_channel_valid(&mac->mlme_cfg->reg, bcn_chan_freq); if (chan_valid) { rrm_freq_list[num_chan] = bcn_chan_freq; num_chan++; } } sme_rrm_ctx->channelList.numOfChannels = num_chan; } local_rrm_freq_list = sme_rrm_ctx->channelList.freq_list; local_num_channel = 0; for (i = 0; i < sme_rrm_ctx->channelList.numOfChannels; i++) { local_bcn_chan_freq = local_rrm_freq_list[i]; chan_valid = true; if (beacon_req->measurement_idx > 0) { for (j = 0; j < rrm_ctx->beacon_rpt_chan_num; j++) { if (rrm_ctx->beacon_rpt_chan_list[j] == local_bcn_chan_freq) { /* * Ignore this channel, As this is already * included in previous request */ chan_valid = false; break; } } } if (chan_valid) { uint8_t beacon_rpt_chan_num; beacon_rpt_chan_num = rrm_ctx->beacon_rpt_chan_num; rrm_ctx->beacon_rpt_chan_list[beacon_rpt_chan_num] = local_bcn_chan_freq; rrm_ctx->beacon_rpt_chan_num++; if (rrm_ctx->beacon_rpt_chan_num >= MAX_NUM_CHANNELS) { /* this should never happen */ sme_err("Reset beacon_rpt_chan_num : %d", rrm_ctx->beacon_rpt_chan_num); rrm_ctx->beacon_rpt_chan_num = 0; } local_rrm_freq_list[local_num_channel] = local_bcn_chan_freq; local_num_channel++; } } if (local_num_channel == 0) goto cleanup; sme_rrm_ctx->channelList.numOfChannels = local_num_channel; /* Copy session bssid */ qdf_mem_copy(sme_rrm_ctx->sessionBssId.bytes, beacon_req->bssId, sizeof(tSirMacAddr)); /* copy measurement bssid */ qdf_mem_copy(sme_rrm_ctx->bssId, beacon_req->macaddrBssid, sizeof(tSirMacAddr)); /* Copy ssid */ qdf_mem_copy(&sme_rrm_ctx->ssId, &beacon_req->ssId, sizeof(tAniSSID)); sme_rrm_ctx->token = beacon_req->uDialogToken; sme_rrm_ctx->regClass = beacon_req->channel_info.reg_class; sme_rrm_ctx->randnIntvl = QDF_MAX(beacon_req->randomizationInterval, mac->rrm.rrmConfig.max_randn_interval); sme_rrm_ctx->currentIndex = 0; sme_rrm_ctx->msgSource = beacon_req->msgSource; qdf_mem_copy((uint8_t *)&sme_rrm_ctx->measMode, (uint8_t *)&beacon_req->fMeasurementtype, SIR_ESE_MAX_MEAS_IE_REQS); qdf_mem_copy((uint8_t *)&sme_rrm_ctx->duration, (uint8_t *)&beacon_req->measurementDuration, SIR_ESE_MAX_MEAS_IE_REQS); sme_rrm_ctx->measurement_type = RRM_BEACON_REPORT; sme_debug("token: %d randnIntvl: %d msgSource: %d measurementduration %d, rrm_ctx duration %d Meas_mode: %s, type: %d", sme_rrm_ctx->token, sme_rrm_ctx->randnIntvl, sme_rrm_ctx->msgSource, beacon_req->measurementDuration[0], sme_rrm_ctx->duration[0], sme_rrm_get_meas_mode_string(sme_rrm_ctx->measMode[0]), sme_rrm_ctx->measurement_type); return sme_rrm_issue_scan_req(mac, beacon_req->measurement_idx); cleanup: if (beacon_req->msgSource == eRRM_MSG_SOURCE_11K) { /* Copy session bssid */ qdf_mem_copy(sme_rrm_ctx->sessionBssId.bytes, beacon_req->bssId, sizeof(tSirMacAddr)); /* copy measurement bssid */ qdf_mem_copy(sme_rrm_ctx->bssId, beacon_req->macaddrBssid, sizeof(tSirMacAddr)); sme_rrm_ctx->token = beacon_req->uDialogToken; sme_rrm_ctx->regClass = beacon_req->channel_info.reg_class; sme_rrm_ctx->randnIntvl = QDF_MAX(beacon_req->randomizationInterval, mac->rrm.rrmConfig.max_randn_interval); sme_rrm_send_beacon_report_xmit_ind(mac, sme_rrm_ctx->measurement_idx, NULL, true, 0); } return status; } /** * sme_rrm_neighbor_report_request() - This is API can be used to trigger a * Neighbor report from the peer. * @sessionId: session identifier on which the request should be made. * @pNeighborReq: a pointer to a neighbor report request. * * This is API can be used to trigger a Neighbor report from the peer. * * Return: QDF_STATUS_SUCCESS - Validation is successful. */ QDF_STATUS sme_rrm_neighbor_report_request(struct mac_context *mac, uint8_t sessionId, tpRrmNeighborReq pNeighborReq, tpRrmNeighborRspCallbackInfo callbackInfo) { QDF_STATUS status = QDF_STATUS_SUCCESS; tpSirNeighborReportReqInd pMsg; sme_debug("Request to send Neighbor report request received "); if (!CSR_IS_SESSION_VALID(mac, sessionId)) { sme_err("Invalid session %d", sessionId); return QDF_STATUS_E_INVAL; } /* If already a report is pending, return failure */ if (true == mac->rrm.rrmSmeContext[0].neighborReqControlInfo. isNeighborRspPending) { sme_err("Neighbor request already pending.. Not allowed"); return QDF_STATUS_E_AGAIN; } pMsg = qdf_mem_malloc(sizeof(tSirNeighborReportReqInd)); if (!pMsg) return QDF_STATUS_E_NOMEM; rrm_ll_purge_neighbor_cache(mac, &mac->rrm.rrmSmeContext[0].neighborReportCache); pMsg->messageType = eWNI_SME_NEIGHBOR_REPORT_REQ_IND; pMsg->length = sizeof(tSirNeighborReportReqInd); wlan_mlme_get_bssid_vdev_id(mac->pdev, sessionId, (struct qdf_mac_addr *)&pMsg->bssId); pMsg->noSSID = pNeighborReq->no_ssid; qdf_mem_copy(&pMsg->ucSSID, &pNeighborReq->ssid, sizeof(tSirMacSSid)); status = umac_send_mb_message_to_mac(pMsg); if (status != QDF_STATUS_SUCCESS) return QDF_STATUS_E_FAILURE; /* Neighbor report request message sent successfully to PE. * Now register the callbacks */ mac->rrm.rrmSmeContext[0].neighborReqControlInfo. neighborRspCallbackInfo.neighborRspCallback = callbackInfo->neighborRspCallback; mac->rrm.rrmSmeContext[0].neighborReqControlInfo. neighborRspCallbackInfo.neighborRspCallbackContext = callbackInfo->neighborRspCallbackContext; mac->rrm.rrmSmeContext[0].neighborReqControlInfo.isNeighborRspPending = true; /* Start neighbor response wait timer now */ qdf_mc_timer_start(&mac->rrm.rrmSmeContext[0].neighborReqControlInfo. neighborRspWaitTimer, callbackInfo->timeout); return QDF_STATUS_SUCCESS; } /** * rrm_calculate_neighbor_ap_roam_score() - calculates roam score * @mac_ctx: mac global context * @pNeighborReportDesc: Neighbor BSS Descriptor node for which roam score * should be calculated * * This API is called while handling individual neighbor reports from the APs * neighbor AP report to calculate the cumulative roam score before storing it * in neighbor cache. * * Return: void */ static void rrm_calculate_neighbor_ap_roam_score(struct mac_context *mac_ctx, tpRrmNeighborReportDesc nbr_report_desc) { tpSirNeighborBssDescripton nbr_bss_desc; uint32_t roam_score = 0; #ifdef FEATURE_WLAN_ESE uint8_t session_id; struct cm_roam_values_copy config; #endif if (!nbr_report_desc) { QDF_ASSERT(0); return; } if (!nbr_report_desc->pNeighborBssDescription) { QDF_ASSERT(0); return; } nbr_bss_desc = nbr_report_desc->pNeighborBssDescription; if (!nbr_bss_desc->bssidInfo.rrmInfo.fMobilityDomain) goto check_11r_assoc; roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_MOBILITY_DOMAIN; if (!nbr_bss_desc->bssidInfo.rrmInfo.fSameSecurityMode) goto check_11r_assoc; roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_SECURITY; if (!nbr_bss_desc->bssidInfo.rrmInfo.fSameAuthenticator) goto check_11r_assoc; roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_KEY_SCOPE; if (!nbr_bss_desc->bssidInfo.rrmInfo.fCapRadioMeasurement) goto check_11r_assoc; roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_RRM; if (nbr_bss_desc->bssidInfo.rrmInfo.fCapSpectrumMeasurement) roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_SPECTRUM_MGMT; if (nbr_bss_desc->bssidInfo.rrmInfo.fCapQos) roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_QOS; if (nbr_bss_desc->bssidInfo.rrmInfo.fCapApsd) roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_APSD; if (nbr_bss_desc->bssidInfo.rrmInfo.fCapDelayedBlockAck) roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_DELAYED_BA; if (nbr_bss_desc->bssidInfo.rrmInfo.fCapImmediateBlockAck) roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_IMMEDIATE_BA; if (nbr_bss_desc->bssidInfo.rrmInfo.fApPreauthReachable) roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_REACHABILITY; check_11r_assoc: #ifdef FEATURE_WLAN_ESE session_id = nbr_report_desc->sessionId; wlan_cm_roam_cfg_get_value(mac_ctx->psoc, session_id, IS_11R_CONNECTION, &config); /* It has come in the report so its the best score */ if (!config.bool_value) { /* IAPP Route so lets make use of this info save all AP, as the * list does not come all the time. Save and reuse till the next * AP List comes to us. Even save our own MAC address. Will be * useful next time around. */ roam_score += RRM_ROAM_SCORE_NEIGHBOR_IAPP_LIST; } #endif nbr_report_desc->roamScore = roam_score; } /** * rrm_store_neighbor_rpt_by_roam_score()-store Neighbor BSS descriptor * @mac: Pointer to mac context * @pNeighborReportDesc - Neighbor BSS Descriptor node to be stored in cache * @index: RRM sme context index * * This API is called to store a given * Neighbor BSS descriptor to the neighbor cache. This function * stores the neighbor BSS descriptors in such a way that descriptors * are sorted by roamScore in descending order * * Return: void. */ static void rrm_store_neighbor_rpt_by_roam_score(struct mac_context *mac, tpRrmNeighborReportDesc pNeighborReportDesc, uint8_t index) { tpRrmSMEContext pSmeRrmContext = &mac->rrm.rrmSmeContext[0]; tListElem *pEntry; tRrmNeighborReportDesc *pTempNeighborReportDesc; if (!pNeighborReportDesc) { QDF_ASSERT(0); return; } if (!pNeighborReportDesc->pNeighborBssDescription) { QDF_ASSERT(0); return; } if (csr_ll_is_list_empty (&pSmeRrmContext->neighborReportCache, LL_ACCESS_LOCK)) { sme_err("Neighbor report cache is empty.. Adding a entry now"); /* Neighbor list cache is empty. Insert this entry * in the tail */ csr_ll_insert_tail(&pSmeRrmContext->neighborReportCache, &pNeighborReportDesc->List, LL_ACCESS_LOCK); return; } /* Should store the neighbor BSS description in the order * sorted by roamScore in descending order. APs with highest * roamScore should be the 1st entry in the list */ pEntry = csr_ll_peek_head(&pSmeRrmContext->neighborReportCache, LL_ACCESS_LOCK); while (pEntry) { pTempNeighborReportDesc = GET_BASE_ADDR(pEntry, tRrmNeighborReportDesc, List); if (pTempNeighborReportDesc->roamScore < pNeighborReportDesc->roamScore) break; pEntry = csr_ll_next(&pSmeRrmContext-> neighborReportCache, pEntry, LL_ACCESS_LOCK); } if (pEntry) /* This BSS roamscore is better than something in the * list. Insert this before that one */ csr_ll_insert_entry(&pSmeRrmContext->neighborReportCache, pEntry, &pNeighborReportDesc->List, LL_ACCESS_LOCK); else /* All the entries in the list has a better roam Score * than this one. Insert this at the last */ csr_ll_insert_tail(&pSmeRrmContext->neighborReportCache, &pNeighborReportDesc->List, LL_ACCESS_LOCK); } /** * sme_rrm_process_neighbor_report() -Process the Neighbor report received * from PE * @mac - Global MAC structure * @msg_buf - a pointer to a buffer that maps to various structures base * on the message type. * The beginning of the buffer can always map to tSirSmeRsp. * This is called to process the Neighbor report received from PE. * * Return: QDF_STATUS_SUCCESS - Validation is successful */ static QDF_STATUS sme_rrm_process_neighbor_report(struct mac_context *mac, void *msg_buf) { QDF_STATUS status = QDF_STATUS_SUCCESS; tpSirNeighborReportInd neighbor_rpt = (tpSirNeighborReportInd)msg_buf; tpRrmNeighborReportDesc neighbor_rpt_desc; uint8_t i = 0; QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; /* Purge the cache on reception of unsolicited neighbor report */ if (!mac->rrm.rrmSmeContext[neighbor_rpt->measurement_idx]. neighborReqControlInfo.isNeighborRspPending) rrm_ll_purge_neighbor_cache(mac, &mac->rrm.rrmSmeContext[neighbor_rpt->measurement_idx]. neighborReportCache); for (i = 0; i < neighbor_rpt->numNeighborReports; i++) { neighbor_rpt_desc = qdf_mem_malloc(sizeof(tRrmNeighborReportDesc)); if (!neighbor_rpt_desc) { status = QDF_STATUS_E_NOMEM; goto end; } neighbor_rpt_desc->pNeighborBssDescription = qdf_mem_malloc(sizeof(tSirNeighborBssDescription)); if (!neighbor_rpt_desc->pNeighborBssDescription) { qdf_mem_free(neighbor_rpt_desc); status = QDF_STATUS_E_NOMEM; goto end; } qdf_mem_copy(neighbor_rpt_desc->pNeighborBssDescription, &neighbor_rpt->sNeighborBssDescription[i], sizeof(tSirNeighborBssDescription)); sme_debug("Received neighbor report with Neighbor BSSID: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF( neighbor_rpt->sNeighborBssDescription[i].bssId)); rrm_calculate_neighbor_ap_roam_score(mac, neighbor_rpt_desc); if (neighbor_rpt_desc->roamScore > 0) { rrm_store_neighbor_rpt_by_roam_score( mac, neighbor_rpt_desc, neighbor_rpt->measurement_idx); } else { sme_err("Roam score of BSSID " QDF_MAC_ADDR_FMT " is 0, Ignoring..", QDF_MAC_ADDR_REF(neighbor_rpt-> sNeighborBssDescription[i]. bssId)); qdf_mem_free( neighbor_rpt_desc->pNeighborBssDescription); qdf_mem_free(neighbor_rpt_desc); } } end: if (!csr_ll_count( &mac->rrm.rrmSmeContext[neighbor_rpt->measurement_idx]. neighborReportCache)) qdf_status = QDF_STATUS_E_FAILURE; rrm_indicate_neighbor_report_result(mac, qdf_status); return status; } /** * sme_rrm_msg_processor()-Process RRM message * @mac - Pointer to the global MAC parameter structure. * @msg_type - the type of msg passed by PE as defined in wni_api.h * @msg_buf - a pointer to a buffer that maps to various structures base * on the message type. * The beginning of the buffer can always map to tSirSmeRsp. * sme_process_msg() calls this function for the * messages that are handled by SME RRM module. * * Return: QDF_STATUS_SUCCESS - Validation is successful. */ QDF_STATUS sme_rrm_msg_processor(struct mac_context *mac, uint16_t msg_type, void *msg_buf) { sme_debug("Msg = %d for RRM measurement", msg_type); /* switch on the msg type & make the state transition accordingly */ switch (msg_type) { case eWNI_SME_NEIGHBOR_REPORT_IND: sme_rrm_process_neighbor_report(mac, msg_buf); break; case eWNI_SME_BEACON_REPORT_REQ_IND: sme_rrm_process_beacon_report_req_ind(mac, msg_buf); break; case eWNI_SME_CHAN_LOAD_REQ_IND: sme_rrm_process_chan_load_req_ind(mac, msg_buf); break; default: sme_err("Unknown msg type: %d", msg_type); break; } return QDF_STATUS_SUCCESS; } /** * rrm_neighbor_rsp_timeout_handler() - Timer handler to handlet the timeout * @mac - The handle returned by mac_open. * * Timer handler to handle the timeout condition when a neighbor request is sent * and no neighbor response is received from the AP * * Return: NULL */ static void rrm_neighbor_rsp_timeout_handler(void *userData) { struct mac_context *mac = (struct mac_context *) userData; sme_warn("Neighbor Response timed out"); rrm_indicate_neighbor_report_result(mac, QDF_STATUS_E_FAILURE); } /** * rrm_change_default_config_param() - Changing default config param to new * @mac - The handle returned by mac_open. * * Return: None */ static void rrm_change_default_config_param(struct mac_context *mac) { mac->rrm.rrmConfig.rrm_enabled = mac->mlme_cfg->rrm_config.rrm_enabled; mac->rrm.rrmConfig.sap_rrm_enabled = mac->mlme_cfg->rrm_config.sap_rrm_enabled; mac->rrm.rrmConfig.max_randn_interval = mac->mlme_cfg->rrm_config.rrm_rand_interval; qdf_mem_copy(&mac->rrm.rrmConfig.rm_capability, &mac->mlme_cfg->rrm_config.rm_capability, RMENABLEDCAP_MAX_LEN); } /** * rrm_open() - Initialize all RRM module * @ mac: The handle returned by mac_open. * * Initialize all RRM module. * * Return: QDF_STATUS */ QDF_STATUS rrm_open(struct mac_context *mac) { QDF_STATUS qdf_status; tpRrmSMEContext pSmeRrmContext; QDF_STATUS qdf_ret_status = QDF_STATUS_SUCCESS; uint8_t i; mac->rrm.rrmConfig.max_randn_interval = 50; /* ms */ for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { pSmeRrmContext = &mac->rrm.rrmSmeContext[i]; qdf_status = qdf_mc_timer_init(&pSmeRrmContext->neighborReqControlInfo. neighborRspWaitTimer, QDF_TIMER_TYPE_SW, rrm_neighbor_rsp_timeout_handler, (void *)mac); if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { sme_err("Fail to init neighbor rsp wait timer"); return QDF_STATUS_E_FAILURE; } pSmeRrmContext->measurement_idx = i; pSmeRrmContext->neighborReqControlInfo.isNeighborRspPending = false; qdf_ret_status = csr_ll_open(&pSmeRrmContext->neighborReportCache); if (QDF_STATUS_SUCCESS != qdf_ret_status) { sme_err("Fail to open neighbor cache result"); return QDF_STATUS_E_FAILURE; } } rrm_change_default_config_param(mac); return QDF_STATUS_SUCCESS; } /** * rrm_close() - Release all RRM modules and their resources. * @mac - The handle returned by mac_open. * * Release all RRM modules and their resources. * * Return: QDF_STATUS * QDF_STATUS_E_FAILURE success * QDF_STATUS_SUCCESS failure */ QDF_STATUS rrm_close(struct mac_context *mac) { QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; tpRrmSMEContext pSmeRrmContext; uint8_t i; for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { pSmeRrmContext = &mac->rrm.rrmSmeContext[i]; if (pSmeRrmContext->channelList.freq_list) { qdf_mem_free(pSmeRrmContext->channelList.freq_list); pSmeRrmContext->channelList.freq_list = NULL; pSmeRrmContext->channelList.numOfChannels = 0; } if (QDF_TIMER_STATE_RUNNING == qdf_mc_timer_get_current_state(&pSmeRrmContext-> neighborReqControlInfo. neighborRspWaitTimer)) { qdf_status = qdf_mc_timer_stop(&pSmeRrmContext-> neighborReqControlInfo. neighborRspWaitTimer); if (QDF_IS_STATUS_ERROR(qdf_status)) sme_err("Timer stop fail"); } qdf_status = qdf_mc_timer_destroy( &pSmeRrmContext->neighborReqControlInfo. neighborRspWaitTimer); if (!QDF_IS_STATUS_SUCCESS(qdf_status)) sme_err("Fail to destroy timer"); rrm_ll_purge_neighbor_cache( mac, &pSmeRrmContext->neighborReportCache); csr_ll_close(&pSmeRrmContext->neighborReportCache); } return qdf_status; } QDF_STATUS rrm_start(struct mac_context *mac_ctx) { tpRrmSMEContext smerrmctx; wlan_scan_requester req_id; uint8_t i; /* Register with scan component */ req_id = wlan_scan_register_requester(mac_ctx->psoc, "RRM", sme_rrm_scan_event_callback, mac_ctx); for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { smerrmctx = &mac_ctx->rrm.rrmSmeContext[i]; smerrmctx->req_id = req_id; } return QDF_STATUS_SUCCESS; } QDF_STATUS rrm_stop(struct mac_context *mac_ctx) { tpRrmSMEContext smerrmctx; wlan_scan_requester req_id; uint8_t i; for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { smerrmctx = &mac_ctx->rrm.rrmSmeContext[i]; req_id = smerrmctx->req_id; smerrmctx->req_id = 0; } wlan_scan_unregister_requester(mac_ctx->psoc, req_id); return QDF_STATUS_SUCCESS; }