| /* |
| * Copyright (c) 2016 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * 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: csr_host_scan_roam.c |
| * |
| * Host based roaming processing scan results and initiating the roaming |
| */ |
| |
| #include "wma_types.h" |
| #include "cds_mq.h" |
| #include "csr_inside_api.h" |
| #include "sms_debug.h" |
| #include "sme_qos_internal.h" |
| #include "sme_inside.h" |
| #include "host_diag_core_event.h" |
| #include "host_diag_core_log.h" |
| #include "csr_api.h" |
| #include "sme_api.h" |
| #include "csr_neighbor_roam.h" |
| #include "mac_trace.h" |
| #include "cds_concurrency.h" |
| |
| /** |
| * csr_roam_issue_reassociate() - Issue Reassociate |
| * @pMac: Global MAC Context |
| * @sessionId: SME Session ID |
| * @pSirBssDesc: BSS Descriptor |
| * @pIes: Pointer to the IE's |
| * @pProfile: Roaming profile |
| * |
| * Return: Success or Failure |
| */ |
| QDF_STATUS csr_roam_issue_reassociate(tpAniSirGlobal pMac, |
| uint32_t sessionId, tSirBssDescription *pSirBssDesc, |
| tDot11fBeaconIEs *pIes, tCsrRoamProfile *pProfile) |
| { |
| csr_roam_state_change(pMac, eCSR_ROAMING_STATE_JOINING, sessionId); |
| /* Set the roaming substate to 'join attempt'... */ |
| csr_roam_substate_change(pMac, eCSR_ROAM_SUBSTATE_REASSOC_REQ, |
| sessionId); |
| QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO, |
| FL(" calling csr_send_join_req_msg (eWNI_SME_REASSOC_REQ)")); |
| /* attempt to Join this BSS... */ |
| return csr_send_join_req_msg(pMac, sessionId, pSirBssDesc, pProfile, |
| pIes, eWNI_SME_REASSOC_REQ); |
| } |
| |
| /** |
| * csr_roam_issue_reassociate_cmd() - Issue the reassociate command |
| * @pMac: Global MAC Context |
| * @sessionId: SME Session ID |
| * |
| * Return: Success or Failure status |
| */ |
| QDF_STATUS csr_roam_issue_reassociate_cmd(tpAniSirGlobal pMac, |
| uint32_t sessionId) |
| { |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| tSmeCmd *pCommand = NULL; |
| bool fHighPriority = true; |
| bool fRemoveCmd = false; |
| tListElem *pEntry; |
| pEntry = csr_ll_peek_head(&pMac->sme.smeCmdActiveList, LL_ACCESS_LOCK); |
| if (pEntry) { |
| pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); |
| if (!pCommand) { |
| sms_log(pMac, LOGE, FL(" fail to get command buffer")); |
| return QDF_STATUS_E_RESOURCES; |
| } |
| if (eSmeCommandRoam == pCommand->command) { |
| if (pCommand->u.roamCmd.roamReason == |
| eCsrSmeIssuedAssocToSimilarAP) |
| fRemoveCmd = |
| csr_ll_remove_entry(&pMac->sme. |
| smeCmdActiveList, |
| pEntry, |
| LL_ACCESS_LOCK); |
| else |
| sms_log(pMac, LOGE, |
| FL(" Unexpected roam cmd present")); |
| if (fRemoveCmd == false) |
| pCommand = NULL; |
| } |
| } |
| if (NULL == pCommand) { |
| sms_log(pMac, LOGE, |
| FL(" fail to get cmd buf based on prev roam command")); |
| return QDF_STATUS_E_RESOURCES; |
| } |
| do { |
| /* Change the substate in case it is wait-for-key */ |
| if (CSR_IS_WAIT_FOR_KEY(pMac, sessionId)) { |
| csr_roam_stop_wait_for_key_timer(pMac); |
| csr_roam_substate_change(pMac, eCSR_ROAM_SUBSTATE_NONE, |
| sessionId); |
| } |
| pCommand->command = eSmeCommandRoam; |
| pCommand->sessionId = (uint8_t) sessionId; |
| pCommand->u.roamCmd.roamReason = eCsrSmeIssuedFTReassoc; |
| status = csr_queue_sme_command(pMac, pCommand, fHighPriority); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| sms_log(pMac, LOGE, |
| FL("fail to send message status=%d"), status); |
| csr_release_command_roam(pMac, pCommand); |
| } |
| } while (0); |
| |
| return status; |
| } |
| |
| /** |
| * csr_neighbor_roam_process_scan_results() - build roaming candidate list |
| * |
| * @mac_ctx: The handle returned by mac_open. |
| * @sessionid: Session information |
| * @scan_results_list: List obtained from csr_scan_get_result() |
| * |
| * This function applies various candidate checks like LFR, 11r, preauth, ESE |
| * and builds a roamable AP list. It applies age limit only if no suitable |
| * recent candidates are found. |
| * |
| * Output list is built in mac_ctx->roam.neighborRoamInfo[sessionid]. |
| * |
| * Return: void |
| */ |
| |
| static void |
| csr_neighbor_roam_process_scan_results(tpAniSirGlobal mac_ctx, |
| uint8_t sessionid, |
| tScanResultHandle *scan_results_list) |
| { |
| tCsrScanResultInfo *scan_result; |
| tpCsrNeighborRoamControlInfo n_roam_info = |
| &mac_ctx->roam.neighborRoamInfo[sessionid]; |
| tpCsrNeighborRoamBSSInfo bss_info; |
| uint32_t cur_ap_rssi; |
| uint32_t age_ticks = 0; |
| uint32_t limit_ticks = |
| qdf_system_msecs_to_ticks(ROAM_AP_AGE_LIMIT_MS); |
| uint8_t num_candidates = 0; |
| uint8_t num_dropped = 0; |
| /* |
| * first iteration of scan list should consider |
| * age constraint for candidates |
| */ |
| bool age_constraint = true; |
| #ifdef FEATURE_WLAN_ESE |
| uint16_t qpresent; |
| uint16_t qavail; |
| bool voadmitted; |
| #endif |
| /* |
| * Find out the Current AP RSSI and keep it handy to check if |
| * it is better than the RSSI of the AP which we are |
| * going to roam.If so, we are going to continue with the |
| * current AP. |
| */ |
| cur_ap_rssi = csr_get_current_ap_rssi(mac_ctx, scan_results_list, |
| sessionid); |
| |
| /* |
| * Expecting the scan result already to be in the sorted order based on |
| * RSSI. Based on the previous state we need to check whether the list |
| * should be sorted again taking neighbor score into consideration. If |
| * previous state is CFG_CHAN_LIST_SCAN, there should not be a neighbor |
| * score associated with any of the BSS. If the previous state is |
| * REPORT_QUERY, then there will be neighbor score for each of the APs. |
| * For now, let us take top of the list provided as it is by CSR Scan |
| * result API. Hence it is assumed that neighbor score and rssi score |
| * are in the same order. This will be taken care later. |
| */ |
| |
| do { |
| while (true) { |
| tSirBssDescription *descr; |
| |
| scan_result = csr_scan_result_get_next( |
| mac_ctx, *scan_results_list); |
| if (NULL == scan_result) |
| break; |
| descr = &scan_result->BssDescriptor; |
| QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, |
| FL("Scan result: BSSID " MAC_ADDRESS_STR |
| " (Rssi %ld, Ch:%d)"), |
| MAC_ADDR_ARRAY(descr->bssId), |
| abs(descr->rssi), descr->channelId); |
| |
| if (!qdf_mem_cmp(descr->bssId, |
| n_roam_info->currAPbssid.bytes, |
| sizeof(tSirMacAddr))) { |
| /* |
| * currently associated AP. Do not have this |
| * in the roamable AP list |
| */ |
| QDF_TRACE(QDF_MODULE_ID_SME, |
| QDF_TRACE_LEVEL_INFO, |
| "SKIP-currently associated AP"); |
| continue; |
| } |
| /* |
| * In case of reassoc requested by upper layer, look |
| * for exact match of bssid & channel. csr cache might |
| * have duplicates |
| */ |
| if ((n_roam_info->uOsRequestedHandoff) && |
| ((qdf_mem_cmp(descr->bssId, |
| n_roam_info->handoffReqInfo.bssid.bytes, |
| sizeof(tSirMacAddr))) |
| || (descr->channelId != |
| n_roam_info->handoffReqInfo.channel))) { |
| QDF_TRACE(QDF_MODULE_ID_SME, |
| QDF_TRACE_LEVEL_INFO, |
| "SKIP-not a candidate AP for OS requested roam"); |
| continue; |
| } |
| |
| if ((n_roam_info->is11rAssoc) && |
| (!csr_neighbor_roam_is_preauth_candidate(mac_ctx, |
| sessionid, descr->bssId))) { |
| sms_log(mac_ctx, LOGE, |
| FL("BSSID in preauth faillist.Ignore")); |
| continue; |
| } |
| |
| #ifdef FEATURE_WLAN_ESE |
| if (!csr_roam_is_roam_offload_scan_enabled(mac_ctx) && |
| (n_roam_info->isESEAssoc) && |
| !csr_neighbor_roam_is_preauth_candidate(mac_ctx, |
| sessionid, descr->bssId)) { |
| sms_log(mac_ctx, LOGE, |
| FL("BSSID in preauth faillist.Ignore")); |
| continue; |
| } |
| |
| qpresent = descr->QBSSLoad_present; |
| qavail = descr->QBSSLoad_avail; |
| voadmitted = n_roam_info->isVOAdmitted; |
| if (voadmitted) |
| sms_log(mac_ctx, LOG1, |
| FL("New QBSS=%s,BWavail=0x%x,req=0x%x"), |
| ((qpresent) ? "yes" : "no"), qavail, |
| n_roam_info->MinQBssLoadRequired); |
| if (voadmitted && qpresent && |
| (qavail < n_roam_info->MinQBssLoadRequired)) { |
| QDF_TRACE(QDF_MODULE_ID_SME, |
| QDF_TRACE_LEVEL_INFO, |
| "BSSID:" MAC_ADDRESS_STR "has no BW", |
| MAC_ADDR_ARRAY(descr->bssId)); |
| continue; |
| } |
| if (voadmitted && !qpresent) { |
| QDF_TRACE(QDF_MODULE_ID_SME, |
| QDF_TRACE_LEVEL_INFO, |
| "BSSID:" MAC_ADDRESS_STR "no LOAD IE", |
| MAC_ADDR_ARRAY(descr->bssId)); |
| continue; |
| } |
| #endif /* FEATURE_WLAN_ESE */ |
| |
| /* |
| * If we are supporting legacy roaming, and |
| * if the candidate is on the "pre-auth failed" list, |
| * ignore it. |
| */ |
| if (csr_roam_is_fast_roam_enabled(mac_ctx, sessionid) && |
| !csr_neighbor_roam_is_preauth_candidate(mac_ctx, |
| sessionid, descr->bssId)) { |
| sms_log(mac_ctx, LOGE, |
| FL("BSSID in preauth faillist Ignore")); |
| continue; |
| } |
| |
| /* check the age of the AP */ |
| age_ticks = (uint32_t) qdf_mc_timer_get_system_ticks() - |
| descr->nReceivedTime; |
| if (age_constraint == true && age_ticks > limit_ticks) { |
| num_dropped++; |
| QDF_TRACE(QDF_MODULE_ID_SME, |
| QDF_TRACE_LEVEL_WARN, |
| FL("Old AP (probe rsp/beacon) skipped.") |
| ); |
| continue; |
| } |
| |
| /* Finished all checks, now add it to candidate list */ |
| bss_info = |
| qdf_mem_malloc(sizeof(tCsrNeighborRoamBSSInfo)); |
| if (NULL == bss_info) { |
| sms_log(mac_ctx, LOGE, |
| FL("Memory alloc fail. Ignored cnd.")); |
| continue; |
| } |
| bss_info->pBssDescription = |
| qdf_mem_malloc(descr->length + |
| sizeof(descr->length)); |
| if (bss_info->pBssDescription != NULL) { |
| qdf_mem_copy(bss_info->pBssDescription, descr, |
| descr->length + sizeof(descr->length)); |
| } else { |
| sms_log(mac_ctx, LOGE, |
| FL("Mem alloc failed. Ignore cand.")); |
| qdf_mem_free(bss_info); |
| continue; |
| } |
| /* |
| * Assign some preference value for now. Need to |
| * calculate theactual score based on RSSI and neighbor |
| * AP score |
| */ |
| bss_info->apPreferenceVal = 10; |
| num_candidates++; |
| csr_ll_insert_tail(&n_roam_info->roamableAPList, |
| &bss_info->List, LL_ACCESS_LOCK); |
| } /* end of while (csr_scan_result_get_next) */ |
| |
| /* if some candidates were found, then no need to repeat */ |
| if (num_candidates) |
| break; |
| /* |
| * if age_constraint is already false, we have done two |
| * iterations and no candidate were found |
| */ |
| if (age_constraint == false) { |
| QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, |
| "%s: No roam able candidates found", |
| __func__); |
| break; |
| } |
| /* |
| * if all candidates were dropped rescan the scan |
| * list but this time without age constraint. |
| */ |
| age_constraint = false; |
| /* if no candidates were dropped no need to repeat */ |
| } while (num_dropped); |
| |
| /* |
| * Now we have all the scan results in our local list. Good time to free |
| * up the the list we got as a part of csrGetScanResult |
| */ |
| csr_scan_result_purge(mac_ctx, *scan_results_list); |
| } |
| |
| /** |
| * csr_neighbor_roam_trigger_handoff() - Start roaming |
| * @mac_ctx: Global MAC Context |
| * @session_id: SME Session ID |
| * |
| * Return: None |
| */ |
| static void csr_neighbor_roam_trigger_handoff(tpAniSirGlobal mac_ctx, |
| uint8_t session_id) |
| { |
| if (csr_roam_is_fast_roam_enabled(mac_ctx, session_id)) |
| csr_neighbor_roam_issue_preauth_req(mac_ctx, session_id); |
| else |
| sms_log(mac_ctx, LOGE, FL("Roaming is disabled")); |
| } |
| |
| /** |
| * csr_neighbor_roam_process_scan_complete() - Post process the scan results |
| * @pMac: Global MAC Context |
| * @sessionId: SME Session ID |
| * |
| * Return: Success or Failure |
| */ |
| QDF_STATUS csr_neighbor_roam_process_scan_complete(tpAniSirGlobal pMac, |
| uint8_t sessionId) |
| { |
| tpCsrNeighborRoamControlInfo pNeighborRoamInfo = |
| &pMac->roam.neighborRoamInfo[sessionId]; |
| tCsrScanResultFilter scanFilter; |
| tScanResultHandle scanResult; |
| uint32_t tempVal = 0; |
| QDF_STATUS hstatus; |
| |
| hstatus = csr_neighbor_roam_prepare_scan_profile_filter(pMac, |
| &scanFilter, |
| sessionId); |
| sms_log(pMac, LOGW, |
| FL("Prepare scan to find neighbor AP filter status = %d"), |
| hstatus); |
| if (QDF_STATUS_SUCCESS != hstatus) { |
| sms_log(pMac, LOGE, |
| FL("Scan Filter prep fail for Assoc %d Bail out"), |
| tempVal); |
| return QDF_STATUS_E_FAILURE; |
| } |
| hstatus = csr_scan_get_result(pMac, &scanFilter, &scanResult); |
| if (hstatus != QDF_STATUS_SUCCESS) { |
| sms_log(pMac, LOGE, |
| FL("Get Scan Result status code %d"), |
| hstatus); |
| } |
| /* Process the scan results and update roamable AP list */ |
| csr_neighbor_roam_process_scan_results(pMac, sessionId, &scanResult); |
| |
| /* Free the scan filter */ |
| csr_free_scan_filter(pMac, &scanFilter); |
| |
| tempVal = csr_ll_count(&pNeighborRoamInfo->roamableAPList); |
| |
| if (tempVal) { |
| csr_neighbor_roam_trigger_handoff(pMac, sessionId); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| if (csr_roam_is_roam_offload_scan_enabled(pMac)) { |
| if (pNeighborRoamInfo->uOsRequestedHandoff) { |
| csr_roam_offload_scan(pMac, sessionId, |
| ROAM_SCAN_OFFLOAD_START, |
| REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW); |
| pNeighborRoamInfo->uOsRequestedHandoff = 0; |
| } else { |
| /* There is no candidate or We are not roaming Now. |
| * Inform the FW to restart Roam Offload Scan */ |
| csr_roam_offload_scan(pMac, sessionId, |
| ROAM_SCAN_OFFLOAD_RESTART, |
| REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW); |
| } |
| csr_neighbor_roam_state_transition(pMac, |
| eCSR_NEIGHBOR_ROAM_STATE_CONNECTED, sessionId); |
| } |
| return QDF_STATUS_SUCCESS; |
| |
| } |
| |
| /** |
| * csr_neighbor_roam_candidate_found_ind_hdlr() |
| * |
| * @mac_ctx: Pointer to Global MAC structure |
| * @msg_buf: pointer to msg buff |
| * |
| * This function is called by CSR as soon as TL posts the candidate |
| * found indication to SME via MC thread |
| * |
| * Return: QDF_STATUS_SUCCESS on success, corresponding error code otherwise |
| */ |
| QDF_STATUS csr_neighbor_roam_candidate_found_ind_hdlr(tpAniSirGlobal pMac, |
| void *pMsg) |
| { |
| tSirSmeCandidateFoundInd *pSirSmeCandidateFoundInd = |
| (tSirSmeCandidateFoundInd *) pMsg; |
| uint32_t sessionId = pSirSmeCandidateFoundInd->sessionId; |
| tpCsrNeighborRoamControlInfo pNeighborRoamInfo = |
| &pMac->roam.neighborRoamInfo[sessionId]; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| sms_log(pMac, LOG1, FL("Received indication from firmware")); |
| |
| /* we must be in connected state, if not ignore it */ |
| if ((eCSR_NEIGHBOR_ROAM_STATE_CONNECTED != |
| pNeighborRoamInfo->neighborRoamState) |
| || (pNeighborRoamInfo->uOsRequestedHandoff)) { |
| sms_log(pMac, LOGE, |
| FL("Recvd in NotCONNECTED or OsReqHandoff. Ignore")); |
| status = QDF_STATUS_E_FAILURE; |
| } else { |
| /* Firmware indicated that roaming candidate is found. Beacons |
| * are already in the SME scan results table. |
| * Process the results for choosing best roaming candidate. |
| */ |
| csr_save_scan_results(pMac, eCsrScanCandidateFound, |
| sessionId); |
| /* Future enhancements: |
| * If firmware tags candidate beacons, give them preference |
| * for roaming. |
| * Age out older entries so that new candidate beacons |
| * will get preference. |
| */ |
| status = csr_neighbor_roam_process_scan_complete(pMac, |
| sessionId); |
| if (QDF_STATUS_SUCCESS != status) { |
| sms_log(pMac, LOGE, |
| FL("scan process complete failed, status %d"), |
| status); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } |
| |
| return status; |
| } |
| |
| /** |
| * csr_neighbor_roam_free_roamable_bss_list() - Frees roamable APs list |
| * @mac_ctx: The handle returned by mac_open. |
| * @llist: Neighbor Roam BSS List to be emptied |
| * |
| * Empties and frees all the nodes in the roamable AP list |
| * |
| * Return: none |
| */ |
| void csr_neighbor_roam_free_roamable_bss_list(tpAniSirGlobal mac_ctx, |
| tDblLinkList *llist) |
| { |
| tpCsrNeighborRoamBSSInfo result = NULL; |
| |
| sms_log(mac_ctx, LOG2, |
| FL("Emptying the BSS list. Current count = %d"), |
| csr_ll_count(llist)); |
| |
| /* |
| * Pick up the head, remove and free the node till |
| * the list becomes empty |
| */ |
| while ((result = csr_neighbor_roam_next_roamable_ap(mac_ctx, llist, |
| NULL)) != NULL) { |
| csr_neighbor_roam_remove_roamable_ap_list_entry(mac_ctx, |
| llist, result); |
| csr_neighbor_roam_free_neighbor_roam_bss_node(mac_ctx, result); |
| } |
| return; |
| } |
| |
| /** |
| * csr_neighbor_roam_remove_roamable_ap_list_entry() |
| * |
| * @mac_ctx: Pointer to Global MAC structure |
| * @pList: The list from which the entry should be removed |
| * @pNeighborEntry: Neighbor Roam BSS Node to be removed |
| * |
| * This function removes a given entry from the given list |
| * |
| * Return: true if successfully removed, else false |
| */ |
| bool csr_neighbor_roam_remove_roamable_ap_list_entry(tpAniSirGlobal pMac, |
| tDblLinkList *pList, |
| tpCsrNeighborRoamBSSInfo |
| pNeighborEntry) |
| { |
| if (pList) { |
| return csr_ll_remove_entry(pList, &pNeighborEntry->List, |
| LL_ACCESS_LOCK); |
| } |
| |
| sms_log(pMac, LOGE, |
| FL("Remove neigh BSS node from fail list. Current count = %d"), |
| csr_ll_count(pList)); |
| |
| return false; |
| } |
| |
| /** |
| * csr_neighbor_roam_next_roamable_ap() - Get next AP from roamable AP list |
| * @mac_ctx - The handle returned by mac_open. |
| * @plist - The list from which the entry should be returned |
| * @neighbor_entry - Neighbor Roam BSS Node whose next entry should be returned |
| * |
| * Gets the entry next to passed entry. If NULL is passed, return the entry |
| * in the head of the list |
| * |
| * Return: Neighbor Roam BSS Node to be returned |
| */ |
| tpCsrNeighborRoamBSSInfo csr_neighbor_roam_next_roamable_ap( |
| tpAniSirGlobal mac_ctx, tDblLinkList *llist, |
| tpCsrNeighborRoamBSSInfo neighbor_entry) |
| { |
| tListElem *entry = NULL; |
| tpCsrNeighborRoamBSSInfo result = NULL; |
| |
| if (llist) { |
| if (NULL == neighbor_entry) |
| entry = csr_ll_peek_head(llist, LL_ACCESS_LOCK); |
| else |
| entry = csr_ll_next(llist, &neighbor_entry->List, |
| LL_ACCESS_LOCK); |
| if (entry) |
| result = GET_BASE_ADDR(entry, tCsrNeighborRoamBSSInfo, |
| List); |
| } |
| |
| return result; |
| } |
| |
| |
| /** |
| * csr_neighbor_roam_request_handoff() - Handoff to a different AP |
| * @mac_ctx: Pointer to Global MAC structure |
| * @session_id: Session ID |
| * |
| * This function triggers actual switching from one AP to the new AP. |
| * It issues disassociate with reason code as Handoff and CSR as a part of |
| * handling disassoc rsp, issues reassociate to the new AP |
| * |
| * Return: none |
| */ |
| void csr_neighbor_roam_request_handoff(tpAniSirGlobal mac_ctx, |
| uint8_t session_id) |
| { |
| tCsrRoamInfo roam_info; |
| tpCsrNeighborRoamControlInfo neighbor_roam_info = |
| &mac_ctx->roam.neighborRoamInfo[session_id]; |
| tCsrNeighborRoamBSSInfo handoff_node; |
| uint32_t roamid = 0; |
| QDF_STATUS status; |
| |
| QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, "%s session_id=%d", |
| __func__, session_id); |
| |
| if (neighbor_roam_info->neighborRoamState != |
| eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE) { |
| sms_log(mac_ctx, LOGE, |
| FL("Roam requested when Neighbor roam is in %s state"), |
| mac_trace_get_neighbour_roam_state( |
| neighbor_roam_info->neighborRoamState)); |
| return; |
| } |
| |
| if (false == csr_neighbor_roam_get_handoff_ap_info(mac_ctx, |
| &handoff_node, session_id)) { |
| QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, |
| FL("failed to obtain handoff AP")); |
| return; |
| } |
| QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, |
| FL("HANDOFF CANDIDATE BSSID "MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(handoff_node.pBssDescription->bssId)); |
| |
| qdf_mem_zero(&roam_info, sizeof(tCsrRoamInfo)); |
| csr_roam_call_callback(mac_ctx, session_id, &roam_info, roamid, |
| eCSR_ROAM_FT_START, eSIR_SME_SUCCESS); |
| |
| qdf_mem_zero(&roam_info, sizeof(tCsrRoamInfo)); |
| csr_neighbor_roam_state_transition(mac_ctx, |
| eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING, session_id); |
| |
| csr_neighbor_roam_send_lfr_metric_event(mac_ctx, session_id, |
| handoff_node.pBssDescription->bssId, |
| eCSR_ROAM_HANDOVER_SUCCESS); |
| /* Free the profile.. Just to make sure we dont leak memory here */ |
| csr_release_profile(mac_ctx, |
| &neighbor_roam_info->csrNeighborRoamProfile); |
| /* |
| * Create the Handoff AP profile. Copy the currently connected profile |
| * and update only the BSSID and channel number. This should happen |
| * before issuing disconnect |
| */ |
| status = csr_roam_copy_connected_profile(mac_ctx, session_id, |
| &neighbor_roam_info->csrNeighborRoamProfile); |
| if (QDF_STATUS_SUCCESS != status) { |
| QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, |
| FL("csr_roam_copy_connected_profile failed %d"), |
| status); |
| return; |
| } |
| qdf_mem_copy(neighbor_roam_info->csrNeighborRoamProfile.BSSIDs.bssid, |
| handoff_node.pBssDescription->bssId, sizeof(tSirMacAddr)); |
| neighbor_roam_info->csrNeighborRoamProfile.ChannelInfo.ChannelList[0] = |
| handoff_node.pBssDescription->channelId; |
| |
| sms_log(mac_ctx, LOGW, |
| " csr_roamHandoffRequested: disassociating with current AP"); |
| |
| if (!QDF_IS_STATUS_SUCCESS |
| (csr_roam_issue_disassociate_cmd |
| (mac_ctx, session_id, |
| eCSR_DISCONNECT_REASON_HANDOFF))) { |
| sms_log(mac_ctx, LOGW, |
| "csr_roamHandoffRequested: fail to issue disassoc"); |
| return; |
| } |
| /* notify HDD for handoff, providing the BSSID too */ |
| roam_info.reasonCode = eCsrRoamReasonBetterAP; |
| |
| qdf_mem_copy(roam_info.bssid.bytes, |
| handoff_node.pBssDescription->bssId, |
| sizeof(struct qdf_mac_addr)); |
| |
| csr_roam_call_callback(mac_ctx, session_id, &roam_info, 0, |
| eCSR_ROAM_ROAMING_START, eCSR_ROAM_RESULT_NONE); |
| |
| return; |
| } |
| |
| |
| /** |
| * csr_neighbor_roam_get_handoff_ap_info - Identifies the best AP for roaming |
| * |
| * @pMac: mac context |
| * @session_id: Session Id |
| * @hand_off_node: AP node that is the handoff candidate returned |
| * |
| * This function returns the best possible AP for handoff. For 11R case, it |
| * returns the 1st entry from pre-auth done list. For non-11r case, it returns |
| * the 1st entry from roamable AP list |
| * |
| * Return: true if able find handoff AP, false otherwise |
| */ |
| |
| bool csr_neighbor_roam_get_handoff_ap_info(tpAniSirGlobal pMac, |
| tpCsrNeighborRoamBSSInfo hand_off_node, |
| uint8_t session_id) |
| { |
| tpCsrNeighborRoamControlInfo ngbr_roam_info = |
| &pMac->roam.neighborRoamInfo[session_id]; |
| tpCsrNeighborRoamBSSInfo bss_node = NULL; |
| |
| if (NULL == hand_off_node) { |
| QDF_ASSERT(NULL != hand_off_node); |
| return false; |
| } |
| if (ngbr_roam_info->is11rAssoc) { |
| /* Always the BSS info in the head is the handoff candidate */ |
| bss_node = csr_neighbor_roam_next_roamable_ap( |
| pMac, |
| &ngbr_roam_info->FTRoamInfo.preAuthDoneList, |
| NULL); |
| sms_log(pMac, LOG1, |
| FL("Number of Handoff candidates = %d"), |
| csr_ll_count(& |
| ngbr_roam_info->FTRoamInfo.preAuthDoneList)); |
| } else |
| #ifdef FEATURE_WLAN_ESE |
| if (ngbr_roam_info->isESEAssoc) { |
| /* Always the BSS info in the head is the handoff candidate */ |
| bss_node = |
| csr_neighbor_roam_next_roamable_ap(pMac, |
| &ngbr_roam_info->FTRoamInfo.preAuthDoneList, |
| NULL); |
| sms_log(pMac, LOG1, |
| FL("Number of Handoff candidates = %d"), |
| csr_ll_count(&ngbr_roam_info->FTRoamInfo. |
| preAuthDoneList)); |
| } else |
| #endif |
| if (csr_roam_is_fast_roam_enabled(pMac, session_id)) { |
| /* Always the BSS info in the head is the handoff candidate */ |
| bss_node = |
| csr_neighbor_roam_next_roamable_ap(pMac, |
| &ngbr_roam_info->FTRoamInfo.preAuthDoneList, |
| NULL); |
| sms_log(pMac, LOG1, |
| FL("Number of Handoff candidates = %d"), |
| csr_ll_count( |
| &ngbr_roam_info->FTRoamInfo.preAuthDoneList)); |
| } else { |
| bss_node = |
| csr_neighbor_roam_next_roamable_ap(pMac, |
| &ngbr_roam_info->roamableAPList, |
| NULL); |
| sms_log(pMac, LOG1, |
| FL("Number of Handoff candidates = %d"), |
| csr_ll_count(&ngbr_roam_info->roamableAPList)); |
| } |
| if (NULL == bss_node) |
| return false; |
| qdf_mem_copy(hand_off_node, bss_node, sizeof(tCsrNeighborRoamBSSInfo)); |
| return true; |
| } |
| |
| /** |
| * csr_neighbor_roam_is_handoff_in_progress() |
| * |
| * @mac_ctx: Pointer to Global MAC structure |
| * @session_id: Session ID |
| * |
| * This function returns whether handoff is in progress or not based on |
| * the current neighbor roam state |
| * |
| * Return: true if reassoc in progress, false otherwise |
| */ |
| bool csr_neighbor_roam_is_handoff_in_progress(tpAniSirGlobal pMac, |
| uint8_t sessionId) |
| { |
| if (eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING == |
| pMac->roam.neighborRoamInfo[sessionId].neighborRoamState) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * csr_neighbor_roam_free_neighbor_roam_bss_node() |
| * |
| * @mac_ctx: Pointer to Global MAC structure |
| * @neighborRoamBSSNode: Neighbor Roam BSS Node to be freed |
| * |
| * This function frees all the internal pointers CSR NeighborRoam BSS Info |
| * and also frees the node itself |
| * |
| * Return: None |
| */ |
| void csr_neighbor_roam_free_neighbor_roam_bss_node(tpAniSirGlobal pMac, |
| tpCsrNeighborRoamBSSInfo |
| neighborRoamBSSNode) |
| { |
| if (neighborRoamBSSNode) { |
| if (neighborRoamBSSNode->pBssDescription) { |
| qdf_mem_free(neighborRoamBSSNode->pBssDescription); |
| neighborRoamBSSNode->pBssDescription = NULL; |
| } |
| qdf_mem_free(neighborRoamBSSNode); |
| neighborRoamBSSNode = NULL; |
| } |
| |
| return; |
| } |
| |