blob: fb29ee10db7f2dc0e36b0beb4378f219f2d95feb [file] [log] [blame]
/*
* 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;
}