blob: 31eb8df56414443fd29ac8064b850d57c2abb1ef [file] [log] [blame]
/*
* Copyright (c) 2011-2019 The Linux Foundation. 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: csr_api_scan.c
*
* Implementation for the Common Scan interfaces.
*/
#include "ani_global.h"
#include "csr_inside_api.h"
#include "sme_inside.h"
#include "csr_support.h"
#include "host_diag_core_log.h"
#include "host_diag_core_event.h"
#include "cds_reg_service.h"
#include "wma_types.h"
#include "cds_utils.h"
#include "wma.h"
#include "wlan_policy_mgr_api.h"
#include "wlan_hdd_main.h"
#include "pld_common.h"
#include "csr_internal.h"
#include <wlan_scan_ucfg_api.h>
#include <wlan_scan_api.h>
#include <wlan_scan_utils_api.h>
#include <wlan_objmgr_vdev_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_utility.h>
#include "wlan_reg_services_api.h"
#include "sch_api.h"
static void csr_set_cfg_valid_channel_list(struct mac_context *mac,
uint8_t *pChannelList,
uint8_t NumChannels);
static void csr_save_tx_power_to_cfg(struct mac_context *mac,
tDblLinkList *pList,
uint32_t cfgId);
static void csr_set_cfg_country_code(struct mac_context *mac,
uint8_t *countryCode);
static void csr_purge_channel_power(struct mac_context *mac,
tDblLinkList *pChannelList);
static bool csr_roam_is_valid_channel(struct mac_context *mac, uint8_t channel);
/* pResult is invalid calling this function. */
void csr_free_scan_result_entry(struct mac_context *mac,
struct tag_csrscan_result *pResult)
{
if (pResult->Result.pvIes)
qdf_mem_free(pResult->Result.pvIes);
qdf_mem_free(pResult);
}
static QDF_STATUS csr_ll_scan_purge_result(struct mac_context *mac,
tDblLinkList *pList)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
tListElem *pEntry;
struct tag_csrscan_result *pBssDesc;
while ((pEntry = csr_ll_remove_head(pList, LL_ACCESS_NOLOCK)) != NULL) {
pBssDesc = GET_BASE_ADDR(pEntry, struct tag_csrscan_result,
Link);
csr_free_scan_result_entry(mac, pBssDesc);
}
return status;
}
QDF_STATUS csr_scan_open(struct mac_context *mac_ctx)
{
csr_ll_open(&mac_ctx->scan.channelPowerInfoList24);
csr_ll_open(&mac_ctx->scan.channelPowerInfoList5G);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS csr_scan_close(struct mac_context *mac)
{
csr_purge_channel_power(mac, &mac->scan.channelPowerInfoList24);
csr_purge_channel_power(mac, &mac->scan.channelPowerInfoList5G);
csr_ll_close(&mac->scan.channelPowerInfoList24);
csr_ll_close(&mac->scan.channelPowerInfoList5G);
ucfg_scan_psoc_set_disable(mac->psoc, REASON_SYSTEM_DOWN);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS csr_scan_handle_search_for_ssid(struct mac_context *mac_ctx,
uint32_t session_id)
{
QDF_STATUS status = QDF_STATUS_E_FAILURE;
tScanResultHandle hBSSList = CSR_INVALID_SCANRESULT_HANDLE;
tCsrScanResultFilter *pScanFilter = NULL;
struct csr_roam_profile *profile;
struct csr_roam_session *session;
session = CSR_GET_SESSION(mac_ctx, session_id);
if (!session) {
sme_err("session %d not found", session_id);
return QDF_STATUS_E_FAILURE;
}
profile = session->scan_info.profile;
sme_debug("session %d", session_id);
do {
/* If this scan is for HDD reassociate */
if (mac_ctx->roam.neighborRoamInfo[session_id].
uOsRequestedHandoff) {
/* notify LFR state m/c */
status = csr_neighbor_roam_sssid_scan_done
(mac_ctx, session_id, QDF_STATUS_SUCCESS);
if (!QDF_IS_STATUS_SUCCESS(status))
csr_neighbor_roam_start_lfr_scan(mac_ctx,
session_id);
status = QDF_STATUS_SUCCESS;
break;
}
/*
* If there is roam command waiting, ignore this roam because
* the newer roam command is the one to execute
*/
if (csr_is_roam_command_waiting_for_session(mac_ctx, session_id)) {
sme_warn("aborts because roam command waiting");
break;
}
if (!profile)
break;
pScanFilter = qdf_mem_malloc(sizeof(tCsrScanResultFilter));
if (!pScanFilter) {
status = QDF_STATUS_E_NOMEM;
break;
}
status = csr_roam_prepare_filter_from_profile(mac_ctx, profile,
pScanFilter);
if (!QDF_IS_STATUS_SUCCESS(status))
break;
status = csr_scan_get_result(mac_ctx, pScanFilter, &hBSSList);
if (!QDF_IS_STATUS_SUCCESS(status))
break;
if (mac_ctx->roam.roamSession[session_id].connectState ==
eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTING) {
sme_err("upper layer issued disconnetion");
status = QDF_STATUS_E_FAILURE;
break;
}
status = csr_roam_issue_connect(mac_ctx, session_id, profile,
hBSSList, eCsrHddIssued,
session->scan_info.roam_id,
true, true);
hBSSList = CSR_INVALID_SCANRESULT_HANDLE;
} while (0);
if (!QDF_IS_STATUS_SUCCESS(status)) {
if (CSR_INVALID_SCANRESULT_HANDLE != hBSSList) {
csr_scan_result_purge(mac_ctx, hBSSList);
}
/* We haven't done anything to this profile */
csr_roam_call_callback(mac_ctx, session_id, NULL,
session->scan_info.roam_id,
eCSR_ROAM_ASSOCIATION_FAILURE,
eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE);
}
if (pScanFilter) {
csr_free_scan_filter(mac_ctx, pScanFilter);
qdf_mem_free(pScanFilter);
}
return status;
}
/**
* csr_handle_fils_scan_for_ssid_failure() - Checks and fills FILS seq number
* in roam_info structure to send to hdd
*
* @roam_profile: Pointer to current roam_profile structure
* @roam_info: Pointer to roam_info strucure to be filled
*
* Return: true for FILS connection else false
*/
#ifdef WLAN_FEATURE_FILS_SK
static bool
csr_handle_fils_scan_for_ssid_failure(struct csr_roam_profile *roam_profile,
struct csr_roam_info *roam_info)
{
if (roam_profile && roam_profile->fils_con_info &&
roam_profile->fils_con_info->is_fils_connection) {
sme_debug("send roam_info for FILS connection failure, seq %d",
roam_profile->fils_con_info->sequence_number);
roam_info->is_fils_connection = true;
roam_info->fils_seq_num =
roam_profile->fils_con_info->sequence_number;
return true;
}
return false;
}
#else
static bool
csr_handle_fils_scan_for_ssid_failure(struct csr_roam_profile *roam_profile,
struct csr_roam_info *roam_info)
{
return false;
}
#endif
QDF_STATUS csr_scan_handle_search_for_ssid_failure(struct mac_context *mac_ctx,
uint32_t session_id)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct csr_roam_profile *profile;
struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id);
eCsrRoamResult roam_result;
struct csr_roam_info *roam_info = NULL;
struct tag_csrscan_result *scan_result;
if (!session) {
sme_err("session %d not found", session_id);
return QDF_STATUS_E_FAILURE;
}
/* If this scan is for HDD reassociate */
if (mac_ctx->roam.neighborRoamInfo[session_id].uOsRequestedHandoff) {
/* notify LFR state m/c */
status = csr_neighbor_roam_sssid_scan_done
(mac_ctx, session_id, QDF_STATUS_E_FAILURE);
if (!QDF_IS_STATUS_SUCCESS(status))
csr_neighbor_roam_start_lfr_scan(mac_ctx, session_id);
return QDF_STATUS_SUCCESS;
}
profile = session->scan_info.profile;
/*
* Check whether it is for start ibss. No need to do anything if it
* is a JOIN request
*/
if (profile && CSR_IS_START_IBSS(profile)) {
status = csr_roam_issue_connect(mac_ctx, session_id, profile, NULL,
eCsrHddIssued, session->scan_info.roam_id,
true, true);
if (!QDF_IS_STATUS_SUCCESS(status)) {
sme_err("failed to issue startIBSS, session_id %d status: 0x%08X roam id %d",
session_id, status, session->scan_info.roam_id);
csr_roam_call_callback(mac_ctx, session_id, NULL,
session->scan_info.roam_id, eCSR_ROAM_FAILED,
eCSR_ROAM_RESULT_FAILURE);
}
return status;
}
roam_result = eCSR_ROAM_RESULT_FAILURE;
if (profile && csr_is_bss_type_ibss(profile->BSSType)) {
roam_result = eCSR_ROAM_RESULT_IBSS_START_FAILED;
goto roam_completion;
}
roam_info = qdf_mem_malloc(sizeof(struct csr_roam_info));
if (!roam_info)
goto roam_completion;
if (session->scan_info.roambssentry) {
scan_result = GET_BASE_ADDR(session->scan_info.roambssentry,
struct tag_csrscan_result, Link);
roam_info->pBssDesc = &scan_result->Result.BssDescriptor;
}
roam_info->statusCode = session->joinFailStatusCode.statusCode;
roam_info->reasonCode = session->joinFailStatusCode.reasonCode;
/* Only indicate assoc_completion if we indicate assoc_start. */
if (session->bRefAssocStartCnt > 0) {
session->bRefAssocStartCnt--;
csr_roam_call_callback(mac_ctx, session_id, roam_info,
session->scan_info.roam_id,
eCSR_ROAM_ASSOCIATION_COMPLETION,
eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE);
} else {
if (!csr_handle_fils_scan_for_ssid_failure(
profile, roam_info)) {
qdf_mem_free(roam_info);
roam_info = NULL;
}
csr_roam_call_callback(mac_ctx, session_id, roam_info,
session->scan_info.roam_id,
eCSR_ROAM_ASSOCIATION_FAILURE,
eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE);
}
if (roam_info)
qdf_mem_free(roam_info);
roam_completion:
csr_roam_completion(mac_ctx, session_id, NULL, NULL, roam_result,
false);
return status;
}
QDF_STATUS csr_scan_result_purge(struct mac_context *mac,
tScanResultHandle hScanList)
{
QDF_STATUS status = QDF_STATUS_E_INVAL;
struct scan_result_list *pScanList =
(struct scan_result_list *) hScanList;
if (pScanList) {
status = csr_ll_scan_purge_result(mac, &pScanList->List);
csr_ll_close(&pScanList->List);
qdf_mem_free(pScanList);
}
return status;
}
/* Add the channel to the occupiedChannels array */
static void csr_add_to_occupied_channels(struct mac_context *mac,
uint8_t ch,
uint8_t sessionId,
struct csr_channel *occupied_ch,
bool is_init_list)
{
QDF_STATUS status;
uint8_t num_occupied_ch = occupied_ch->numChannels;
uint8_t *occupied_ch_lst = occupied_ch->channelList;
if (is_init_list)
mac->scan.roam_candidate_count[sessionId]++;
if (csr_is_channel_present_in_list(occupied_ch_lst,
num_occupied_ch, ch))
return;
status = csr_add_to_channel_list_front(occupied_ch_lst,
num_occupied_ch, ch);
if (QDF_IS_STATUS_SUCCESS(status)) {
occupied_ch->numChannels++;
sme_debug("Added channel %d to the list (count=%d)",
ch, occupied_ch->numChannels);
if (occupied_ch->numChannels >
CSR_BG_SCAN_OCCUPIED_CHANNEL_LIST_LEN)
occupied_ch->numChannels =
CSR_BG_SCAN_OCCUPIED_CHANNEL_LIST_LEN;
}
}
/* Put the BSS into the scan result list */
/* pIes can not be NULL */
static void csr_scan_add_result(struct mac_context *mac_ctx,
struct tag_csrscan_result *pResult)
{
qdf_nbuf_t buf;
uint8_t *data;
struct mgmt_rx_event_params rx_param = {0};
struct wlan_frame_hdr *hdr;
struct wlan_bcn_frame *fixed_frame;
uint32_t buf_len, i;
tSirBssDescription *bss_desc;
enum mgmt_frame_type frm_type = MGMT_BEACON;
if (!pResult) {
sme_err("pResult is null");
return;
}
bss_desc = &pResult->Result.BssDescriptor;
if (bss_desc->fProbeRsp)
frm_type = MGMT_PROBE_RESP;
rx_param.pdev_id = 0;
rx_param.channel = bss_desc->channelId;
rx_param.rssi = bss_desc->rssi;
rx_param.tsf_delta = bss_desc->tsf_delta;
/* Set all per chain rssi as invalid */
for (i = 0; i < WLAN_MGMT_TXRX_HOST_MAX_ANTENNA; i++)
rx_param.rssi_ctl[i] = WLAN_INVALID_PER_CHAIN_RSSI;
buf_len = GET_IE_LEN_IN_BSS(bss_desc->length) +
+ offsetof(struct wlan_bcn_frame, ie) + sizeof(*hdr);
buf = qdf_nbuf_alloc(NULL, qdf_roundup(buf_len, 4),
0, 4, false);
if (!buf)
return;
qdf_nbuf_put_tail(buf, buf_len);
qdf_nbuf_set_protocol(buf, ETH_P_CONTROL);
data = qdf_nbuf_data(buf);
hdr = (struct wlan_frame_hdr *) data;
qdf_mem_copy(hdr->i_addr3, bss_desc->bssId, QDF_MAC_ADDR_SIZE);
qdf_mem_copy(hdr->i_addr2, bss_desc->bssId, QDF_MAC_ADDR_SIZE);
qdf_mem_copy(hdr->i_seq,
&bss_desc->seq_ctrl, sizeof(uint16_t));
data += sizeof(*hdr);
fixed_frame = (struct wlan_bcn_frame *)data;
qdf_mem_copy(fixed_frame->timestamp,
bss_desc->timeStamp, 8);
fixed_frame->beacon_interval = bss_desc->beaconInterval;
fixed_frame->capability.value = bss_desc->capabilityInfo;
data += offsetof(struct wlan_bcn_frame, ie);
qdf_mem_copy(data, bss_desc->ieFields,
GET_IE_LEN_IN_BSS(bss_desc->length));
wlan_scan_process_bcn_probe_rx_sync(mac_ctx->psoc, buf, &rx_param,
frm_type);
}
/*
* NOTE: This routine is being added to make
* sure that scan results are not being flushed
* while roaming. If the scan results are flushed,
* we are unable to recover from
* csr_roam_roaming_state_disassoc_rsp_processor.
* If it is needed to remove this routine,
* first ensure that we recover gracefully from
* csr_roam_roaming_state_disassoc_rsp_processor if
* csr_scan_get_result returns with a failure because
* of not being able to find the roaming BSS.
*/
static bool csr_scan_flush_denied(struct mac_context *mac)
{
uint8_t sessionId;
for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) {
if (CSR_IS_SESSION_VALID(mac, sessionId)) {
if (csr_neighbor_middle_of_roaming(mac, sessionId))
return 1;
}
}
return 0;
}
static bool csr_scan_save_bss_description(struct mac_context *mac,
tSirBssDescription *pBSSDescription)
{
struct tag_csrscan_result *pCsrBssDescription = NULL;
uint32_t cbBSSDesc;
uint32_t cbAllocated;
/* figure out how big the BSS description is (the BSSDesc->length does
* NOT include the size of the length field itself).
*/
cbBSSDesc = pBSSDescription->length + sizeof(pBSSDescription->length);
cbAllocated = sizeof(struct tag_csrscan_result) + cbBSSDesc;
pCsrBssDescription = qdf_mem_malloc(cbAllocated);
if (!pCsrBssDescription)
return false;
pCsrBssDescription->AgingCount =
(int32_t) mac->roam.configParam.agingCount;
sme_debug(
"Set Aging Count = %d for BSS " QDF_MAC_ADDR_STR " ",
pCsrBssDescription->AgingCount,
QDF_MAC_ADDR_ARRAY(pCsrBssDescription->Result.BssDescriptor.
bssId));
qdf_mem_copy(&pCsrBssDescription->Result.BssDescriptor,
pBSSDescription, cbBSSDesc);
csr_scan_add_result(mac, pCsrBssDescription);
csr_free_scan_result_entry(mac, pCsrBssDescription);
return true;
}
/* Append a Bss Description... */
bool csr_scan_append_bss_description(struct mac_context *mac,
tSirBssDescription *pSirBssDescription)
{
return csr_scan_save_bss_description(mac, pSirBssDescription);
}
static void csr_purge_channel_power(struct mac_context *mac,
tDblLinkList *pChannelList)
{
struct csr_channel_powerinfo *pChannelSet;
tListElem *pEntry;
/*
* Remove the channel sets from the learned list and put them
* in the free list
*/
while ((pEntry = csr_ll_remove_head(pChannelList,
LL_ACCESS_NOLOCK)) != NULL) {
pChannelSet = GET_BASE_ADDR(pEntry,
struct csr_channel_powerinfo, link);
if (pChannelSet)
qdf_mem_free(pChannelSet);
}
}
/*
* Save the channelList into the ultimate storage as the final stage of channel
* Input: pCountryInfo -- the country code (e.g. "USI"), channel list, and power
* limit are all stored inside this data structure
*/
QDF_STATUS csr_save_to_channel_power2_g_5_g(struct mac_context *mac,
uint32_t tableSize,
tSirMacChanInfo *channelTable)
{
uint32_t i = tableSize / sizeof(tSirMacChanInfo);
tSirMacChanInfo *pChannelInfo;
struct csr_channel_powerinfo *pChannelSet;
bool f2GHzInfoFound = false;
bool f2GListPurged = false, f5GListPurged = false;
pChannelInfo = channelTable;
/* atleast 3 bytes have to be remaining -- from "countryString" */
while (i--) {
pChannelSet = qdf_mem_malloc(sizeof(struct csr_channel_powerinfo));
if (!pChannelSet) {
pChannelInfo++;
continue;
}
pChannelSet->firstChannel = pChannelInfo->firstChanNum;
pChannelSet->numChannels = pChannelInfo->numChannels;
/*
* Now set the inter-channel offset based on the frequency band
* the channel set lies in
*/
if ((WLAN_REG_IS_24GHZ_CH(pChannelSet->firstChannel)) &&
((pChannelSet->firstChannel +
(pChannelSet->numChannels - 1)) <=
WLAN_REG_MAX_24GHZ_CH_NUM)) {
pChannelSet->interChannelOffset = 1;
f2GHzInfoFound = true;
} else if ((WLAN_REG_IS_5GHZ_CH(pChannelSet->firstChannel))
&& ((pChannelSet->firstChannel +
((pChannelSet->numChannels - 1) * 4)) <=
WLAN_REG_MAX_5GHZ_CH_NUM)) {
pChannelSet->interChannelOffset = 4;
f2GHzInfoFound = false;
} else {
sme_warn("Invalid Channel %d Present in Country IE",
pChannelSet->firstChannel);
qdf_mem_free(pChannelSet);
return QDF_STATUS_E_FAILURE;
}
pChannelSet->txPower = QDF_MIN(pChannelInfo->maxTxPower,
mac->mlme_cfg->power.max_tx_power);
if (f2GHzInfoFound) {
if (!f2GListPurged) {
/* purge previous results if found new */
csr_purge_channel_power(mac,
&mac->scan.
channelPowerInfoList24);
f2GListPurged = true;
}
if (CSR_IS_OPERATING_BG_BAND(mac)) {
/* add to the list of 2.4 GHz channel sets */
csr_ll_insert_tail(&mac->scan.
channelPowerInfoList24,
&pChannelSet->link,
LL_ACCESS_LOCK);
} else {
sme_debug(
"Adding 11B/G ch in 11A. 1st ch %d",
pChannelSet->firstChannel);
qdf_mem_free(pChannelSet);
}
} else {
/* 5GHz info found */
if (!f5GListPurged) {
/* purge previous results if found new */
csr_purge_channel_power(mac,
&mac->scan.
channelPowerInfoList5G);
f5GListPurged = true;
}
if (CSR_IS_OPERATING_A_BAND(mac)) {
/* add to the list of 5GHz channel sets */
csr_ll_insert_tail(&mac->scan.
channelPowerInfoList5G,
&pChannelSet->link,
LL_ACCESS_LOCK);
} else {
sme_debug(
"Adding 11A ch in B/G. 1st ch %d",
pChannelSet->firstChannel);
qdf_mem_free(pChannelSet);
}
}
pChannelInfo++; /* move to next entry */
}
return QDF_STATUS_SUCCESS;
}
void csr_apply_power2_current(struct mac_context *mac)
{
sme_debug("Updating Cfg with power settings");
csr_save_tx_power_to_cfg(mac, &mac->scan.channelPowerInfoList24,
BAND_2G);
csr_save_tx_power_to_cfg(mac, &mac->scan.channelPowerInfoList5G,
BAND_5G);
}
void csr_apply_channel_power_info_to_fw(struct mac_context *mac_ctx,
struct csr_channel *ch_lst,
uint8_t *countryCode)
{
int i;
uint8_t num_ch = 0;
uint8_t tempNumChannels = 0;
struct csr_channel tmp_ch_lst;
if (ch_lst->numChannels) {
tempNumChannels = QDF_MIN(ch_lst->numChannels,
CFG_VALID_CHANNEL_LIST_LEN);
for (i = 0; i < tempNumChannels; i++) {
tmp_ch_lst.channelList[num_ch] = ch_lst->channelList[i];
num_ch++;
}
tmp_ch_lst.numChannels = num_ch;
/* Store the channel+power info in the global place: Cfg */
csr_apply_power2_current(mac_ctx);
csr_set_cfg_valid_channel_list(mac_ctx, tmp_ch_lst.channelList,
tmp_ch_lst.numChannels);
} else {
sme_err("11D channel list is empty");
}
csr_set_cfg_country_code(mac_ctx, countryCode);
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR
static void csr_diag_reset_country_information(struct mac_context *mac)
{
host_log_802_11d_pkt_type *p11dLog;
int Index;
WLAN_HOST_DIAG_LOG_ALLOC(p11dLog, host_log_802_11d_pkt_type,
LOG_WLAN_80211D_C);
if (!p11dLog)
return;
p11dLog->eventId = WLAN_80211D_EVENT_RESET;
qdf_mem_copy(p11dLog->countryCode, mac->scan.countryCodeCurrent, 3);
p11dLog->numChannel = mac->scan.base_channels.numChannels;
if (p11dLog->numChannel <= HOST_LOG_MAX_NUM_CHANNEL) {
qdf_mem_copy(p11dLog->Channels,
mac->scan.base_channels.channelList,
p11dLog->numChannel);
for (Index = 0;
Index < mac->scan.base_channels.numChannels;
Index++) {
p11dLog->TxPwr[Index] = QDF_MIN(
mac->scan.defaultPowerTable[Index].tx_power,
mac->mlme_cfg->power.max_tx_power);
}
}
if (!mac->mlme_cfg->gen.enabled_11d)
p11dLog->supportMultipleDomain = WLAN_80211D_DISABLED;
else
p11dLog->supportMultipleDomain =
WLAN_80211D_SUPPORT_MULTI_DOMAIN;
WLAN_HOST_DIAG_LOG_REPORT(p11dLog);
}
#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */
/**
* csr_apply_channel_power_info_wrapper() - sends channel info to fw
* @mac: main MAC data structure
*
* This function sends the channel power info to firmware
*
* Return: none
*/
void csr_apply_channel_power_info_wrapper(struct mac_context *mac)
{
#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR
csr_diag_reset_country_information(mac);
#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */
csr_prune_channel_list_for_mode(mac, &mac->scan.base_channels);
csr_save_channel_power_for_band(mac, false);
csr_save_channel_power_for_band(mac, true);
/* apply the channel list, power settings, and the country code. */
csr_apply_channel_power_info_to_fw(mac,
&mac->scan.base_channels, mac->scan.countryCodeCurrent);
/* clear the 11d channel list */
qdf_mem_zero(&mac->scan.channels11d, sizeof(mac->scan.channels11d));
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR
/* caller allocated memory for pNumChn and pChnPowerInfo */
/* As input, *pNumChn has the size of the array of pChnPowerInfo */
/* Upon return, *pNumChn has the number of channels assigned. */
static void csr_get_channel_power_info(struct mac_context *mac,
tDblLinkList *list,
uint32_t *num_ch,
struct channel_power *chn_pwr_info)
{
tListElem *entry;
uint32_t chn_idx = 0, idx;
struct csr_channel_powerinfo *ch_set;
/* Get 2.4Ghz first */
entry = csr_ll_peek_head(list, LL_ACCESS_LOCK);
while (entry && (chn_idx < *num_ch)) {
ch_set = GET_BASE_ADDR(entry,
struct csr_channel_powerinfo, link);
for (idx = 0; (idx < ch_set->numChannels)
&& (chn_idx < *num_ch); idx++) {
chn_pwr_info[chn_idx].chan_num =
(uint8_t) (ch_set->firstChannel
+ (idx * ch_set->interChannelOffset));
chn_pwr_info[chn_idx++].tx_power = ch_set->txPower;
}
entry = csr_ll_next(list, entry, LL_ACCESS_LOCK);
}
*num_ch = chn_idx;
}
static void csr_diag_apply_country_info(struct mac_context *mac_ctx)
{
host_log_802_11d_pkt_type *p11dLog;
struct channel_power chnPwrInfo[CFG_VALID_CHANNEL_LIST_LEN];
uint32_t nChnInfo = CFG_VALID_CHANNEL_LIST_LEN, nTmp;
WLAN_HOST_DIAG_LOG_ALLOC(p11dLog, host_log_802_11d_pkt_type,
LOG_WLAN_80211D_C);
if (!p11dLog)
return;
p11dLog->eventId = WLAN_80211D_EVENT_COUNTRY_SET;
qdf_mem_copy(p11dLog->countryCode, mac_ctx->scan.countryCode11d, 3);
p11dLog->numChannel = mac_ctx->scan.channels11d.numChannels;
if (p11dLog->numChannel > HOST_LOG_MAX_NUM_CHANNEL)
goto diag_end;
qdf_mem_copy(p11dLog->Channels,
mac_ctx->scan.channels11d.channelList,
p11dLog->numChannel);
csr_get_channel_power_info(mac_ctx,
&mac_ctx->scan.channelPowerInfoList24,
&nChnInfo, chnPwrInfo);
nTmp = nChnInfo;
nChnInfo = CFG_VALID_CHANNEL_LIST_LEN - nTmp;
csr_get_channel_power_info(mac_ctx,
&mac_ctx->scan.channelPowerInfoList5G,
&nChnInfo, &chnPwrInfo[nTmp]);
for (nTmp = 0; nTmp < p11dLog->numChannel; nTmp++) {
for (nChnInfo = 0;
nChnInfo < CFG_VALID_CHANNEL_LIST_LEN;
nChnInfo++) {
if (p11dLog->Channels[nTmp] ==
chnPwrInfo[nChnInfo].chan_num) {
p11dLog->TxPwr[nTmp] =
chnPwrInfo[nChnInfo].tx_power;
break;
}
}
}
diag_end:
if (!mac_ctx->mlme_cfg->gen.enabled_11d)
p11dLog->supportMultipleDomain = WLAN_80211D_DISABLED;
else
p11dLog->supportMultipleDomain =
WLAN_80211D_SUPPORT_MULTI_DOMAIN;
WLAN_HOST_DIAG_LOG_REPORT(p11dLog);
}
#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */
/**
* csr_apply_country_information() - apply country code information
* @mac: core MAC data structure
*
* This function programs the new country code
*
* Return: none
*/
void csr_apply_country_information(struct mac_context *mac)
{
v_REGDOMAIN_t domainId;
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!wlan_reg_11d_enabled_on_host(mac->psoc))
return;
status = csr_get_regulatory_domain_for_country(mac,
mac->scan.countryCode11d, &domainId, SOURCE_QUERY);
if (!QDF_IS_STATUS_SUCCESS(status))
return;
/* Check whether we need to enforce default domain */
#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR
csr_diag_apply_country_info(mac);
#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */
if (mac->scan.domainIdCurrent != domainId)
return;
if (mac->scan.domainIdCurrent != domainId) {
sme_debug("Domain Changed Old %d, new %d",
mac->scan.domainIdCurrent, domainId);
if (domainId >= REGDOMAIN_COUNT)
sme_err("fail to set regId %d", domainId);
}
mac->scan.domainIdCurrent = domainId;
/* switch to active scans using this new channel list */
mac->scan.curScanType = eSIR_ACTIVE_SCAN;
}
void csr_save_channel_power_for_band(struct mac_context *mac, bool fill_5f)
{
uint32_t idx, count = 0;
tSirMacChanInfo *chan_info;
tSirMacChanInfo *ch_info_start;
int32_t max_ch_idx;
bool tmp_bool;
uint8_t ch = 0;
max_ch_idx =
(mac->scan.base_channels.numChannels <
CFG_VALID_CHANNEL_LIST_LEN) ?
mac->scan.base_channels.numChannels :
CFG_VALID_CHANNEL_LIST_LEN;
chan_info = qdf_mem_malloc(sizeof(tSirMacChanInfo) *
CFG_VALID_CHANNEL_LIST_LEN);
if (!chan_info)
return;
ch_info_start = chan_info;
for (idx = 0; idx < max_ch_idx; idx++) {
ch = mac->scan.defaultPowerTable[idx].chan_num;
tmp_bool = (fill_5f && WLAN_REG_IS_5GHZ_CH(ch)) ||
(!fill_5f && WLAN_REG_IS_24GHZ_CH(ch));
if (!tmp_bool)
continue;
if (count >= CFG_VALID_CHANNEL_LIST_LEN) {
sme_warn("count: %d exceeded", count);
break;
}
chan_info->firstChanNum =
mac->scan.defaultPowerTable[idx].chan_num;
chan_info->numChannels = 1;
chan_info->maxTxPower =
QDF_MIN(mac->scan.defaultPowerTable[idx].tx_power,
mac->mlme_cfg->power.max_tx_power);
chan_info++;
count++;
}
if (count) {
csr_save_to_channel_power2_g_5_g(mac,
count * sizeof(tSirMacChanInfo), ch_info_start);
}
qdf_mem_free(ch_info_start);
}
bool csr_is_supported_channel(struct mac_context *mac, uint8_t channelId)
{
bool fRet = false;
uint32_t i;
for (i = 0; i < mac->scan.base_channels.numChannels; i++) {
if (channelId ==
mac->scan.base_channels.channelList[i]) {
fRet = true;
break;
}
}
return fRet;
}
/*
* 802.11D only: Gather 11d IE via beacon or Probe response and store them in
* pAdapter->channels11d
*/
bool csr_learn_11dcountry_information(struct mac_context *mac,
tSirBssDescription *pSirBssDesc,
tDot11fBeaconIEs *pIes, bool fForce)
{
QDF_STATUS status;
uint8_t *pCountryCodeSelected;
bool fRet = false;
v_REGDOMAIN_t domainId;
tDot11fBeaconIEs *pIesLocal = pIes;
bool useVoting = false;
if ((!pSirBssDesc) && (!pIes))
useVoting = true;
/* check if .11d support is enabled */
if (!wlan_reg_11d_enabled_on_host(mac->psoc))
goto free_ie;
if (false == useVoting) {
if (!pIesLocal &&
(!QDF_IS_STATUS_SUCCESS(
csr_get_parsed_bss_description_ies(
mac, pSirBssDesc, &pIesLocal))))
goto free_ie;
/* check if country information element is present */
if (!pIesLocal->Country.present)
/* No country info */
goto free_ie;
status = csr_get_regulatory_domain_for_country(mac,
pIesLocal->Country.country, &domainId,
SOURCE_QUERY);
if (QDF_IS_STATUS_SUCCESS(status)
&& (domainId == REGDOMAIN_WORLD))
goto free_ie;
} /* useVoting == false */
if (false == useVoting)
pCountryCodeSelected = pIesLocal->Country.country;
else
pCountryCodeSelected = mac->scan.countryCodeElected;
if (qdf_mem_cmp(pCountryCodeSelected, mac->scan.countryCodeCurrent,
CDS_COUNTRY_CODE_LEN) == 0) {
qdf_mem_copy(mac->scan.countryCode11d,
mac->scan.countryCodeCurrent,
CDS_COUNTRY_CODE_LEN);
goto free_ie;
}
mac->reg_hint_src = SOURCE_11D;
status = csr_get_regulatory_domain_for_country(mac,
pCountryCodeSelected, &domainId, SOURCE_11D);
if (status != QDF_STATUS_SUCCESS) {
sme_err("fail to get regId %d", domainId);
fRet = false;
goto free_ie;
}
fRet = true;
free_ie:
if (!pIes && pIesLocal) {
/* locally allocated */
qdf_mem_free(pIesLocal);
}
return fRet;
}
static enum csr_scancomplete_nextcommand
csr_scan_get_next_command_state(struct mac_context *mac_ctx,
uint32_t session_id,
eCsrScanStatus scan_status,
uint8_t *chan)
{
enum csr_scancomplete_nextcommand NextCommand = eCsrNextScanNothing;
int8_t channel;
struct csr_roam_session *session;
if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
sme_err("session %d is invalid", session_id);
return NextCommand;
}
session = CSR_GET_SESSION(mac_ctx, session_id);
switch (session->scan_info.scan_reason) {
case eCsrScanForSsid:
sme_debug("Resp for Scan For Ssid");
channel = csr_scan_get_channel_for_hw_mode_change(
mac_ctx,
session_id,
session->scan_info.profile);
if ((!channel) || scan_status) {
NextCommand = eCsrNexteScanForSsidFailure;
sme_err("next Scan For Ssid Failure %d %d",
channel, scan_status);
} else {
NextCommand = eCsrNextCheckAllowConc;
*chan = channel;
sme_debug("next CheckAllowConc");
}
break;
default:
NextCommand = eCsrNextScanNothing;
break;
}
sme_debug("Next Command %d", NextCommand);
return NextCommand;
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR
static void
csr_diag_scan_complete(struct mac_context *mac_ctx,
eCsrScanStatus scan_status)
{
host_log_scan_pkt_type *pScanLog = NULL;
qdf_list_t *list = NULL;
struct wlan_objmgr_pdev *pdev = NULL;
struct scan_cache_node *cur_node = NULL;
struct scan_cache_node *next_node = NULL;
int n = 0, c = 0;
WLAN_HOST_DIAG_LOG_ALLOC(pScanLog,
host_log_scan_pkt_type,
LOG_WLAN_SCAN_C);
if (!pScanLog)
return;
pScanLog->eventId = WLAN_SCAN_EVENT_ACTIVE_SCAN_RSP;
if (eCSR_SCAN_SUCCESS != scan_status) {
pScanLog->status = WLAN_SCAN_STATUS_FAILURE;
WLAN_HOST_DIAG_LOG_REPORT(pScanLog);
return;
}
pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc,
0, WLAN_LEGACY_MAC_ID);
if (!pdev) {
sme_err("pdev is NULL");
return;
}
list = ucfg_scan_get_result(pdev, NULL);
if (list)
sme_debug("num_entries %d", qdf_list_size(list));
if (!list || (list && !qdf_list_size(list))) {
sme_err("get scan result failed");
WLAN_HOST_DIAG_LOG_REPORT(pScanLog);
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
if (list)
ucfg_scan_purge_results(list);
return;
}
qdf_list_peek_front(list,
(qdf_list_node_t **) &cur_node);
while (cur_node) {
if (n < HOST_LOG_MAX_NUM_BSSID) {
qdf_mem_copy(pScanLog->bssid[n],
cur_node->entry->bssid.bytes,
QDF_MAC_ADDR_SIZE);
if (cur_node->entry->ssid.length >
WLAN_SSID_MAX_LEN)
cur_node->entry->ssid.length =
WLAN_SSID_MAX_LEN;
qdf_mem_copy(pScanLog->ssid[n],
cur_node->entry->ssid.ssid,
cur_node->entry->ssid.length);
n++;
}
c++;
qdf_list_peek_next(
list,
(qdf_list_node_t *) cur_node,
(qdf_list_node_t **) &next_node);
cur_node = next_node;
next_node = NULL;
}
pScanLog->numSsid = (uint8_t) n;
pScanLog->totalSsid = (uint8_t) c;
ucfg_scan_purge_results(list);
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
WLAN_HOST_DIAG_LOG_REPORT(pScanLog);
}
#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */
/**
* csr_saved_scan_cmd_free_fields() - Free internal fields of scan command
*
* @mac_ctx: Global MAC context
* @session: sme session
*
* Frees data structures allocated inside saved_scan_cmd and releases
* the profile.
* Return: None
*/
void csr_saved_scan_cmd_free_fields(struct mac_context *mac_ctx,
struct csr_roam_session *session)
{
if (session->scan_info.profile) {
sme_debug("Free profile for session %d", session->sessionId);
csr_release_profile(mac_ctx,
session->scan_info.profile);
qdf_mem_free(session->scan_info.profile);
session->scan_info.profile = NULL;
}
if (session->scan_info.roambssentry) {
qdf_mem_free(session->scan_info.roambssentry);
session->scan_info.roambssentry = NULL;
}
}
/**
* csr_save_profile() - Save the profile info from sme command
* @mac_ctx: Global MAC context
* @save_cmd: Pointer where the command will be saved
* @command: Command from which the profile will be saved
*
* Saves the profile information from the SME's scan command
*
* Return: QDF_STATUS
*/
static QDF_STATUS csr_save_profile(struct mac_context *mac_ctx,
uint32_t session_id)
{
struct tag_csrscan_result *scan_result;
struct tag_csrscan_result *temp;
uint32_t bss_len;
struct csr_roam_session *session;
/*
* check the session's validity first, if session itself
* is not valid then there is no point of releasing the memory
* for invalid session (i.e. "goto error" case)
*/
if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
sme_err("session %d is invalid", session_id);
return QDF_STATUS_E_FAILURE;
}
session = CSR_GET_SESSION(mac_ctx, session_id);
if (!session->scan_info.roambssentry)
return QDF_STATUS_SUCCESS;
scan_result = GET_BASE_ADDR(session->scan_info.roambssentry,
struct tag_csrscan_result, Link);
bss_len = scan_result->Result.BssDescriptor.length +
sizeof(scan_result->Result.BssDescriptor.length);
temp = qdf_mem_malloc(sizeof(struct tag_csrscan_result) + bss_len);
if (!temp)
goto error;
temp->AgingCount = scan_result->AgingCount;
temp->preferValue = scan_result->preferValue;
temp->capValue = scan_result->capValue;
temp->ucEncryptionType = scan_result->ucEncryptionType;
temp->mcEncryptionType = scan_result->mcEncryptionType;
temp->authType = scan_result->authType;
/* pvIes is unsued in success/failure */
temp->Result.pvIes = NULL;
qdf_mem_copy(temp->Result.pvIes,
scan_result->Result.pvIes,
sizeof(*scan_result->Result.pvIes));
temp->Result.ssId.length = scan_result->Result.ssId.length;
qdf_mem_copy(temp->Result.ssId.ssId,
scan_result->Result.ssId.ssId,
sizeof(scan_result->Result.ssId.ssId));
temp->Result.timer = scan_result->Result.timer;
qdf_mem_copy(&temp->Result.BssDescriptor,
&scan_result->Result.BssDescriptor,
sizeof(temp->Result.BssDescriptor));
temp->Link.last = temp->Link.next = NULL;
session->scan_info.roambssentry = (tListElem *)temp;
return QDF_STATUS_SUCCESS;
error:
csr_scan_handle_search_for_ssid_failure(mac_ctx,
session_id);
csr_saved_scan_cmd_free_fields(mac_ctx, session);
return QDF_STATUS_E_FAILURE;
}
static void csr_handle_nxt_cmd(struct mac_context *mac_ctx,
enum csr_scancomplete_nextcommand nxt_cmd,
uint32_t session_id,
uint8_t chan)
{
QDF_STATUS status, ret;
struct csr_roam_session *session;
switch (nxt_cmd) {
case eCsrNexteScanForSsidSuccess:
csr_scan_handle_search_for_ssid(mac_ctx, session_id);
break;
case eCsrNexteScanForSsidFailure:
csr_scan_handle_search_for_ssid_failure(mac_ctx, session_id);
break;
case eCsrNextCheckAllowConc:
ret = policy_mgr_current_connections_update(mac_ctx->psoc,
session_id, chan,
POLICY_MGR_UPDATE_REASON_HIDDEN_STA);
sme_debug("chan: %d session: %d status: %d",
chan, session_id, ret);
status = csr_save_profile(mac_ctx, session_id);
if (!QDF_IS_STATUS_SUCCESS(status)) {
/* csr_save_profile should report error */
sme_err("profile save failed %d", status);
break;
}
if (QDF_STATUS_E_FAILURE == ret) {
sme_err("conn update fail %d", chan);
csr_scan_handle_search_for_ssid_failure(mac_ctx,
session_id);
} else if ((QDF_STATUS_E_NOSUPPORT == ret) ||
(QDF_STATUS_E_ALREADY == ret)) {
sme_err("conn update ret %d", ret);
csr_scan_handle_search_for_ssid(mac_ctx, session_id);
}
/* Else: Set hw mode was issued and the saved connect would
* be issued after set hw mode response
*/
if (QDF_IS_STATUS_SUCCESS(ret))
return;
default:
break;
}
session = CSR_GET_SESSION(mac_ctx, session_id);
if (session)
csr_saved_scan_cmd_free_fields(mac_ctx, session);
}
void csr_scan_callback(struct wlan_objmgr_vdev *vdev,
struct scan_event *event, void *arg)
{
eCsrScanStatus scan_status = eCSR_SCAN_FAILURE;
enum csr_scancomplete_nextcommand NextCommand = eCsrNextScanNothing;
struct mac_context *mac_ctx;
struct csr_roam_session *session;
uint32_t session_id = 0;
uint8_t chan = 0;
bool success = false;
mac_ctx = (struct mac_context *)arg;
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;
session_id = wlan_vdev_get_id(vdev);
if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
sme_err("session %d is invalid", session_id);
return;
}
session = CSR_GET_SESSION(mac_ctx, session_id);
sme_debug("Scan Completion: status %d session %d scan_id %d",
scan_status, session_id, event->scan_id);
/* verify whether scan event is related to scan interested by CSR */
if (session->scan_info.scan_id != event->scan_id) {
sme_debug("Scan Completion on wrong scan_id %d, expected %d",
session->scan_info.scan_id, event->scan_id);
return;
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR
csr_diag_scan_complete(mac_ctx, scan_status);
#endif
NextCommand = csr_scan_get_next_command_state(mac_ctx,
session_id, scan_status,
&chan);
/* We reuse the command here instead reissue a new command */
csr_handle_nxt_cmd(mac_ctx, NextCommand,
session_id, chan);
}
tCsrScanResultInfo *csr_scan_result_get_first(struct mac_context *mac,
tScanResultHandle hScanResult)
{
tListElem *pEntry;
struct tag_csrscan_result *pResult;
tCsrScanResultInfo *pRet = NULL;
struct scan_result_list *pResultList =
(struct scan_result_list *) hScanResult;
if (pResultList) {
pEntry = csr_ll_peek_head(&pResultList->List, LL_ACCESS_NOLOCK);
if (pEntry) {
pResult = GET_BASE_ADDR(pEntry, struct
tag_csrscan_result, Link);
pRet = &pResult->Result;
}
pResultList->pCurEntry = pEntry;
}
return pRet;
}
tCsrScanResultInfo *csr_scan_result_get_next(struct mac_context *mac,
tScanResultHandle hScanResult)
{
tListElem *pEntry = NULL;
struct tag_csrscan_result *pResult = NULL;
tCsrScanResultInfo *pRet = NULL;
struct scan_result_list *pResultList =
(struct scan_result_list *) hScanResult;
if (!pResultList)
return NULL;
if (!pResultList->pCurEntry)
pEntry = csr_ll_peek_head(&pResultList->List, LL_ACCESS_NOLOCK);
else
pEntry = csr_ll_next(&pResultList->List, pResultList->pCurEntry,
LL_ACCESS_NOLOCK);
if (pEntry) {
pResult = GET_BASE_ADDR(pEntry, struct tag_csrscan_result,
Link);
pRet = &pResult->Result;
}
pResultList->pCurEntry = pEntry;
return pRet;
}
/**
* csr_scan_for_ssid() - Function usually used for BSSs that suppresses SSID
* @mac_ctx: Pointer to Global Mac structure
* @profile: pointer to struct csr_roam_profile
* @roam_id: variable representing roam id
* @notify: boolean variable
*
* Function is usually used for BSSs that suppresses SSID so the profile
* shall have one and only one SSID.
*
* Return: Success - QDF_STATUS_SUCCESS, Failure - error number
*/
QDF_STATUS csr_scan_for_ssid(struct mac_context *mac_ctx, uint32_t session_id,
struct csr_roam_profile *profile, uint32_t roam_id,
bool notify)
{
QDF_STATUS status = QDF_STATUS_E_INVAL;
uint32_t num_ssid = profile->SSIDs.numOfSSIDs;
struct scan_start_request *req;
struct wlan_objmgr_vdev *vdev;
uint8_t i, chan, num_chan = 0;
uint8_t pdev_id;
wlan_scan_id scan_id;
struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id);
if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
sme_err("session %d is invalid", session_id);
return status;
}
if (num_ssid != 1) {
sme_err("numSSID (%d) is invalid", profile->SSIDs.numOfSSIDs);
return status;
}
if (!mac_ctx->pdev) {
sme_err("pdev ctx is NULL");
return status;
}
pdev_id = wlan_objmgr_pdev_get_pdev_id(mac_ctx->pdev);
/* Free old memory if any before its overwritten */
csr_saved_scan_cmd_free_fields(mac_ctx, session);
session->scan_info.profile =
qdf_mem_malloc(sizeof(struct csr_roam_profile));
if (!session->scan_info.profile)
status = QDF_STATUS_E_NOMEM;
else
status = csr_roam_copy_profile(mac_ctx,
session->scan_info.profile,
profile);
if (QDF_IS_STATUS_ERROR(status))
goto error;
scan_id = ucfg_scan_get_scan_id(mac_ctx->psoc);
session->scan_info.scan_id = scan_id;
session->scan_info.scan_reason = eCsrScanForSsid;
session->scan_info.roam_id = roam_id;
req = qdf_mem_malloc(sizeof(*req));
if (!req) {
status = QDF_STATUS_E_NOMEM;
goto error;
}
vdev = wlan_objmgr_get_vdev_by_macaddr_from_psoc(mac_ctx->psoc,
pdev_id,
session->selfMacAddr.bytes,
WLAN_LEGACY_SME_ID);
ucfg_scan_init_default_params(vdev, req);
req->scan_req.scan_id = scan_id;
req->scan_req.vdev_id = session_id;
req->scan_req.scan_req_id = mac_ctx->scan.requester_id;
req->scan_req.scan_f_passive = false;
req->scan_req.scan_f_bcast_probe = false;
if (QDF_P2P_CLIENT_MODE == profile->csrPersona)
req->scan_req.scan_priority = SCAN_PRIORITY_HIGH;
/* Allocate memory for IE field */
if (profile->pAddIEScan) {
req->scan_req.extraie.ptr =
qdf_mem_malloc(profile->nAddIEScanLength);
if (!req->scan_req.extraie.ptr)
status = QDF_STATUS_E_NOMEM;
else
status = QDF_STATUS_SUCCESS;
if (QDF_IS_STATUS_SUCCESS(status)) {
qdf_mem_copy(req->scan_req.extraie.ptr,
profile->pAddIEScan,
profile->nAddIEScanLength);
req->scan_req.extraie.len = profile->nAddIEScanLength;
} else {
sme_err("No memory for scanning IE fields");
}
}
req->scan_req.num_bssid = 1;
if (profile->BSSIDs.numOfBSSIDs == 1)
qdf_copy_macaddr(&req->scan_req.bssid_list[0],
profile->BSSIDs.bssid);
else
qdf_set_macaddr_broadcast(&req->scan_req.bssid_list[0]);
if (profile->ChannelInfo.numOfChannels) {
for (i = 0; i < profile->ChannelInfo.numOfChannels; i++) {
if (csr_roam_is_valid_channel(mac_ctx,
profile->ChannelInfo.ChannelList[i])) {
chan = profile->ChannelInfo.ChannelList[i];
req->scan_req.chan_list.chan[num_chan].freq =
wlan_chan_to_freq(chan);
num_chan++;
}
}
req->scan_req.chan_list.num_chan = num_chan;
}
/* Extend it for multiple SSID */
if (profile->SSIDs.numOfSSIDs) {
if (profile->SSIDs.SSIDList[0].SSID.length > WLAN_SSID_MAX_LEN) {
sme_debug("wrong ssid length = %d",
profile->SSIDs.SSIDList[0].SSID.length);
status = QDF_STATUS_E_INVAL;
goto error;
}
req->scan_req.num_ssids = 1;
qdf_mem_copy(&req->scan_req.ssid[0].ssid,
&profile->SSIDs.SSIDList[0].SSID.ssId,
profile->SSIDs.SSIDList[0].SSID.length);
req->scan_req.ssid[0].length =
profile->SSIDs.SSIDList[0].SSID.length;
sme_debug("scan for SSID = %.*s", req->scan_req.ssid[0].length,
req->scan_req.ssid[0].ssid);
}
status = ucfg_scan_start(req);
wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID);
error:
if (QDF_IS_STATUS_ERROR(status)) {
sme_err("failed to initiate scan with status: %d", status);
csr_release_profile(mac_ctx, session->scan_info.profile);
qdf_mem_free(session->scan_info.profile);
session->scan_info.profile = NULL;
if (notify)
csr_roam_call_callback(mac_ctx, session_id, NULL,
roam_id, eCSR_ROAM_FAILED,
eCSR_ROAM_RESULT_FAILURE);
}
return status;
}
static void csr_set_cfg_valid_channel_list(struct mac_context *mac,
uint8_t *pChannelList,
uint8_t NumChannels)
{
QDF_STATUS status;
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
"%s: dump valid channel list(NumChannels(%d))",
__func__, NumChannels);
QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
pChannelList, NumChannels);
qdf_mem_copy(mac->mlme_cfg->reg.valid_channel_list, pChannelList,
NumChannels);
mac->mlme_cfg->reg.valid_channel_list_num = NumChannels;
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
"Scan offload is enabled, update default chan list");
/*
* disable fcc constraint since new country code
* is being set
*/
mac->scan.fcc_constraint = false;
status = csr_update_channel_list(mac);
if (QDF_STATUS_SUCCESS != status) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
"failed to update the supported channel list");
}
}
/*
* The Tx power limits are saved in the cfg for future usage.
*/
static void csr_save_tx_power_to_cfg(struct mac_context *mac,
tDblLinkList *pList,
enum band_info band)
{
tListElem *pEntry;
uint32_t cbLen = 0, dataLen, tmp_len;
struct csr_channel_powerinfo *ch_set;
uint32_t idx, count = 0;
tSirMacChanInfo *ch_pwr_set;
uint8_t *p_buf = NULL;
/* allocate maximum space for all channels */
dataLen = CFG_VALID_CHANNEL_LIST_LEN * sizeof(tSirMacChanInfo);
p_buf = qdf_mem_malloc(dataLen);
if (!p_buf)
return;
ch_pwr_set = (tSirMacChanInfo *)(p_buf);
pEntry = csr_ll_peek_head(pList, LL_ACCESS_LOCK);
/*
* write the tuples (startChan, numChan, txPower) for each channel found
* in the channel power list.
*/
while (pEntry) {
ch_set = GET_BASE_ADDR(pEntry,
struct csr_channel_powerinfo, link);
if (1 != ch_set->interChannelOffset) {
/*
* we keep the 5G channel sets internally with an
* interchannel offset of 4. Expand these to the right
* format. (inter channel offset of 1 is the only option
* for the triplets that 11d advertises.
*/
tmp_len = cbLen + (ch_set->numChannels *
sizeof(tSirMacChanInfo));
if (tmp_len >= dataLen) {
/*
* expanding this entry will overflow our
* allocation
*/
sme_err(
"Buffer overflow, start %d, num %d, offset %d",
ch_set->firstChannel,
ch_set->numChannels,
ch_set->interChannelOffset);
break;
}
for (idx = 0; idx < ch_set->numChannels; idx++) {
ch_pwr_set->firstChanNum = (tSirMacChanNum)
(ch_set->firstChannel + (idx *
ch_set->interChannelOffset));
sme_debug(
"Setting Channel Number %d",
ch_pwr_set->firstChanNum);
ch_pwr_set->numChannels = 1;
ch_pwr_set->maxTxPower =
QDF_MIN(ch_set->txPower,
mac->mlme_cfg->power.max_tx_power);
sme_debug(
"Setting Max Transmit Power %d",
ch_pwr_set->maxTxPower);
cbLen += sizeof(tSirMacChanInfo);
ch_pwr_set++;
count++;
}
} else {
if (cbLen >= dataLen) {
/* this entry will overflow our allocation */
sme_err(
"Buffer overflow, start %d, num %d, offset %d",
ch_set->firstChannel,
ch_set->numChannels,
ch_set->interChannelOffset);
break;
}
ch_pwr_set->firstChanNum = ch_set->firstChannel;
sme_debug("Setting Channel Number %d",
ch_pwr_set->firstChanNum);
ch_pwr_set->numChannels = ch_set->numChannels;
ch_pwr_set->maxTxPower = QDF_MIN(ch_set->txPower,
mac->mlme_cfg->power.max_tx_power);
sme_debug(
"Setting Max Tx Power %d, nTxPower %d",
ch_pwr_set->maxTxPower,
mac->mlme_cfg->power.max_tx_power);
cbLen += sizeof(tSirMacChanInfo);
ch_pwr_set++;
count++;
}
pEntry = csr_ll_next(pList, pEntry, LL_ACCESS_LOCK);
}
if (band == BAND_2G) {
mac->mlme_cfg->power.max_tx_power_24.len = 3 * count;
qdf_mem_copy(mac->mlme_cfg->power.max_tx_power_24.data,
(uint8_t *)p_buf,
mac->mlme_cfg->power.max_tx_power_24.len);
}
if (band == BAND_5G) {
mac->mlme_cfg->power.max_tx_power_5.len = 3 * count;
qdf_mem_copy(mac->mlme_cfg->power.max_tx_power_5.data,
(uint8_t *)p_buf,
mac->mlme_cfg->power.max_tx_power_5.len);
}
qdf_mem_free(p_buf);
}
static void csr_set_cfg_country_code(struct mac_context *mac,
uint8_t *countryCode)
{
uint8_t cc[CFG_COUNTRY_CODE_LEN];
/* v_REGDOMAIN_t DomainId */
sme_debug("Setting Country Code in Cfg %s", countryCode);
qdf_mem_copy(cc, countryCode, CFG_COUNTRY_CODE_LEN);
/*
* Don't program the bogus country codes that we created for Korea in
* the MAC. if we see the bogus country codes, program the MAC with
* the right country code.
*/
if (('K' == countryCode[0] && '1' == countryCode[1]) ||
('K' == countryCode[0] && '2' == countryCode[1]) ||
('K' == countryCode[0] && '3' == countryCode[1]) ||
('K' == countryCode[0] && '4' == countryCode[1])) {
/*
* replace the alternate Korea country codes, 'K1', 'K2', ..
* with 'KR' for Korea
*/
cc[1] = 'R';
}
/*
* country code is moved to mlme component, and it is limited to call
* legacy api, so required to call sch_edca_profile_update_all if
* overwrite country code in mlme component
*/
qdf_mem_copy(mac->mlme_cfg->reg.country_code, cc, CFG_COUNTRY_CODE_LEN);
mac->mlme_cfg->reg.country_code_len = CFG_COUNTRY_CODE_LEN;
sme_debug("CFG_COUNTRY_CODE changed");
sch_edca_profile_update_all(mac);
/*
* Need to let HALPHY know about the current domain so it can apply some
* domain-specific settings (TX filter...)
*/
}
QDF_STATUS csr_get_country_code(struct mac_context *mac, uint8_t *pBuf,
uint8_t *pbLen)
{
if (pBuf && pbLen && (*pbLen >= CFG_COUNTRY_CODE_LEN)) {
*pbLen = mac->mlme_cfg->reg.country_code_len;
qdf_mem_copy(pBuf, mac->mlme_cfg->reg.country_code,
(uint32_t)*pbLen);
return QDF_STATUS_SUCCESS;
}
return QDF_STATUS_E_INVAL;
}
QDF_STATUS csr_scan_abort_mac_scan(struct mac_context *mac_ctx,
uint32_t vdev_id,
uint32_t scan_id)
{
struct scan_cancel_request *req;
QDF_STATUS status;
struct wlan_objmgr_vdev *vdev;
req = qdf_mem_malloc(sizeof(*req));
if (!req)
return QDF_STATUS_E_NOMEM;
/* Get NL global context from objmgr*/
if (vdev_id == INVAL_VDEV_ID)
vdev = wlan_objmgr_pdev_get_first_vdev(mac_ctx->pdev,
WLAN_LEGACY_SME_ID);
else
vdev = wlan_objmgr_get_vdev_by_id_from_pdev(mac_ctx->pdev,
vdev_id, WLAN_LEGACY_SME_ID);
if (!vdev) {
sme_err("Failed get vdev");
qdf_mem_free(req);
return QDF_STATUS_E_INVAL;
}
req->vdev = vdev;
req->cancel_req.scan_id = scan_id;
req->cancel_req.pdev_id = wlan_objmgr_pdev_get_pdev_id(mac_ctx->pdev);
req->cancel_req.vdev_id = vdev_id;
if (scan_id != INVAL_SCAN_ID)
req->cancel_req.req_type = WLAN_SCAN_CANCEL_SINGLE;
if (vdev_id == INVAL_VDEV_ID)
req->cancel_req.req_type = WLAN_SCAN_CANCEL_PDEV_ALL;
else
req->cancel_req.req_type = WLAN_SCAN_CANCEL_VDEV_ALL;
status = ucfg_scan_cancel(req);
if (QDF_IS_STATUS_ERROR(status))
sme_err("Cancel scan request failed");
wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID);
return status;
}
QDF_STATUS csr_remove_nonscan_cmd_from_pending_list(struct mac_context *mac,
uint8_t sessionId,
eSmeCommandType commandType)
{
tDblLinkList localList;
tListElem *pEntry;
tSmeCmd *pCommand;
tListElem *pEntryToRemove;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
qdf_mem_zero(&localList, sizeof(tDblLinkList));
if (!QDF_IS_STATUS_SUCCESS(csr_ll_open(&localList))) {
sme_err("failed to open list");
return status;
}
pEntry = csr_nonscan_pending_ll_peek_head(mac, LL_ACCESS_NOLOCK);
/*
* Have to make sure we don't loop back to the head of the list,
* which will happen if the entry is NOT on the list
*/
while (pEntry) {
pEntryToRemove = pEntry;
pEntry = csr_nonscan_pending_ll_next(mac,
pEntry, LL_ACCESS_NOLOCK);
pCommand = GET_BASE_ADDR(pEntryToRemove, tSmeCmd, Link);
if ((pCommand->command == commandType) &&
(pCommand->sessionId == sessionId)) {
/* Insert to localList and remove later */
csr_ll_insert_tail(&localList, pEntryToRemove,
LL_ACCESS_NOLOCK);
status = QDF_STATUS_SUCCESS;
}
}
while ((pEntry = csr_ll_remove_head(&localList, LL_ACCESS_NOLOCK))) {
pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link);
sme_debug("Sending abort for command ID %d",
sessionId);
csr_release_command(mac, pCommand);
}
csr_ll_close(&localList);
return status;
}
bool csr_roam_is_valid_channel(struct mac_context *mac, uint8_t channel)
{
bool fValid = false;
uint32_t idx_valid_ch;
uint32_t len = mac->roam.numValidChannels;
for (idx_valid_ch = 0; (idx_valid_ch < len); idx_valid_ch++) {
if (channel == mac->roam.validChannelList[idx_valid_ch]) {
fValid = true;
break;
}
}
return fValid;
}
QDF_STATUS csr_scan_create_entry_in_scan_cache(struct mac_context *mac,
uint32_t sessionId,
struct qdf_mac_addr bssid,
uint8_t channel)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
tSirBssDescription *pNewBssDescriptor = NULL;
uint32_t size = 0;
if (!pSession) {
return QDF_STATUS_E_FAILURE;
}
sme_debug("Current bssid::"QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(pSession->pConnectBssDesc->bssId));
sme_debug("My bssid::"QDF_MAC_ADDR_STR" channel %d",
QDF_MAC_ADDR_ARRAY(bssid.bytes), channel);
size = pSession->pConnectBssDesc->length +
sizeof(pSession->pConnectBssDesc->length);
if (!size) {
sme_err("length of bss descriptor is 0");
return QDF_STATUS_E_FAILURE;
}
pNewBssDescriptor = qdf_mem_malloc(size);
if (pNewBssDescriptor)
return QDF_STATUS_E_FAILURE;
qdf_mem_copy(pNewBssDescriptor, pSession->pConnectBssDesc, size);
/* change the BSSID & channel as passed */
qdf_mem_copy(pNewBssDescriptor->bssId, bssid.bytes,
sizeof(tSirMacAddr));
pNewBssDescriptor->channelId = channel;
if (!csr_scan_append_bss_description(mac, pNewBssDescriptor)) {
sme_err("csr_scan_append_bss_description failed");
status = QDF_STATUS_E_FAILURE;
goto free_mem;
}
sme_err("entry successfully added in scan cache");
free_mem:
if (pNewBssDescriptor)
qdf_mem_free(pNewBssDescriptor);
return status;
}
#ifdef FEATURE_WLAN_ESE
/* Update the TSF with the difference in system time */
void update_cckmtsf(uint32_t *timeStamp0, uint32_t *timeStamp1,
uint64_t *incr)
{
uint64_t timeStamp64 = ((uint64_t) *timeStamp1 << 32) | (*timeStamp0);
timeStamp64 = (uint64_t)(timeStamp64 + (*incr));
*timeStamp0 = (uint32_t) (timeStamp64 & 0xffffffff);
*timeStamp1 = (uint32_t) ((timeStamp64 >> 32) & 0xffffffff);
}
#endif
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
QDF_STATUS
csr_rso_save_ap_to_scan_cache(struct mac_context *mac,
struct roam_offload_synch_ind *roam_sync_ind_ptr,
struct bss_description *bss_desc_ptr)
{
uint32_t length = 0;
struct tag_csrscan_result *scan_res_ptr = NULL;
length = roam_sync_ind_ptr->beaconProbeRespLength -
(SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET);
scan_res_ptr = qdf_mem_malloc(sizeof(struct tag_csrscan_result) +
length);
if (!scan_res_ptr)
return QDF_STATUS_E_NOMEM;
qdf_mem_copy(&scan_res_ptr->Result.BssDescriptor,
bss_desc_ptr,
(sizeof(tSirBssDescription) + length));
sme_debug("LFR3:Add BSSID to scan cache" QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(scan_res_ptr->Result.BssDescriptor.bssId));
csr_scan_add_result(mac, scan_res_ptr);
csr_free_scan_result_entry(mac, scan_res_ptr);
return QDF_STATUS_SUCCESS;
}
#endif
/**
* csr_get_fst_bssdescr_ptr() - This function returns the pointer to first bss
* description from scan handle
* @result_handle: an object for the result.
*
* Return: first bss descriptor from the scan handle.
*/
struct bss_description*
csr_get_fst_bssdescr_ptr(tScanResultHandle result_handle)
{
tListElem *first_element = NULL;
struct tag_csrscan_result *scan_result = NULL;
struct scan_result_list *bss_list =
(struct scan_result_list *)result_handle;
if (!bss_list) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("Empty bss_list"));
return NULL;
}
if (csr_ll_is_list_empty(&bss_list->List, LL_ACCESS_NOLOCK)) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("bss_list->List is empty"));
return NULL;
}
first_element = csr_ll_peek_head(&bss_list->List, LL_ACCESS_NOLOCK);
if (!first_element) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("peer head return NULL"));
return NULL;
}
scan_result = GET_BASE_ADDR(first_element, struct tag_csrscan_result,
Link);
return &scan_result->Result.BssDescriptor;
}
/**
* csr_get_bssdescr_from_scan_handle() - This function to extract
* first bss description from scan handle
* @result_handle: an object for the result.
*
* This function is written to extract first bss from scan handle.
*
* Return: first bss descriptor from the scan handle.
*/
tSirBssDescription *
csr_get_bssdescr_from_scan_handle(tScanResultHandle result_handle,
tSirBssDescription *bss_descr)
{
tListElem *first_element = NULL;
struct tag_csrscan_result *scan_result = NULL;
struct scan_result_list *bss_list =
(struct scan_result_list *)result_handle;
if (!bss_list) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("Empty bss_list"));
return NULL;
}
if (csr_ll_is_list_empty(&bss_list->List, LL_ACCESS_NOLOCK)) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("bss_list->List is empty"));
qdf_mem_free(bss_list);
return NULL;
}
first_element = csr_ll_peek_head(&bss_list->List, LL_ACCESS_NOLOCK);
if (first_element) {
scan_result = GET_BASE_ADDR(first_element,
struct tag_csrscan_result,
Link);
qdf_mem_copy(bss_descr,
&scan_result->Result.BssDescriptor,
sizeof(tSirBssDescription));
}
return bss_descr;
}
uint8_t
csr_get_channel_for_hw_mode_change(struct mac_context *mac_ctx,
tScanResultHandle result_handle,
uint32_t session_id)
{
tListElem *next_element = NULL;
struct tag_csrscan_result *scan_result = NULL;
struct scan_result_list *bss_list =
(struct scan_result_list *)result_handle;
uint8_t channel_id = 0;
if (!bss_list) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("Empty bss_list"));
goto end;
}
if (policy_mgr_is_hw_dbs_capable(mac_ctx->psoc) == false) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
FL("driver isn't dbs capable"));
goto end;
}
if (policy_mgr_is_current_hwmode_dbs(mac_ctx->psoc)) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
FL("driver is already in DBS"));
goto end;
}
if (!policy_mgr_is_dbs_allowed_for_concurrency(mac_ctx->psoc,
QDF_STA_MODE)) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
FL("DBS is not allowed for this concurrency combo"));
goto end;
}
if (!policy_mgr_is_hw_dbs_2x2_capable(mac_ctx->psoc) &&
!policy_mgr_get_connection_count(mac_ctx->psoc)) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
FL("1x1 DBS HW with no prior connection"));
goto end;
}
if (csr_ll_is_list_empty(&bss_list->List, LL_ACCESS_NOLOCK)) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("bss_list->List is empty"));
qdf_mem_free(bss_list);
goto end;
}
next_element = csr_ll_peek_head(&bss_list->List, LL_ACCESS_NOLOCK);
while (next_element) {
scan_result = GET_BASE_ADDR(next_element,
struct tag_csrscan_result,
Link);
if (policy_mgr_is_hw_dbs_2x2_capable(mac_ctx->psoc)) {
if (WLAN_REG_IS_24GHZ_CH
(scan_result->Result.BssDescriptor.channelId)) {
channel_id =
scan_result->
Result.BssDescriptor.channelId;
break;
}
} else {
if (WLAN_REG_IS_24GHZ_CH
(scan_result->Result.BssDescriptor.channelId) &&
policy_mgr_is_any_mode_active_on_band_along_with_session
(mac_ctx->psoc,
session_id, POLICY_MGR_BAND_5)) {
channel_id =
scan_result->
Result.BssDescriptor.channelId;
break;
}
if (WLAN_REG_IS_5GHZ_CH
(scan_result->Result.BssDescriptor.channelId) &&
policy_mgr_is_any_mode_active_on_band_along_with_session
(mac_ctx->psoc,
session_id, POLICY_MGR_BAND_24)) {
channel_id =
scan_result->
Result.BssDescriptor.channelId;
break;
}
}
next_element = csr_ll_next(&bss_list->List, next_element,
LL_ACCESS_NOLOCK);
}
end:
return channel_id;
}
uint8_t
csr_scan_get_channel_for_hw_mode_change(struct mac_context *mac_ctx,
uint32_t session_id,
struct csr_roam_profile *profile)
{
tScanResultHandle result_handle = NULL;
QDF_STATUS status;
uint8_t first_ap_ch = 0;
uint8_t candidate_chan;
status = sme_get_ap_channel_from_scan_cache(profile, &result_handle,
&first_ap_ch);
if (status != QDF_STATUS_SUCCESS || !result_handle || !first_ap_ch) {
if (result_handle)
sme_scan_result_purge(result_handle);
sme_err("fail get scan result: status %d first ap ch %d",
status, first_ap_ch);
return 0;
}
if (!policy_mgr_check_for_session_conc(mac_ctx->psoc, session_id,
first_ap_ch)) {
sme_scan_result_purge(result_handle);
sme_err("Conc not allowed for the session %d ch %d",
session_id, first_ap_ch);
return 0;
}
candidate_chan = csr_get_channel_for_hw_mode_change(mac_ctx,
result_handle,
session_id);
sme_scan_result_purge(result_handle);
if (!candidate_chan)
candidate_chan = first_ap_ch;
sme_debug("session %d hw mode check candidate_chan %d", session_id,
candidate_chan);
return candidate_chan;
}
static enum wlan_auth_type csr_covert_auth_type_new(eCsrAuthType auth)
{
switch (auth) {
case eCSR_AUTH_TYPE_NONE:
case eCSR_AUTH_TYPE_OPEN_SYSTEM:
return WLAN_AUTH_TYPE_OPEN_SYSTEM;
case eCSR_AUTH_TYPE_SHARED_KEY:
return WLAN_AUTH_TYPE_SHARED_KEY;
case eCSR_AUTH_TYPE_AUTOSWITCH:
return WLAN_AUTH_TYPE_AUTOSWITCH;
case eCSR_AUTH_TYPE_WPA:
return WLAN_AUTH_TYPE_WPA;
case eCSR_AUTH_TYPE_WPA_PSK:
return WLAN_AUTH_TYPE_WPA_PSK;
case eCSR_AUTH_TYPE_WPA_NONE:
return WLAN_AUTH_TYPE_WPA_NONE;
case eCSR_AUTH_TYPE_RSN:
return WLAN_AUTH_TYPE_RSN;
case eCSR_AUTH_TYPE_RSN_PSK:
return WLAN_AUTH_TYPE_RSN_PSK;
case eCSR_AUTH_TYPE_FT_RSN:
return WLAN_AUTH_TYPE_FT_RSN;
case eCSR_AUTH_TYPE_FT_RSN_PSK:
return WLAN_AUTH_TYPE_FT_RSN_PSK;
case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE:
return WLAN_AUTH_TYPE_WAPI_WAI_CERTIFICATE;
case eCSR_AUTH_TYPE_WAPI_WAI_PSK:
return WLAN_AUTH_TYPE_WAPI_WAI_PSK;
case eCSR_AUTH_TYPE_CCKM_WPA:
return WLAN_AUTH_TYPE_CCKM_WPA;
case eCSR_AUTH_TYPE_CCKM_RSN:
return WLAN_AUTH_TYPE_CCKM_RSN;
case eCSR_AUTH_TYPE_RSN_PSK_SHA256:
return WLAN_AUTH_TYPE_RSN_PSK_SHA256;
case eCSR_AUTH_TYPE_RSN_8021X_SHA256:
return WLAN_AUTH_TYPE_RSN_8021X_SHA256;
case eCSR_AUTH_TYPE_FILS_SHA256:
return WLAN_AUTH_TYPE_FILS_SHA256;
case eCSR_AUTH_TYPE_FILS_SHA384:
return WLAN_AUTH_TYPE_FILS_SHA384;
case eCSR_AUTH_TYPE_FT_FILS_SHA256:
return WLAN_AUTH_TYPE_FT_FILS_SHA256;
case eCSR_AUTH_TYPE_FT_FILS_SHA384:
return WLAN_AUTH_TYPE_FT_FILS_SHA384;
case eCSR_AUTH_TYPE_DPP_RSN:
return WLAN_AUTH_TYPE_DPP_RSN;
case eCSR_AUTH_TYPE_OWE:
return WLAN_AUTH_TYPE_OWE;
case eCSR_AUTH_TYPE_SUITEB_EAP_SHA256:
return WLAN_AUTH_TYPE_SUITEB_EAP_SHA256;
case eCSR_AUTH_TYPE_SUITEB_EAP_SHA384:
return WLAN_AUTH_TYPE_SUITEB_EAP_SHA384;
case eCSR_AUTH_TYPE_SAE:
return WLAN_AUTH_TYPE_SAE;
case eCSR_AUTH_TYPE_OSEN:
return WLAN_AUTH_TYPE_OSEN;
case eCSR_AUTH_TYPE_FT_SAE:
return WLAN_AUTH_TYPE_FT_SAE;
case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384:
return WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384;
case eCSR_NUM_OF_SUPPORT_AUTH_TYPE:
default:
return WLAN_AUTH_TYPE_OPEN_SYSTEM;
}
}
static eCsrAuthType csr_covert_auth_type_old(enum wlan_auth_type auth)
{
switch (auth) {
case WLAN_AUTH_TYPE_OPEN_SYSTEM:
return eCSR_AUTH_TYPE_OPEN_SYSTEM;
case WLAN_AUTH_TYPE_SHARED_KEY:
return eCSR_AUTH_TYPE_SHARED_KEY;
case WLAN_AUTH_TYPE_AUTOSWITCH:
return eCSR_AUTH_TYPE_AUTOSWITCH;
case WLAN_AUTH_TYPE_WPA:
return eCSR_AUTH_TYPE_WPA;
case WLAN_AUTH_TYPE_WPA_PSK:
return eCSR_AUTH_TYPE_WPA_PSK;
case WLAN_AUTH_TYPE_WPA_NONE:
return eCSR_AUTH_TYPE_WPA_NONE;
case WLAN_AUTH_TYPE_RSN:
return eCSR_AUTH_TYPE_RSN;
case WLAN_AUTH_TYPE_RSN_PSK:
return eCSR_AUTH_TYPE_RSN_PSK;
case WLAN_AUTH_TYPE_FT_RSN:
return eCSR_AUTH_TYPE_FT_RSN;
case WLAN_AUTH_TYPE_FT_RSN_PSK:
return eCSR_AUTH_TYPE_FT_RSN_PSK;
case WLAN_AUTH_TYPE_WAPI_WAI_CERTIFICATE:
return eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE;
case WLAN_AUTH_TYPE_WAPI_WAI_PSK:
return eCSR_AUTH_TYPE_WAPI_WAI_PSK;
case WLAN_AUTH_TYPE_CCKM_WPA:
return eCSR_AUTH_TYPE_CCKM_WPA;
case WLAN_AUTH_TYPE_CCKM_RSN:
return eCSR_AUTH_TYPE_CCKM_RSN;
case WLAN_AUTH_TYPE_RSN_PSK_SHA256:
return eCSR_AUTH_TYPE_RSN_PSK_SHA256;
case WLAN_AUTH_TYPE_RSN_8021X_SHA256:
return eCSR_AUTH_TYPE_RSN_8021X_SHA256;
case WLAN_AUTH_TYPE_FILS_SHA256:
return eCSR_AUTH_TYPE_FILS_SHA256;
case WLAN_AUTH_TYPE_FILS_SHA384:
return eCSR_AUTH_TYPE_FILS_SHA384;
case WLAN_AUTH_TYPE_FT_FILS_SHA256:
return eCSR_AUTH_TYPE_FT_FILS_SHA256;
case WLAN_AUTH_TYPE_FT_FILS_SHA384:
return eCSR_AUTH_TYPE_FT_FILS_SHA384;
case WLAN_AUTH_TYPE_DPP_RSN:
return eCSR_AUTH_TYPE_DPP_RSN;
case WLAN_AUTH_TYPE_OWE:
return eCSR_AUTH_TYPE_OWE;
case WLAN_AUTH_TYPE_SUITEB_EAP_SHA256:
return eCSR_AUTH_TYPE_SUITEB_EAP_SHA256;
case WLAN_AUTH_TYPE_SUITEB_EAP_SHA384:
return eCSR_AUTH_TYPE_SUITEB_EAP_SHA384;
case WLAN_AUTH_TYPE_SAE:
return eCSR_AUTH_TYPE_SAE;
case WLAN_AUTH_TYPE_OSEN:
return eCSR_AUTH_TYPE_OSEN;
case WLAN_AUTH_TYPE_FT_SAE:
return eCSR_AUTH_TYPE_FT_SAE;
case WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384:
return eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384;
case WLAN_NUM_OF_SUPPORT_AUTH_TYPE:
default:
return eCSR_AUTH_TYPE_OPEN_SYSTEM;
}
}
static enum wlan_enc_type csr_covert_enc_type_new(eCsrEncryptionType enc)
{
switch (enc) {
case eCSR_ENCRYPT_TYPE_NONE:
return WLAN_ENCRYPT_TYPE_NONE;
case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY:
return WLAN_ENCRYPT_TYPE_WEP40_STATICKEY;
case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY:
return WLAN_ENCRYPT_TYPE_WEP104_STATICKEY;
case eCSR_ENCRYPT_TYPE_WEP40:
return WLAN_ENCRYPT_TYPE_WEP40;
case eCSR_ENCRYPT_TYPE_WEP104:
return WLAN_ENCRYPT_TYPE_WEP104;
case eCSR_ENCRYPT_TYPE_TKIP:
return WLAN_ENCRYPT_TYPE_TKIP;
case eCSR_ENCRYPT_TYPE_AES:
return WLAN_ENCRYPT_TYPE_AES;
case eCSR_ENCRYPT_TYPE_WPI:
return WLAN_ENCRYPT_TYPE_WPI;
case eCSR_ENCRYPT_TYPE_KRK:
return WLAN_ENCRYPT_TYPE_KRK;
case eCSR_ENCRYPT_TYPE_BTK:
return WLAN_ENCRYPT_TYPE_BTK;
case eCSR_ENCRYPT_TYPE_AES_CMAC:
return WLAN_ENCRYPT_TYPE_AES_CMAC;
case eCSR_ENCRYPT_TYPE_AES_GCMP:
return WLAN_ENCRYPT_TYPE_AES_GCMP;
case eCSR_ENCRYPT_TYPE_AES_GCMP_256:
return WLAN_ENCRYPT_TYPE_AES_GCMP_256;
case eCSR_ENCRYPT_TYPE_ANY:
default:
return WLAN_ENCRYPT_TYPE_NONE;
}
}
static eCsrEncryptionType csr_covert_enc_type_old(enum wlan_enc_type enc)
{
switch (enc) {
case WLAN_ENCRYPT_TYPE_NONE:
return eCSR_ENCRYPT_TYPE_NONE;
case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY:
return eCSR_ENCRYPT_TYPE_WEP40_STATICKEY;
case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY:
return eCSR_ENCRYPT_TYPE_WEP104_STATICKEY;
case WLAN_ENCRYPT_TYPE_WEP40:
return eCSR_ENCRYPT_TYPE_WEP40;
case WLAN_ENCRYPT_TYPE_WEP104:
return eCSR_ENCRYPT_TYPE_WEP104;
case WLAN_ENCRYPT_TYPE_TKIP:
return eCSR_ENCRYPT_TYPE_TKIP;
case WLAN_ENCRYPT_TYPE_AES:
return eCSR_ENCRYPT_TYPE_AES;
case WLAN_ENCRYPT_TYPE_WPI:
return eCSR_ENCRYPT_TYPE_WPI;
case WLAN_ENCRYPT_TYPE_KRK:
return eCSR_ENCRYPT_TYPE_KRK;
case WLAN_ENCRYPT_TYPE_BTK:
return eCSR_ENCRYPT_TYPE_BTK;
case WLAN_ENCRYPT_TYPE_AES_CMAC:
return eCSR_ENCRYPT_TYPE_AES_CMAC;
case WLAN_ENCRYPT_TYPE_AES_GCMP:
return eCSR_ENCRYPT_TYPE_AES_GCMP;
case WLAN_ENCRYPT_TYPE_AES_GCMP_256:
return eCSR_ENCRYPT_TYPE_AES_GCMP_256;
case WLAN_ENCRYPT_TYPE_ANY:
default:
return eCSR_ENCRYPT_TYPE_NONE;
}
}
#ifdef WLAN_FEATURE_11W
/**
* csr_update_pmf_cap: Updates PMF cap
* @src_filter: Source filter
* @dst_filter: Destination filter
*
* Return: None
*/
static void csr_update_pmf_cap(tCsrScanResultFilter *src_filter,
struct scan_filter *dst_filter) {
if (src_filter->MFPCapable || src_filter->MFPEnabled)
dst_filter->pmf_cap = WLAN_PMF_CAPABLE;
if (src_filter->MFPRequired)
dst_filter->pmf_cap = WLAN_PMF_REQUIRED;
}
#else
static inline void csr_update_pmf_cap(tCsrScanResultFilter *src_filter,
struct scan_filter *dst_filter)
{}
#endif
/**
* csr_convert_dotllmod_phymode: Convert eCsrPhyMode to wlan_phymode
* @dotllmode: phy mode
*
* Return: returns enum wlan_phymode
*/
static enum wlan_phymode csr_convert_dotllmod_phymode(eCsrPhyMode dotllmode)
{
enum wlan_phymode con_phy_mode;
switch (dotllmode) {
case eCSR_DOT11_MODE_abg:
con_phy_mode = WLAN_PHYMODE_AUTO;
break;
case eCSR_DOT11_MODE_11a:
con_phy_mode = WLAN_PHYMODE_11A;
break;
case eCSR_DOT11_MODE_11b:
con_phy_mode = WLAN_PHYMODE_11B;
break;
case eCSR_DOT11_MODE_11g:
con_phy_mode = WLAN_PHYMODE_11G;
break;
case eCSR_DOT11_MODE_11n:
con_phy_mode = WLAN_PHYMODE_11NA_HT20;
break;
case eCSR_DOT11_MODE_11g_ONLY:
con_phy_mode = WLAN_PHYMODE_11G;
break;
case eCSR_DOT11_MODE_11n_ONLY:
con_phy_mode = WLAN_PHYMODE_11NA_HT20;
break;
case eCSR_DOT11_MODE_11b_ONLY:
con_phy_mode = WLAN_PHYMODE_11B;
break;
case eCSR_DOT11_MODE_11ac:
con_phy_mode = WLAN_PHYMODE_11AC_VHT160;
break;
case eCSR_DOT11_MODE_11ac_ONLY:
con_phy_mode = WLAN_PHYMODE_11AC_VHT160;
break;
case eCSR_DOT11_MODE_AUTO:
con_phy_mode = WLAN_PHYMODE_AUTO;
break;
case eCSR_DOT11_MODE_11ax:
con_phy_mode = WLAN_PHYMODE_11AXA_HE160;
break;
case eCSR_DOT11_MODE_11ax_ONLY:
con_phy_mode = WLAN_PHYMODE_11AXA_HE160;
break;
default:
con_phy_mode = WLAN_PHYMODE_AUTO;
break;
}
return con_phy_mode;
}
#ifdef WLAN_ADAPTIVE_11R
/**
* csr_update_adaptive_11r_scan_filter - Copy adaptive 11r ini to scan
* module
* @mac_ctx: Pointer to mac_context
* @scan_filter: Scan filter to be sent to scan module
*
* Return: None
*/
static void
csr_update_adaptive_11r_scan_filter(struct mac_context *mac_ctx,
struct scan_filter *filter)
{
filter->enable_adaptive_11r =
mac_ctx->mlme_cfg->lfr.enable_adaptive_11r;
}
#else
static inline void
csr_update_adaptive_11r_scan_filter(struct mac_context *mac_ctx,
struct scan_filter *filter)
{
filter->enable_adaptive_11r = false;
}
#endif
static QDF_STATUS csr_prepare_scan_filter(struct mac_context *mac_ctx,
tCsrScanResultFilter *pFilter,
struct scan_filter *filter)
{
int i;
uint32_t len = 0;
QDF_STATUS status;
enum policy_mgr_con_mode new_mode;
filter->num_of_bssid = pFilter->BSSIDs.numOfBSSIDs;
if (filter->num_of_bssid > WLAN_SCAN_FILTER_NUM_BSSID)
filter->num_of_bssid = WLAN_SCAN_FILTER_NUM_BSSID;
for (i = 0; i < filter->num_of_bssid; i++)
qdf_mem_copy(filter->bssid_list[i].bytes,
pFilter->BSSIDs.bssid[i].bytes,
QDF_MAC_ADDR_SIZE);
filter->num_of_ssid = pFilter->SSIDs.numOfSSIDs;
if (filter->num_of_ssid > WLAN_SCAN_FILTER_NUM_SSID)
filter->num_of_ssid = WLAN_SCAN_FILTER_NUM_SSID;
for (i = 0; i < filter->num_of_ssid; i++) {
filter->ssid_list[i].length =
pFilter->SSIDs.SSIDList[i].SSID.length;
qdf_mem_copy(filter->ssid_list[i].ssid,
pFilter->SSIDs.SSIDList[i].SSID.ssId,
filter->ssid_list[i].length);
}
filter->num_of_channels =
pFilter->ChannelInfo.numOfChannels;
if (filter->num_of_channels > QDF_MAX_NUM_CHAN)
filter->num_of_channels = QDF_MAX_NUM_CHAN;
qdf_mem_copy(filter->channel_list,
pFilter->ChannelInfo.ChannelList,
filter->num_of_channels);
if (pFilter->realm_check) {
filter->fils_scan_filter.realm_check = true;
qdf_mem_copy(filter->fils_scan_filter.fils_realm,
pFilter->fils_realm, REAM_HASH_LEN);
}
if (pFilter->force_rsne_override) {
sme_debug("force_rsne_override set auth type and enctype to any and ignore pmf cap");
filter->num_of_auth = 1;
filter->auth_type[0] = WLAN_AUTH_TYPE_ANY;
filter->num_of_enc_type = 1;
filter->enc_type[0] = WLAN_ENCRYPT_TYPE_ANY;
filter->num_of_mc_enc_type = 1;
filter->mc_enc_type[0] = WLAN_ENCRYPT_TYPE_ANY;
filter->ignore_pmf_cap = true;
} else {
filter->num_of_auth =
pFilter->authType.numEntries;
if (filter->num_of_auth > WLAN_NUM_OF_SUPPORT_AUTH_TYPE)
filter->num_of_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE;
for (i = 0; i < filter->num_of_auth; i++)
filter->auth_type[i] =
csr_covert_auth_type_new(
pFilter->authType.authType[i]);
filter->num_of_enc_type =
pFilter->EncryptionType.numEntries;
if (filter->num_of_enc_type > WLAN_NUM_OF_ENCRYPT_TYPE)
filter->num_of_enc_type = WLAN_NUM_OF_ENCRYPT_TYPE;
for (i = 0; i < filter->num_of_enc_type; i++)
filter->enc_type[i] =
csr_covert_enc_type_new(
pFilter->EncryptionType.encryptionType[i]);
filter->num_of_mc_enc_type =
pFilter->mcEncryptionType.numEntries;
if (filter->num_of_mc_enc_type > WLAN_NUM_OF_ENCRYPT_TYPE)
filter->num_of_mc_enc_type = WLAN_NUM_OF_ENCRYPT_TYPE;
for (i = 0; i < filter->num_of_mc_enc_type; i++)
filter->mc_enc_type[i] =
csr_covert_enc_type_new(
pFilter->mcEncryptionType.encryptionType[i]);
}
qdf_mem_copy(filter->country, pFilter->countryCode,
CFG_COUNTRY_CODE_LEN);
if (pFilter->bWPSAssociation || pFilter->bOSENAssociation)
filter->ignore_auth_enc_type = true;
filter->rrm_measurement_filter = pFilter->fMeasurement;
filter->mobility_domain = pFilter->mdid.mobility_domain;
filter->p2p_results = pFilter->p2pResult;
csr_update_pmf_cap(pFilter, filter);
if (pFilter->BSSType == eCSR_BSS_TYPE_INFRASTRUCTURE)
filter->bss_type = WLAN_TYPE_BSS;
else if (pFilter->BSSType == eCSR_BSS_TYPE_IBSS ||
pFilter->BSSType == eCSR_BSS_TYPE_START_IBSS)
filter->bss_type = WLAN_TYPE_IBSS;
else
filter->bss_type = WLAN_TYPE_ANY;
filter->dot11_mode = csr_convert_dotllmod_phymode(pFilter->phyMode);
// enable bss scoring for only STA mode
if (pFilter->csrPersona == QDF_STA_MODE)
filter->bss_scoring_required = true;
else
filter->bss_scoring_required = false;
csr_update_adaptive_11r_scan_filter(mac_ctx, filter);
if (!pFilter->BSSIDs.numOfBSSIDs) {
if (policy_mgr_map_concurrency_mode(
&pFilter->csrPersona, &new_mode)) {
status = policy_mgr_get_pcl(mac_ctx->psoc, new_mode,
filter->pcl_channel_list, &len,
filter->pcl_weight_list, QDF_MAX_NUM_CHAN);
filter->num_of_pcl_channels = (uint8_t)len;
}
}
qdf_mem_copy(filter->bssid_hint.bytes,
pFilter->bssid_hint.bytes,
QDF_MAC_ADDR_SIZE);
return QDF_STATUS_SUCCESS;
}
#ifdef WLAN_FEATURE_FILS_SK
/**
* csr_update_bss_with_fils_data: Fill FILS params in bss desc from scan entry
* @mac_ctx: mac context
* @scan_entry: scan entry
* @bss_descr: bss description
*/
static void csr_update_bss_with_fils_data(struct mac_context *mac_ctx,
struct scan_cache_entry *scan_entry,
tSirBssDescription *bss_descr)
{
int ret;
tDot11fIEfils_indication fils_indication = {0};
struct sir_fils_indication fils_ind;
if (!scan_entry->ie_list.fils_indication)
return;
ret = dot11f_unpack_ie_fils_indication(mac_ctx,
scan_entry->ie_list.fils_indication +
SIR_FILS_IND_ELEM_OFFSET,
*(scan_entry->ie_list.fils_indication + 1),
&fils_indication, false);
if (DOT11F_FAILED(ret)) {
sme_err("unpack failed ret: 0x%x", ret);
return;
}
update_fils_data(&fils_ind, &fils_indication);
if (fils_ind.realm_identifier.realm_cnt > SIR_MAX_REALM_COUNT)
fils_ind.realm_identifier.realm_cnt = SIR_MAX_REALM_COUNT;
bss_descr->fils_info_element.realm_cnt =
fils_ind.realm_identifier.realm_cnt;
qdf_mem_copy(bss_descr->fils_info_element.realm,
fils_ind.realm_identifier.realm,
bss_descr->fils_info_element.realm_cnt * SIR_REALM_LEN);
if (fils_ind.cache_identifier.is_present) {
bss_descr->fils_info_element.is_cache_id_present = true;
qdf_mem_copy(bss_descr->fils_info_element.cache_id,
fils_ind.cache_identifier.identifier, CACHE_ID_LEN);
}
if (fils_ind.is_fils_sk_auth_supported)
bss_descr->fils_info_element.is_fils_sk_supported = true;
}
#else
static void csr_update_bss_with_fils_data(struct mac_context *mac_ctx,
struct scan_cache_entry *scan_entry,
tSirBssDescription *bss_descr)
{ }
#endif
/**
* csr_is_assoc_disallowed() - Find if assoc disallowed
* bit is set in AP's beacon or probe response
* @mac_ctx: mac context
* @scan_entry: scan entry
*
* Return: True if assoc disallowed is set else false
*/
static bool csr_is_assoc_disallowed(struct mac_context *mac_ctx,
struct scan_cache_entry *scan_entry)
{
int ret;
tDot11fIEMBO_IE mbo_ie = {0};
uint8_t *mbo_oce;
mbo_oce = util_scan_entry_mbo_oce(scan_entry);
if (!mbo_oce)
return false;
ret = dot11f_unpack_ie_MBO_IE(mac_ctx, mbo_oce + SIR_MBO_ELEM_OFFSET,
*(mbo_oce + 1) - SIR_MAC_MBO_OUI_SIZE,
&mbo_ie, false);
if (DOT11F_FAILED(ret)) {
sme_err("unpack failed ret: 0x%x", ret);
return false;
}
return mbo_ie.assoc_disallowed.present;
}
static QDF_STATUS csr_fill_bss_from_scan_entry(struct mac_context *mac_ctx,
struct scan_cache_entry *scan_entry,
struct tag_csrscan_result **p_result)
{
tDot11fBeaconIEs *bcn_ies;
tSirBssDescription *bss_desc;
tCsrScanResultInfo *result_info;
tpSirMacMgmtHdr hdr;
uint8_t *ie_ptr;
struct tag_csrscan_result *bss;
uint32_t bss_len, alloc_len, ie_len;
QDF_STATUS status;
ie_len = util_scan_entry_ie_len(scan_entry);
ie_ptr = util_scan_entry_ie_data(scan_entry);
hdr = (tpSirMacMgmtHdr)scan_entry->raw_frame.ptr;
bss_len = (uint16_t)(offsetof(tSirBssDescription,
ieFields[0]) + ie_len);
alloc_len = sizeof(struct tag_csrscan_result) + bss_len;
bss = qdf_mem_malloc(alloc_len);
if (!bss)
return QDF_STATUS_E_NOMEM;
bss->AgingCount =
(int32_t) mac_ctx->roam.configParam.agingCount;
bss->ucEncryptionType =
csr_covert_enc_type_old(scan_entry->neg_sec_info.uc_enc);
bss->mcEncryptionType =
csr_covert_enc_type_old(scan_entry->neg_sec_info.mc_enc);
bss->authType =
csr_covert_auth_type_old(scan_entry->neg_sec_info.auth_type);
bss->bss_score = scan_entry->bss_score;
result_info = &bss->Result;
result_info->ssId.length = scan_entry->ssid.length;
qdf_mem_copy(result_info->ssId.ssId,
scan_entry->ssid.ssid,
result_info->ssId.length);
result_info->timer = scan_entry->hidden_ssid_timestamp;
bss_desc = &result_info->BssDescriptor;
bss_desc->length = (uint16_t) (offsetof(tSirBssDescription,
ieFields[0]) - sizeof(bss_desc->length) + ie_len);
qdf_mem_copy(bss_desc->bssId,
scan_entry->bssid.bytes,
QDF_MAC_ADDR_SIZE);
bss_desc->scansystimensec = scan_entry->scan_entry_time;
qdf_mem_copy(bss_desc->timeStamp,
scan_entry->tsf_info.data, 8);
bss_desc->beaconInterval = scan_entry->bcn_int;
bss_desc->capabilityInfo = scan_entry->cap_info.value;
if (WLAN_REG_IS_5GHZ_CH(scan_entry->channel.chan_idx))
bss_desc->nwType = eSIR_11A_NW_TYPE;
else if (scan_entry->phy_mode == WLAN_PHYMODE_11B)
bss_desc->nwType = eSIR_11B_NW_TYPE;
else
bss_desc->nwType = eSIR_11G_NW_TYPE;
bss_desc->rssi = scan_entry->rssi_raw;
bss_desc->rssi_raw = scan_entry->rssi_raw;
/* channelId what peer sent in beacon/probersp. */
bss_desc->channelId =
scan_entry->channel.chan_idx;
/* channelId on which we are parked at. */
/* used only in scan case. */
bss_desc->channelIdSelf =
scan_entry->channel.chan_idx;
bss_desc->rx_channel = bss_desc->channelIdSelf;
bss_desc->received_time =
scan_entry->scan_entry_time;
bss_desc->startTSF[0] =
mac_ctx->rrm.rrmPEContext.startTSF[0];
bss_desc->startTSF[1] =
mac_ctx->rrm.rrmPEContext.startTSF[1];
bss_desc->parentTSF =
scan_entry->rrm_parent_tsf;
bss_desc->fProbeRsp = (scan_entry->frm_subtype ==
MGMT_SUBTYPE_PROBE_RESP);
bss_desc->seq_ctrl = hdr->seqControl;
bss_desc->tsf_delta = scan_entry->tsf_delta;
bss_desc->assoc_disallowed = csr_is_assoc_disallowed(mac_ctx,
scan_entry);
bss_desc->adaptive_11r_ap = scan_entry->adaptive_11r_ap;
qdf_mem_copy(&bss_desc->mbssid_info, &scan_entry->mbssid_info,
sizeof(struct scan_mbssid_info));
qdf_mem_copy((uint8_t *) &bss_desc->ieFields,
ie_ptr, ie_len);
status = csr_get_parsed_bss_description_ies(mac_ctx,
bss_desc, &bcn_ies);
if (QDF_IS_STATUS_ERROR(status)) {
qdf_mem_free(bss);
return status;
}
result_info->pvIes = bcn_ies;
if (bcn_ies->MobilityDomain.present) {
bss_desc->mdiePresent = true;
qdf_mem_copy((uint8_t *)&(bss_desc->mdie[0]),
(uint8_t *)&(bcn_ies->MobilityDomain.MDID),
sizeof(uint16_t));
bss_desc->mdie[2] =
((bcn_ies->MobilityDomain.overDSCap << 0) |
(bcn_ies->MobilityDomain.resourceReqCap << 1));
}
#ifdef FEATURE_WLAN_ESE
if (bcn_ies->QBSSLoad.present) {
bss_desc->QBSSLoad_present = true;
bss_desc->QBSSLoad_avail =
bcn_ies->QBSSLoad.avail;
}
#endif
csr_update_bss_with_fils_data(mac_ctx, scan_entry, bss_desc);
if (scan_entry->alt_wcn_ie.ptr) {
bss_desc->WscIeLen = scan_entry->alt_wcn_ie.len;
qdf_mem_copy(bss_desc->WscIeProbeRsp,
scan_entry->alt_wcn_ie.ptr,
scan_entry->alt_wcn_ie.len);
}
*p_result = bss;
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS csr_parse_scan_list(struct mac_context *mac_ctx,
struct scan_result_list *ret_list,
qdf_list_t *scan_list)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct tag_csrscan_result *pResult = NULL;
struct scan_cache_node *cur_node = NULL;
struct scan_cache_node *next_node = NULL;
status =
qdf_list_peek_front(scan_list,
(qdf_list_node_t **) &cur_node);
while (cur_node) {
qdf_list_peek_next(
scan_list,
(qdf_list_node_t *) cur_node,
(qdf_list_node_t **) &next_node);
status = csr_fill_bss_from_scan_entry(mac_ctx,
cur_node->entry, &pResult);
if (QDF_IS_STATUS_ERROR(status))
return status;
if (pResult)
csr_ll_insert_tail(&ret_list->List, &pResult->Link,
LL_ACCESS_NOLOCK);
cur_node = next_node;
next_node = NULL;
}
return status;
}
/**
* csr_remove_ap_due_to_rssi() - check if bss is present in
* list of BSSID which rejected Assoc due to RSSI
* @list: rssi based rejected BSS list
* @bss_descr: pointer to bss description
*
* Check if the time interval indicated in last Assoc reject
* has expired OR rssi has improved by margin indicated
* in last Assoc reject. If any of the condition match remove
* the AP from the avoid list, else do not try to conenct
* to the AP
*
* Return: true if connection cannot be tried with AP else false
*/
static bool csr_remove_ap_due_to_rssi(qdf_list_t *list,
tSirBssDescription *bss_descr)
{
QDF_STATUS status;
struct sir_rssi_disallow_lst *cur_node = NULL;
qdf_list_node_t *cur_lst = NULL, *next_lst = NULL;
qdf_time_t cur_time;
uint32_t time_diff;
if (!qdf_list_size(list))
return false;
cur_time = qdf_do_div(qdf_get_monotonic_boottime(),
QDF_MC_TIMER_TO_MS_UNIT);
qdf_list_peek_front(list, &cur_lst);
while (cur_lst) {
cur_node = qdf_container_of(cur_lst,
struct sir_rssi_disallow_lst, node);
qdf_list_peek_next(list, cur_lst, &next_lst);
time_diff = cur_time - cur_node->time_during_rejection;
if ((time_diff > cur_node->retry_delay)) {
sme_debug("Remove %pM as time diff %d is greater retry delay %d",
cur_node->bssid.bytes, time_diff,
cur_node->retry_delay);
status = qdf_list_remove_node(list, cur_lst);
if (QDF_IS_STATUS_SUCCESS(status))
qdf_mem_free(cur_node);
cur_lst = next_lst;
next_lst = NULL;
cur_node = NULL;
continue;
}
if (!qdf_mem_cmp(cur_node->bssid.bytes,
bss_descr->bssId, QDF_MAC_ADDR_SIZE))
break;
cur_lst = next_lst;
next_lst = NULL;
cur_node = NULL;
}
if (cur_node) {
time_diff = cur_time - cur_node->time_during_rejection;
if (!(time_diff > cur_node->retry_delay ||
bss_descr->rssi_raw >= cur_node->expected_rssi)) {
sme_err("Don't Attempt to connect %pM (time diff %d retry delay %d rssi %d expected rssi %d)",
cur_node->bssid.bytes, time_diff,
cur_node->retry_delay, bss_descr->rssi_raw,
cur_node->expected_rssi);
return true;
}
sme_debug("Remove %pM as time diff %d is greater retry delay %d or RSSI %d is greater than expected %d",
cur_node->bssid.bytes, time_diff,
cur_node->retry_delay,
bss_descr->rssi_raw,
cur_node->expected_rssi);
status = qdf_list_remove_node(list, cur_lst);
if (QDF_IS_STATUS_SUCCESS(status))
qdf_mem_free(cur_node);
}
return false;
}
/**
* csr_filter_ap_due_to_rssi_reject() - filter the AP who has sent
* assoc reject due to RSSI if condition has not improved
* @mac_ctx: mac context
* @scan_list: candidate list for the connection
*
* Return: void
*/
static void csr_filter_ap_due_to_rssi_reject(struct mac_context *mac_ctx,
struct scan_result_list *scan_list)
{
tListElem *cur_entry;
tListElem *next_entry;
struct tag_csrscan_result *scan_res;
bool remove;
if (!scan_list ||
!qdf_list_size(&mac_ctx->roam.rssi_disallow_bssid))
return;
cur_entry = csr_ll_peek_head(&scan_list->List, LL_ACCESS_NOLOCK);
while (cur_entry) {
scan_res = GET_BASE_ADDR(cur_entry, struct tag_csrscan_result,
Link);
next_entry = csr_ll_next(&scan_list->List,
cur_entry, LL_ACCESS_NOLOCK);
qdf_mutex_acquire(&mac_ctx->roam.rssi_disallow_bssid_lock);
remove = csr_remove_ap_due_to_rssi(
&mac_ctx->roam.rssi_disallow_bssid,
&scan_res->Result.BssDescriptor);
qdf_mutex_release(&mac_ctx->roam.rssi_disallow_bssid_lock);
if (remove) {
csr_ll_remove_entry(&scan_list->List,
cur_entry, LL_ACCESS_NOLOCK);
csr_free_scan_result_entry(mac_ctx, scan_res);
}
cur_entry = next_entry;
next_entry = NULL;
}
}
/**
* csr_remove_ap_with_assoc_disallowed() - Remove APs with assoc
* disallowed bit set
* @mac_ctx: mac context
* @scan_list: candidate list for the connection
*
* Return: None
*/
static void csr_remove_ap_with_assoc_disallowed(struct mac_context *mac_ctx,
struct scan_result_list *scan_list)
{
tListElem *cur_entry;
tListElem *next_entry;
struct tag_csrscan_result *scan_res;
if (!scan_list)
return;
cur_entry = csr_ll_peek_head(&scan_list->List, LL_ACCESS_NOLOCK);
while (cur_entry) {
scan_res = GET_BASE_ADDR(cur_entry, struct tag_csrscan_result,
Link);
next_entry = csr_ll_next(&scan_list->List, cur_entry,
LL_ACCESS_NOLOCK);
if (!mac_ctx->ignore_assoc_disallowed &&
scan_res->Result.BssDescriptor.assoc_disallowed) {
csr_ll_remove_entry(&scan_list->List, cur_entry,
LL_ACCESS_NOLOCK);
csr_free_scan_result_entry(mac_ctx, scan_res);
}
cur_entry = next_entry;
next_entry = NULL;
}
}
QDF_STATUS csr_scan_get_result(struct mac_context *mac_ctx,
tCsrScanResultFilter *pFilter,
tScanResultHandle *results)
{
QDF_STATUS status;
struct scan_result_list *ret_list = NULL;
qdf_list_t *list = NULL;
struct scan_filter *filter = NULL;
struct wlan_objmgr_pdev *pdev = NULL;
uint32_t num_bss;
if (results)
*results = CSR_INVALID_SCANRESULT_HANDLE;
pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc,
0, WLAN_LEGACY_MAC_ID);
if (!pdev) {
sme_err("pdev is NULL");
return QDF_STATUS_E_INVAL;
}
if (pFilter) {
filter = qdf_mem_malloc(sizeof(*filter));
if (!filter) {
status = QDF_STATUS_E_NOMEM;
goto error;
}
status = csr_prepare_scan_filter(mac_ctx, pFilter, filter);
if (QDF_IS_STATUS_ERROR(status)) {
sme_err("Prepare filter failed");
goto error;
}
}
list = ucfg_scan_get_result(pdev,
pFilter ? filter : NULL);
if (list)
sme_debug("num_entries %d", qdf_list_size(list));
if (!list || (list && !qdf_list_size(list))) {
sme_debug("get scan result failed");
status = QDF_STATUS_E_NULL_VALUE;
goto error;
}
ret_list = qdf_mem_malloc(sizeof(struct scan_result_list));
if (!ret_list) {
status = QDF_STATUS_E_NOMEM;
goto error;
}
csr_ll_open(&ret_list->List);
ret_list->pCurEntry = NULL;
status = csr_parse_scan_list(mac_ctx,
ret_list, list);
num_bss = csr_ll_count(&ret_list->List);
sme_debug("status: %d No of BSS: %d",
status, num_bss);
if (QDF_IS_STATUS_ERROR(status) || !results)
/* Fail or No one wants the result. */
csr_scan_result_purge(mac_ctx, (tScanResultHandle) ret_list);
else {
if (pFilter) {
csr_filter_ap_due_to_rssi_reject(mac_ctx, ret_list);
csr_remove_ap_with_assoc_disallowed(mac_ctx, ret_list);
}
if (!csr_ll_count(&ret_list->List)) {
/* This mean that there is no match */
csr_ll_close(&ret_list->List);
qdf_mem_free(ret_list);
/*
* Do not trigger scan for ssid if the scan entries
* are removed either due to rssi reject or assoc
* disallowed.
*/
if (num_bss)
status = QDF_STATUS_E_EXISTS;
else
status = QDF_STATUS_E_NULL_VALUE;
} else if (results) {
*results = ret_list;
}
}
error:
if (filter)
qdf_mem_free(filter);
if (list)
ucfg_scan_purge_results(list);
if (pdev)
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
return status;
}
QDF_STATUS csr_scan_get_result_for_bssid(struct mac_context *mac_ctx,
struct qdf_mac_addr *bssid,
tCsrScanResultInfo *res)
{
QDF_STATUS status = QDF_STATUS_E_FAILURE;
tCsrScanResultFilter *scan_filter = NULL;
tScanResultHandle filtered_scan_result = NULL;
tCsrScanResultInfo *scan_result;
if (!mac_ctx) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("mac_ctx is NULL"));
return QDF_STATUS_E_FAILURE;
}
scan_filter = qdf_mem_malloc(sizeof(tCsrScanResultFilter));
if (!scan_filter)
return QDF_STATUS_E_NOMEM;
scan_filter->BSSIDs.bssid = qdf_mem_malloc(sizeof(*bssid));
if (!scan_filter->BSSIDs.bssid) {
status = QDF_STATUS_E_FAILURE;
goto free_filter;
}
scan_filter->BSSIDs.numOfBSSIDs = 1;
qdf_mem_copy(scan_filter->BSSIDs.bssid, bssid, sizeof(*bssid));
status = csr_scan_get_result(mac_ctx, scan_filter,
&filtered_scan_result);
if (!QDF_IS_STATUS_SUCCESS(status)) {
sme_err("Failed to get scan result");
goto free_filter;
}
scan_result = csr_scan_result_get_first(mac_ctx, filtered_scan_result);
if (scan_result) {
res->pvIes = NULL;
res->ssId.length = scan_result->ssId.length;
qdf_mem_copy(&res->ssId.ssId, &scan_result->ssId.ssId,
res->ssId.length);
res->timer = scan_result->timer;
qdf_mem_copy(&res->BssDescriptor, &scan_result->BssDescriptor,
sizeof(tSirBssDescription));
status = QDF_STATUS_SUCCESS;
} else {
status = QDF_STATUS_E_FAILURE;
}
csr_scan_result_purge(mac_ctx, filtered_scan_result);
free_filter:
csr_free_scan_filter(mac_ctx, scan_filter);
if (scan_filter)
qdf_mem_free(scan_filter);
return status;
}
static inline QDF_STATUS
csr_flush_scan_results(struct mac_context *mac_ctx,
struct scan_filter *filter)
{
struct wlan_objmgr_pdev *pdev = NULL;
QDF_STATUS status;
pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc,
0, WLAN_LEGACY_MAC_ID);
if (!pdev) {
sme_err("pdev is NULL");
return QDF_STATUS_E_INVAL;
}
status = ucfg_scan_flush_results(pdev, filter);
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
return status;
}
QDF_STATUS csr_scan_flush_result(struct mac_context *mac_ctx)
{
if (csr_scan_flush_denied(mac_ctx)) {
sme_err("scan flush denied in roam state");
return QDF_STATUS_E_FAILURE;
}
return csr_flush_scan_results(mac_ctx, NULL);
}
static inline void csr_flush_bssid(struct mac_context *mac_ctx,
uint8_t *bssid)
{
struct scan_filter *filter;
filter = qdf_mem_malloc(sizeof(*filter));
if (!filter)
return;
filter->num_of_bssid = 1;
qdf_mem_copy(filter->bssid_list[0].bytes,
bssid, QDF_MAC_ADDR_SIZE);
csr_flush_scan_results(mac_ctx, filter);
sme_debug("Removed BSS entry:%pM", bssid);
if (filter)
qdf_mem_free(filter);
}
void csr_remove_bssid_from_scan_list(struct mac_context *mac_ctx,
tSirMacAddr bssid)
{
csr_flush_bssid(mac_ctx, bssid);
}
void csr_init_occupied_channels_list(struct mac_context *mac_ctx,
uint8_t sessionId)
{
qdf_list_t *list = NULL;
struct wlan_objmgr_pdev *pdev = NULL;
qdf_list_node_t *cur_lst = NULL;
qdf_list_node_t *next_lst = NULL;
struct scan_cache_node *cur_node = NULL;
struct scan_filter *filter;
tpCsrNeighborRoamControlInfo neighbor_roam_info =
&mac_ctx->roam.neighborRoamInfo[sessionId];
tCsrRoamConnectedProfile *profile = NULL;
uint8_t ch;
if (!(mac_ctx && mac_ctx->roam.roamSession &&
CSR_IS_SESSION_VALID(mac_ctx, sessionId))) {
sme_debug("Invalid session");
return;
}
if (neighbor_roam_info->cfgParams.channelInfo.numOfChannels) {
/*
* Ini file contains neighbor scan channel list, hence NO need
* to build occupied channel list"
*/
sme_debug("Ini contains neighbor scan ch list");
return;
}
if (!csr_neighbor_roam_is_new_connected_profile(mac_ctx, sessionId)) {
/*
* Do not flush occupied list since current roam profile matches
* previous
*/
sme_debug("Current roam profile matches prev");
return;
}
profile = &mac_ctx->roam.roamSession[sessionId].connectedProfile;
if (!profile)
return;
filter = qdf_mem_malloc(sizeof(*filter));
if (!filter) {
sme_err("filter is NULL");
return;
}
filter->num_of_auth = 1;
filter->auth_type[0] = csr_covert_auth_type_new(profile->AuthType);
filter->num_of_enc_type = 1;
filter->enc_type[0] = csr_covert_enc_type_new(profile->EncryptionType);
filter->num_of_mc_enc_type = 1;
filter->mc_enc_type[0] =
csr_covert_enc_type_new(profile->mcEncryptionType);
filter->num_of_ssid = 1;
filter->ssid_list[0].length = profile->SSID.length;
qdf_mem_copy(filter->ssid_list[0].ssid, profile->SSID.ssId,
profile->SSID.length);
pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc, 0, WLAN_LEGACY_MAC_ID);
if (!pdev) {
sme_err("pdev is NULL");
qdf_mem_free(filter);
return;
}
/* Empty occupied channels here */
mac_ctx->scan.occupiedChannels[sessionId].numChannels = 0;
mac_ctx->scan.roam_candidate_count[sessionId] = 0;
csr_add_to_occupied_channels(
mac_ctx, profile->operationChannel,
sessionId,
&mac_ctx->scan.occupiedChannels[sessionId],
true);
list = ucfg_scan_get_result(pdev, filter);
if (list)
sme_debug("num_entries %d", qdf_list_size(list));
if (!list || (list && !qdf_list_size(list))) {
sme_err("get scan result failed");
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
qdf_mem_free(filter);
if (list)
ucfg_scan_purge_results(list);
return;
}
qdf_list_peek_front(list, &cur_lst);
while (cur_lst) {
cur_node = qdf_container_of(cur_lst, struct scan_cache_node,
node);
ch = cur_node->entry->channel.chan_idx;
csr_add_to_occupied_channels(
mac_ctx, ch,
sessionId,
&mac_ctx->scan.occupiedChannels[sessionId],
true);
qdf_list_peek_next(list, cur_lst, &next_lst);
cur_lst = next_lst;
next_lst = NULL;
}
qdf_mem_free(filter);
ucfg_scan_purge_results(list);
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
}
/**
* csr_scan_filter_results: filter scan result based
* on valid channel list number.
* @mac_ctx: mac context
*
* Get scan result from scan list and Check Scan result channel number
* with 11d channel list if channel number is found in 11d channel list
* then do not remove scan result entry from scan list
*
* return: QDF Status
*/
QDF_STATUS csr_scan_filter_results(struct mac_context *mac_ctx)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint32_t len = sizeof(mac_ctx->roam.validChannelList);
struct wlan_objmgr_pdev *pdev = NULL;
pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc,
0, WLAN_LEGACY_MAC_ID);
if (!pdev) {
sme_err("pdev is NULL");
return QDF_STATUS_E_INVAL;
}
status = csr_get_cfg_valid_channels(mac_ctx,
mac_ctx->roam.validChannelList,
&len);
/* Get valid channels list from CFG */
if (QDF_IS_STATUS_ERROR(status)) {
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
sme_err("Failed to get Channel list from CFG");
return status;
}
sme_debug("No of valid channel %d", len);
ucfg_scan_filter_valid_channel(pdev,
mac_ctx->roam.validChannelList, len);
wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID);
return QDF_STATUS_SUCCESS;
}