blob: d0f6c497059fe6ecd558d7cb727c3a92b19ca128 [file] [log] [blame]
/** @file mlan_scan.c
*
* @brief Functions implementing wlan scan IOCTL and firmware command APIs
*
* IOCTL handlers as well as command preparation and response routines
* for sending scan commands to the firmware.
*
* Copyright (C) 2008-2009, Marvell International Ltd.
* All Rights Reserved
*/
/******************************************************
Change log:
10/28/2008: initial version
******************************************************/
#include "mlan.h"
#include "mlan_join.h"
#include "mlan_util.h"
#include "mlan_fw.h"
#include "mlan_main.h"
#include "mlan_11n.h"
#include "mlan_11h.h"
/********************************************************
Local Constants
********************************************************/
/** The maximum number of channels the firmware can scan per command */
#define MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN 14
/**
* Number of channels to scan per firmware scan command issuance.
*
* Number restricted to prevent hitting the limit on the amount of scan data
* returned in a single firmware scan command.
*/
#define MRVDRV_CHANNELS_PER_SCAN_CMD 4
/** Memory needed to store a max sized Channel List TLV for a firmware scan */
#define CHAN_TLV_MAX_SIZE (sizeof(MrvlIEtypesHeader_t) \
+ (MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN \
* sizeof(ChanScanParamSet_t)))
/** Memory needed to store supported rate */
#define RATE_TLV_MAX_SIZE (sizeof(MrvlIEtypes_RatesParamSet_t) + HOSTCMD_SUPPORTED_RATES)
/** Memory needed to store a max number/size WildCard SSID TLV for a firmware scan */
#define WILDCARD_SSID_TLV_MAX_SIZE \
(MRVDRV_MAX_SSID_LIST_LENGTH * (sizeof(MrvlIEtypes_WildCardSsIdParamSet_t) + MRVDRV_MAX_SSID_LENGTH))
/** Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max */
#define MAX_SCAN_CFG_ALLOC (sizeof(wlan_scan_cmd_config) \
+ sizeof(MrvlIEtypes_NumProbes_t) \
+ sizeof(MrvlIETypes_HTCap_t) \
+ CHAN_TLV_MAX_SIZE \
+ RATE_TLV_MAX_SIZE \
+ WILDCARD_SSID_TLV_MAX_SIZE)
/** Macro to enable/disable SSID checking before storing a scan table */
#ifdef DISCARD_BAD_SSID
#define CHECK_SSID_IS_VALID(x) wlan_ssid_valid(&bssidEntry.ssid)
#else
#define CHECK_SSID_IS_VALID(x) MTRUE
#endif
/********************************************************
Local Variables
********************************************************/
/**
* Interally used to send a configured scan cmd between driver routines
*/
typedef union
{
/** Scan configuration (variable length) */
wlan_scan_cmd_config config;
/** Max allocated block */
t_u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC];
} wlan_scan_cmd_config_tlv;
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
t_u8 wpa_oui_aes[] = { 0x00, 0x50, 0xf2, 0x04 }; /* AES */
t_u8 rsn_oui_aes[] = { 0x00, 0x0f, 0xac, 0x04 }; /* AES */
/**
* @brief This function will parse a given IE for a given OUI
*
* Parse a given WPA/RSN IE to find if it has a given oui in PTK,
* if no OUI found for PTK it returns 0.
*
* @param pbss_desc A pointer to current BSS descriptor
* @return 0 on failure to find OUI, 1 on success.
*/
static t_u8
search_oui_in_ie(IEBody * ie_body, t_u8 * oui)
{
t_u8 count;
count = ie_body->PtkCnt[0];
/* There could be multiple OUIs for PTK hence 1) Take the length. 2) Check
all the OUIs for AES. 3) If one of them is AES then pass success. */
while (count) {
if (!memcmp(ie_body->PtkBody, oui, sizeof(ie_body->PtkBody))) {
return MLAN_OUI_PRESENT;
}
--count;
if (count) {
ie_body = (IEBody *) ((t_u8 *) ie_body + sizeof(ie_body->PtkBody));
}
}
PRINTM(MINFO, "PTK not AES\n");
return MLAN_OUI_NOT_PRESENT;
}
/**
* @brief This function will pass the correct ie and oui to search_oui_in_ie
*
* Check the pbss_desc for appropriate IE and then check if RSN IE has AES
* OUI in it. If RSN IE does not have AES in PTK then return 0;
*
* @param pbss_desc A pointer to current BSS descriptor
* @return 0 on failure to find AES OUI, 1 on success.
*/
static t_u8
is_rsn_aes_present(BSSDescriptor_t * pbss_desc)
{
t_u8 *oui = MNULL;
IEBody *ie_body = MNULL;
t_u8 ret = MLAN_OUI_NOT_PRESENT;
if (((pbss_desc->prsn_ie) && ((*(pbss_desc->prsn_ie)).
ieee_hdr.element_id == RSN_IE))) {
ie_body = (IEBody *) (((t_u8 *) pbss_desc->prsn_ie->data) +
RSN_GTK_OUI_OFFSET);
oui = rsn_oui_aes;
if ((ret = search_oui_in_ie(ie_body, oui)))
return ret;
}
return ret;
}
/**
* @brief This function will pass the correct ie and oui to search_oui_in_ie
*
* Check the pbss_desc for appropriate IE and then check if WPA IE has AES
* OUI in it. If WPA IE does not have AES in PTK then return 0;
*
* @param pbss_desc A pointer to current BSS descriptor
* @return 0 on failure to find AES OUI, 1 on success.
*/
static t_u8
is_wpa_aes_present(BSSDescriptor_t * pbss_desc)
{
t_u8 *oui = MNULL;
IEBody *ie_body = MNULL;
t_u8 ret = MLAN_OUI_NOT_PRESENT;
if (((pbss_desc->pwpa_ie) && ((*(pbss_desc->pwpa_ie)).
vend_hdr.element_id == WPA_IE))) {
ie_body = (IEBody *) pbss_desc->pwpa_ie->data;
oui = wpa_oui_aes;
if ((ret = search_oui_in_ie(ie_body, oui)))
return ret;
}
return ret;
}
/**
* @brief Check if a scanned network compatible with the driver settings
*
* WEP WPA WPA2 ad-hoc encrypt Network
* enabled enabled enabled AES mode Privacy WPA WPA2 Compatible
* 0 0 0 0 NONE 0 0 0 yes No security
* 0 1 0 0 x 1x 1 x yes WPA
* 0 0 1 0 x 1x x 1 yes WPA2
* 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES
*
* 1 0 0 0 NONE 1 0 0 yes Static WEP
* 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP
*
*
* @param pmpriv A pointer to mlan_private
* @param index Index in scan table to check against current driver settings
* @param mode Network mode: Infrastructure or IBSS
*
* @return Index in ScanTable, or negative value if error
*/
static t_s32
wlan_is_network_compatible(IN mlan_private * pmpriv,
IN t_u32 index, IN t_u32 mode)
{
mlan_adapter *pmadapter = pmpriv->adapter;
BSSDescriptor_t *pbss_desc;
ENTER();
pbss_desc = &pmadapter->pscan_table[index];
/* Don't check for compatibility if roaming */
if ((pmpriv->media_connected == MTRUE)
&& (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA)
&& (pbss_desc->bss_mode == MLAN_BSS_MODE_INFRA)) {
LEAVE();
return index;
}
if ((pbss_desc->bss_mode == mode) &&
(pmpriv->sec_info.ewpa_enabled == MTRUE)) {
if (((pbss_desc->pwpa_ie) &&
((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE)) ||
((pbss_desc->prsn_ie) &&
((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE))) {
if (((pmpriv->adapter->config_bands & BAND_GN ||
pmpriv->adapter->config_bands & BAND_AN) &&
pbss_desc->pht_cap)
&& !is_wpa_aes_present(pbss_desc)
&& !is_rsn_aes_present(pbss_desc)) {
PRINTM(MINFO, "AES not supported by AP when 11n enabled\n");
LEAVE();
return -1;
}
LEAVE();
return index;
} else {
PRINTM(MINFO, "ewpa_enabled: Ignore none WPA/WPA2 AP\n");
LEAVE();
return -1;
}
}
if (pmpriv->wps.session_enable == MTRUE) {
PRINTM(MINFO, "Return success directly in WPS period\n");
LEAVE();
return index;
}
if (pmpriv->sec_info.wapi_enabled &&
(pbss_desc->pwapi_ie &&
((*(pbss_desc->pwapi_ie)).ieee_hdr.element_id == WAPI_IE))) {
PRINTM(MINFO, "Return success for WAPI AP\n");
LEAVE();
return index;
}
if (pbss_desc->bss_mode == mode) {
if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled
&& !pmpriv->sec_info.wpa_enabled
&& !pmpriv->sec_info.wpa2_enabled
&& ((!pbss_desc->pwpa_ie) ||
((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != WPA_IE))
&& ((!pbss_desc->prsn_ie) ||
((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != RSN_IE))
&& !pmpriv->adhoc_aes_enabled &&
pmpriv->sec_info.encryption_mode == MLAN_ENCRYPTION_MODE_NONE &&
!pbss_desc->privacy) {
/* No security */
LEAVE();
return index;
} else if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled
&& !pmpriv->sec_info.wpa_enabled
&& !pmpriv->sec_info.wpa2_enabled
&& !pmpriv->adhoc_aes_enabled && pbss_desc->privacy) {
/* Static WEP enabled */
LEAVE();
return index;
} else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled
&& pmpriv->sec_info.wpa_enabled
&& !pmpriv->sec_info.wpa2_enabled
&& ((pbss_desc->pwpa_ie) &&
((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE))
&& !pmpriv->adhoc_aes_enabled
/*
* Privacy bit may NOT be set in some APs like LinkSys WRT54G
* && pbss_desc->privacy
*/
) {
/* WPA enabled */
PRINTM(MINFO,
"wlan_is_network_compatible() WPA: index=%d wpa_ie=%#x "
"wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x "
"privacy=%#x\n", index,
(pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)).vend_hdr.
element_id : 0,
(pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)).ieee_hdr.
element_id : 0,
(pmpriv->sec_info.wep_status ==
Wlan802_11WEPEnabled) ? "e" : "d",
(pmpriv->sec_info.wpa_enabled) ? "e" : "d",
(pmpriv->sec_info.wpa2_enabled) ? "e" : "d",
pmpriv->sec_info.encryption_mode, pbss_desc->privacy);
if (((pmpriv->adapter->config_bands & BAND_GN ||
pmpriv->adapter->config_bands & BAND_AN) &&
pbss_desc->pht_cap)
&& !is_wpa_aes_present(pbss_desc)) {
PRINTM(MINFO, "AES not supported by AP when 11n enabled\n");
LEAVE();
return -1;
}
LEAVE();
return index;
} else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled
&& !pmpriv->sec_info.wpa_enabled
&& pmpriv->sec_info.wpa2_enabled
&& ((pbss_desc->prsn_ie) &&
((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE))
&& !pmpriv->adhoc_aes_enabled
/*
* Privacy bit may NOT be set in some APs like LinkSys WRT54G
* && pbss_desc->privacy
*/
) {
/* WPA2 enabled */
PRINTM(MINFO,
"wlan_is_network_compatible() WPA2: index=%d wpa_ie=%#x "
"wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x "
"privacy=%#x\n", index,
(pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)).vend_hdr.
element_id : 0,
(pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)).ieee_hdr.
element_id : 0,
(pmpriv->sec_info.wep_status ==
Wlan802_11WEPEnabled) ? "e" : "d",
(pmpriv->sec_info.wpa_enabled) ? "e" : "d",
(pmpriv->sec_info.wpa2_enabled) ? "e" : "d",
pmpriv->sec_info.encryption_mode, pbss_desc->privacy);
if (((pmpriv->adapter->config_bands & BAND_GN ||
pmpriv->adapter->config_bands & BAND_AN) &&
pbss_desc->pht_cap)
&& !is_rsn_aes_present(pbss_desc)) {
PRINTM(MINFO, "AES not supported by AP when 11n enabled\n");
LEAVE();
return -1;
}
LEAVE();
return index;
} else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled
&& !pmpriv->sec_info.wpa_enabled
&& !pmpriv->sec_info.wpa2_enabled
&& ((!pbss_desc->pwpa_ie) ||
((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != WPA_IE))
&& ((!pbss_desc->prsn_ie) ||
((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != RSN_IE))
&& pmpriv->adhoc_aes_enabled &&
pmpriv->sec_info.encryption_mode == MLAN_ENCRYPTION_MODE_NONE
&& pbss_desc->privacy) {
/* Ad-hoc AES enabled */
LEAVE();
return index;
} else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled
&& !pmpriv->sec_info.wpa_enabled
&& !pmpriv->sec_info.wpa2_enabled
&& ((!pbss_desc->pwpa_ie) ||
((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != WPA_IE))
&& ((!pbss_desc->prsn_ie) ||
((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != RSN_IE))
&& !pmpriv->adhoc_aes_enabled &&
pmpriv->sec_info.encryption_mode != MLAN_ENCRYPTION_MODE_NONE
&& pbss_desc->privacy) {
/* Dynamic WEP enabled */
PRINTM(MINFO, "wlan_is_network_compatible() dynamic WEP: index=%d "
"wpa_ie=%#x wpa2_ie=%#x EncMode=%#x privacy=%#x\n",
index,
(pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)).vend_hdr.
element_id : 0,
(pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)).ieee_hdr.
element_id : 0, pmpriv->sec_info.encryption_mode,
pbss_desc->privacy);
LEAVE();
return index;
}
/* Security doesn't match */
PRINTM(MINFO,
"wlan_is_network_compatible() FAILED: index=%d wpa_ie=%#x "
"wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n",
index,
(pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)).vend_hdr.
element_id : 0,
(pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)).ieee_hdr.
element_id : 0,
(pmpriv->sec_info.wep_status ==
Wlan802_11WEPEnabled) ? "e" : "d",
(pmpriv->sec_info.wpa_enabled) ? "e" : "d",
(pmpriv->sec_info.wpa2_enabled) ? "e" : "d",
pmpriv->sec_info.encryption_mode, pbss_desc->privacy);
LEAVE();
return -1;
}
/* Mode doesn't match */
LEAVE();
return -1;
}
/**
* @brief This function finds the best SSID in the Scan List
*
* Search the scan table for the best SSID that also matches the current
* adapter network preference (infrastructure or adhoc)
*
* @param pmpriv A pointer to mlan_private structure
* @return index in BSSID list
*/
static t_s32
wlan_find_best_network_in_list(IN mlan_private * pmpriv)
{
mlan_adapter *pmadapter = pmpriv->adapter;
t_u32 mode = pmpriv->bss_mode;
t_s32 best_net = -1;
t_s32 best_rssi = 0;
t_u32 i;
ENTER();
PRINTM(MINFO, "Num of BSSIDs = %d\n", pmadapter->num_in_scan_table);
for (i = 0; i < pmadapter->num_in_scan_table; i++) {
switch (mode) {
case MLAN_BSS_MODE_INFRA:
case MLAN_BSS_MODE_IBSS:
if (wlan_is_network_compatible(pmpriv, i, mode) >= 0) {
if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > best_rssi) {
best_rssi = SCAN_RSSI(pmadapter->pscan_table[i].rssi);
best_net = i;
}
}
break;
case MLAN_BSS_MODE_AUTO:
default:
if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > best_rssi) {
best_rssi = SCAN_RSSI(pmadapter->pscan_table[i].rssi);
best_net = i;
}
break;
}
}
LEAVE();
return best_net;
}
/**
* @brief Create a channel list for the driver to scan based on region info
*
* Use the driver region/band information to construct a comprehensive list
* of channels to scan. This routine is used for any scan that is not
* provided a specific channel list to scan.
*
* @param pmpriv A pointer to mlan_private structure
* @param puser_scan_in MNULL or pointer to scan configuration parameters
* @param pscan_chan_list Output parameter: Resulting channel list to scan
* @param filtered_scan Flag indicating whether or not a BSSID or SSID filter
* is being sent in the command to firmware. Used to
* increase the number of channels sent in a scan
* command and to disable the firmware channel scan
* filter.
*
* @return n/a
*/
static t_void
wlan_scan_create_channel_list(IN mlan_private * pmpriv,
IN const wlan_user_scan_cfg * puser_scan_in,
OUT ChanScanParamSet_t * pscan_chan_list,
IN t_u8 filtered_scan)
{
mlan_adapter *pmadapter = pmpriv->adapter;
region_chan_t *pscan_region;
chan_freq_power_t *cfp;
t_u32 region_idx;
t_u32 chan_idx;
t_u32 next_chan;
t_u8 scan_type;
ENTER();
chan_idx = 0;
/* Set the default scan type to the user specified type, will later be
changed to passive on a per channel basis if restricted by regulatory
requirements (11d or 11h) */
scan_type = pmadapter->scan_type;
for (region_idx = 0;
region_idx < NELEMENTS(pmadapter->region_channel); region_idx++) {
if (wlan_11d_get_state(pmpriv) == ENABLE_11D &&
pmpriv->media_connected != MTRUE) {
/* Scan all the supported chan for the first scan */
if (!pmadapter->universal_channel[region_idx].valid)
continue;
pscan_region = &pmadapter->universal_channel[region_idx];
} else {
if (!pmadapter->region_channel[region_idx].valid)
continue;
pscan_region = &pmadapter->region_channel[region_idx];
}
for (next_chan = 0;
next_chan < pscan_region->num_cfp; next_chan++, chan_idx++) {
cfp = pscan_region->pcfp + next_chan;
if (wlan_11d_get_state(pmpriv) == ENABLE_11D)
scan_type =
wlan_11d_get_scan_type(pmadapter, (t_u8) cfp->channel,
&pmadapter->parsed_region_chan);
switch (pscan_region->band) {
case BAND_A:
pscan_chan_list[chan_idx].radio_type =
HostCmd_SCAN_RADIO_TYPE_A;
if (wlan_11h_radar_detect_required(pmpriv, cfp->channel)) {
scan_type = HostCmd_SCAN_TYPE_PASSIVE;
}
break;
case BAND_B:
case BAND_G:
default:
pscan_chan_list[chan_idx].radio_type =
HostCmd_SCAN_RADIO_TYPE_BG;
break;
}
if (puser_scan_in && puser_scan_in->chan_list[0].scan_time) {
pscan_chan_list[chan_idx].max_scan_time =
wlan_cpu_to_le16((t_u16) puser_scan_in->chan_list[0].
scan_time);
} else if (scan_type == HostCmd_SCAN_TYPE_PASSIVE) {
pscan_chan_list[chan_idx].max_scan_time =
wlan_cpu_to_le16(pmadapter->passive_scan_time);
} else {
pscan_chan_list[chan_idx].max_scan_time =
wlan_cpu_to_le16(pmadapter->active_scan_time);
}
if (scan_type == HostCmd_SCAN_TYPE_PASSIVE) {
pscan_chan_list[chan_idx].chan_scan_mode.passive_scan = MTRUE;
} else {
pscan_chan_list[chan_idx].chan_scan_mode.passive_scan = MFALSE;
}
pscan_chan_list[chan_idx].chan_number = (t_u8) cfp->channel;
if (filtered_scan) {
pscan_chan_list[chan_idx].max_scan_time =
wlan_cpu_to_le16(pmadapter->specific_scan_time);
pscan_chan_list[chan_idx].chan_scan_mode.disable_chan_filt =
MTRUE;
}
}
}
LEAVE();
}
/**
* @brief Construct and send multiple scan config commands to the firmware
*
* Previous routines have created a wlan_scan_cmd_config with any requested
* TLVs. This function splits the channel TLV into max_chan_per_scan lists
* and sends the portion of the channel TLV along with the other TLVs
* to the wlan_cmd routines for execution in the firmware.
*
* @param pmpriv A pointer to mlan_private structure
* @param pioctl_buf A pointer to MLAN IOCTl Request buffer
* @param max_chan_per_scan Maximum number channels to be included in each
* scan command sent to firmware
* @param filtered_scan Flag indicating whether or not a BSSID or SSID
* filter is being used for the firmware command
* scan command sent to firmware
* @param pscan_cfg_out Scan configuration used for this scan.
* @param pchan_tlv_out Pointer in the pscan_cfg_out where the channel TLV
* should start. This is past any other TLVs that
* must be sent down in each firmware command.
* @param pscan_chan_list List of channels to scan in max_chan_per_scan segments
*
* @return MLAN_STATUS_SUCCESS or error return otherwise
*/
static mlan_status
wlan_scan_channel_list(IN mlan_private * pmpriv,
IN t_void * pioctl_buf,
IN t_u32 max_chan_per_scan,
IN t_u8 filtered_scan,
OUT wlan_scan_cmd_config * pscan_cfg_out,
OUT MrvlIEtypes_ChanListParamSet_t * pchan_tlv_out,
IN ChanScanParamSet_t * pscan_chan_list)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
ChanScanParamSet_t *ptmp_chan_list;
ChanScanParamSet_t *pstart_chan;
t_u32 tlv_idx;
t_u32 total_scan_time;
t_u32 done_early;
ENTER();
if (!pscan_cfg_out || !pchan_tlv_out || !pscan_chan_list) {
PRINTM(MINFO, "Scan: Null detect: %p, %p, %p\n",
pscan_cfg_out, pchan_tlv_out, pscan_chan_list);
LEAVE();
return MLAN_STATUS_FAILURE;
}
pchan_tlv_out->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST);
/* Set the temp channel struct pointer to the start of the desired list */
ptmp_chan_list = pscan_chan_list;
/* Loop through the desired channel list, sending a new firmware scan
commands for each max_chan_per_scan channels (or for 1,6,11 individually
if configured accordingly) */
while (ptmp_chan_list->chan_number) {
tlv_idx = 0;
total_scan_time = 0;
pchan_tlv_out->header.len = 0;
pstart_chan = ptmp_chan_list;
done_early = MFALSE;
/*
* Construct the Channel TLV for the scan command. Continue to
* insert channel TLVs until:
* - the tlv_idx hits the maximum configured per scan command
* - the next channel to insert is 0 (end of desired channel list)
* - done_early is set (controlling individual scanning of 1,6,11)
*/
while (tlv_idx < max_chan_per_scan && ptmp_chan_list->chan_number &&
!done_early) {
PRINTM(MINFO, "Scan: Chan(%3d), Radio(%d), Mode(%d,%d), Dur(%d)\n",
ptmp_chan_list->chan_number,
ptmp_chan_list->radio_type,
ptmp_chan_list->chan_scan_mode.passive_scan,
ptmp_chan_list->chan_scan_mode.disable_chan_filt,
wlan_le16_to_cpu(ptmp_chan_list->max_scan_time));
/* Copy the current channel TLV to the command being prepared */
memcpy(pchan_tlv_out->chan_scan_param + tlv_idx,
ptmp_chan_list, sizeof(pchan_tlv_out->chan_scan_param));
/* Increment the TLV header length by the size appended */
pchan_tlv_out->header.len += sizeof(pchan_tlv_out->chan_scan_param);
/*
* The tlv buffer length is set to the number of bytes of the
* between the channel tlv pointer and the start of the
* tlv buffer. This compensates for any TLVs that were appended
* before the channel list.
*/
pscan_cfg_out->tlv_buf_len = (t_u32) ((t_u8 *) pchan_tlv_out
- pscan_cfg_out->tlv_buf);
/* Add the size of the channel tlv header and the data length */
pscan_cfg_out->tlv_buf_len += (sizeof(pchan_tlv_out->header)
+ pchan_tlv_out->header.len);
/* Increment the index to the channel tlv we are constructing */
tlv_idx++;
/* Count the total scan time per command */
total_scan_time += wlan_le16_to_cpu(ptmp_chan_list->max_scan_time);
done_early = MFALSE;
/* Stop the loop if the *current* channel is in the 1,6,11 set and
we are not filtering on a BSSID or SSID. */
if (!filtered_scan && (ptmp_chan_list->chan_number == 1 ||
ptmp_chan_list->chan_number == 6 ||
ptmp_chan_list->chan_number == 11)) {
done_early = MTRUE;
}
/* Increment the tmp pointer to the next channel to be scanned */
ptmp_chan_list++;
/* Stop the loop if the *next* channel is in the 1,6,11 set. This
will cause it to be the only channel scanned on the next
interation */
if (!filtered_scan && (ptmp_chan_list->chan_number == 1 ||
ptmp_chan_list->chan_number == 6 ||
ptmp_chan_list->chan_number == 11)) {
done_early = MTRUE;
}
}
/* The total scan time should be less than scan command timeout value */
if (total_scan_time > MRVDRV_MAX_TOTAL_SCAN_TIME) {
PRINTM(MMSG,
"Total scan time %d ms is over limit (%d ms), scan skipped\n",
total_scan_time, MRVDRV_MAX_TOTAL_SCAN_TIME);
ret = MLAN_STATUS_FAILURE;
break;
}
pchan_tlv_out->header.len = wlan_cpu_to_le16(pchan_tlv_out->header.len);
pmpriv->adapter->pscan_channels = pstart_chan;
/* Send the scan command to the firmware with the specified cfg */
ret = wlan_prepare_cmd(pmpriv,
HostCmd_CMD_802_11_SCAN,
HostCmd_ACT_GEN_SET,
0, pioctl_buf, pscan_cfg_out);
if (ret)
break;
}
LEAVE();
if (ret) {
return MLAN_STATUS_FAILURE;
}
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Construct a wlan_scan_cmd_config structure to use in issue scan commands
*
* Application layer or other functions can invoke wlan_scan_networks
* with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct.
* This structure is used as the basis of one or many wlan_scan_cmd_config
* commands that are sent to the command processing module and sent to
* firmware.
*
* Create a wlan_scan_cmd_config based on the following user supplied
* parameters (if present):
* - SSID filter
* - BSSID filter
* - Number of Probes to be sent
* - Channel list
*
* If the SSID or BSSID filter is not present, disable/clear the filter.
* If the number of probes is not set, use the adapter default setting
* Qualify the channel
*
* @param pmpriv A pointer to mlan_private structure
* @param puser_scan_in MNULL or pointer to scan configuration parameters
* @param pscan_cfg_out Output parameter: Resulting scan configuration
* @param ppchan_list_out Output parameter: Pointer to the start of the
* channel TLV portion of the output scan config
* @param pscan_chan_list Output parameter: Pointer to the resulting
* channel list to scan
* @param pmax_chan_per_scan Output parameter: Number of channels to scan for
* each issuance of the firmware scan command
* @param pfiltered_scan Output parameter: Flag indicating whether or not
* a BSSID or SSID filter is being sent in the
* command to firmware. Used to increase the number
* of channels sent in a scan command and to
* disable the firmware channel scan filter.
* @param pscan_current_only Output parameter: Flag indicating whether or not
* we are only scanning our current active channel
*
* @return n/a
*/
static t_void
wlan_scan_setup_scan_config(IN mlan_private * pmpriv,
IN const wlan_user_scan_cfg * puser_scan_in,
OUT wlan_scan_cmd_config * pscan_cfg_out,
OUT MrvlIEtypes_ChanListParamSet_t **
ppchan_list_out,
OUT ChanScanParamSet_t * pscan_chan_list,
OUT t_u8 * pmax_chan_per_scan,
OUT t_u8 * pfiltered_scan,
OUT t_u8 * pscan_current_only)
{
mlan_adapter *pmadapter = pmpriv->adapter;
MrvlIEtypes_NumProbes_t *pnum_probes_tlv;
MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv;
MrvlIEtypes_RatesParamSet_t *prates_tlv;
const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 };
t_u8 *ptlv_pos;
t_u32 num_probes;
t_u32 ssid_len;
t_u32 chan_idx;
t_u32 scan_type;
t_u16 scan_dur;
t_u8 channel;
t_u8 radio_type;
t_u32 ssid_idx;
t_u8 ssid_filter;
WLAN_802_11_RATES rates;
t_u32 rates_size;
MrvlIETypes_HTCap_t *pht_cap;
ENTER();
/* The tlv_buf_len is calculated for each scan command. The TLVs added in
this routine will be preserved since the routine that sends the command
will append channelTLVs at *ppchan_list_out. The difference between the
*ppchan_list_out and the tlv_buf start will be used to calculate the
size of anything we add in this routine. */
pscan_cfg_out->tlv_buf_len = 0;
/* Running tlv pointer. Assigned to ppchan_list_out at end of function so
later routines know where channels can be added to the command buf */
ptlv_pos = pscan_cfg_out->tlv_buf;
/* Initialize the scan as un-filtered; the flag is later set to TRUE below
if a SSID or BSSID filter is sent in the command */
*pfiltered_scan = MFALSE;
/* Initialize the scan as not being only on the current channel. If the
channel list is customized, only contains one channel, and is the active
channel, this is set true and data flow is not halted. */
*pscan_current_only = MFALSE;
if (puser_scan_in) {
/* Default the ssid_filter flag to TRUE, set false under certain
wildcard conditions and qualified by the existence of an SSID list
before marking the scan as filtered */
ssid_filter = MTRUE;
/* Set the bss type scan filter, use Adapter setting if unset */
pscan_cfg_out->bss_mode =
(puser_scan_in->bss_mode ? (t_u8) puser_scan_in->
bss_mode : (t_u8) pmadapter->scan_mode);
/* Set the number of probes to send, use Adapter setting if unset */
num_probes = (puser_scan_in->num_probes ? puser_scan_in->num_probes :
pmadapter->scan_probes);
/*
* Set the BSSID filter to the incoming configuration,
* if non-zero. If not set, it will remain disabled (all zeros).
*/
memcpy(pscan_cfg_out->specific_bssid,
puser_scan_in->specific_bssid,
sizeof(pscan_cfg_out->specific_bssid));
for (ssid_idx = 0; ((ssid_idx < NELEMENTS(puser_scan_in->ssid_list))
&& (*puser_scan_in->ssid_list[ssid_idx].ssid
|| puser_scan_in->ssid_list[ssid_idx].max_len));
ssid_idx++) {
ssid_len =
wlan_strlen((t_s8 *) puser_scan_in->ssid_list[ssid_idx].ssid) +
1;
pwildcard_ssid_tlv
= (MrvlIEtypes_WildCardSsIdParamSet_t *) ptlv_pos;
pwildcard_ssid_tlv->header.type
= wlan_cpu_to_le16(TLV_TYPE_WILDCARDSSID);
pwildcard_ssid_tlv->header.len
=
(t_u16) (ssid_len +
sizeof(pwildcard_ssid_tlv->max_ssid_length));
pwildcard_ssid_tlv->max_ssid_length =
puser_scan_in->ssid_list[ssid_idx].max_len;
memcpy(pwildcard_ssid_tlv->ssid,
puser_scan_in->ssid_list[ssid_idx].ssid, ssid_len);
ptlv_pos += (sizeof(pwildcard_ssid_tlv->header)
+ pwildcard_ssid_tlv->header.len);
pwildcard_ssid_tlv->header.len
= wlan_cpu_to_le16(pwildcard_ssid_tlv->header.len);
PRINTM(MINFO, "Scan: ssid_list[%d]: %s, %d\n",
ssid_idx,
pwildcard_ssid_tlv->ssid,
pwildcard_ssid_tlv->max_ssid_length);
/* Empty wildcard ssid with a maxlen will match many or potentially
all SSIDs (maxlen == 32), therefore do not treat the scan as
filtered. */
if (!ssid_len && pwildcard_ssid_tlv->max_ssid_length) {
ssid_filter = MFALSE;
}
}
/*
* The default number of channels sent in the command is low to
* ensure the response buffer from the firmware does not truncate
* scan results. That is not an issue with an SSID or BSSID
* filter applied to the scan results in the firmware.
*/
if ((ssid_idx && ssid_filter)
|| memcmp(pscan_cfg_out->specific_bssid, &zero_mac,
sizeof(zero_mac))) {
*pfiltered_scan = MTRUE;
}
} else {
pscan_cfg_out->bss_mode = (t_u8) pmadapter->scan_mode;
num_probes = pmadapter->scan_probes;
}
/*
* If a specific BSSID or SSID is used, the number of channels in the
* scan command will be increased to the absolute maximum.
*/
if (*pfiltered_scan)
*pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN;
else
*pmax_chan_per_scan = MRVDRV_CHANNELS_PER_SCAN_CMD;
/* If the input config or adapter has the number of Probes set, add tlv */
if (num_probes) {
PRINTM(MINFO, "Scan: num_probes = %d\n", num_probes);
pnum_probes_tlv = (MrvlIEtypes_NumProbes_t *) ptlv_pos;
pnum_probes_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_NUMPROBES);
pnum_probes_tlv->header.len = sizeof(pnum_probes_tlv->num_probes);
pnum_probes_tlv->num_probes = wlan_cpu_to_le16((t_u16) num_probes);
ptlv_pos +=
sizeof(pnum_probes_tlv->header) + pnum_probes_tlv->header.len;
pnum_probes_tlv->header.len =
wlan_cpu_to_le16(pnum_probes_tlv->header.len);
}
/* Append rates tlv */
memset(rates, 0, sizeof(rates));
rates_size = wlan_get_supported_rates(pmpriv, rates);
prates_tlv = (MrvlIEtypes_RatesParamSet_t *) ptlv_pos;
prates_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_RATES);
prates_tlv->header.len = wlan_cpu_to_le16((t_u16) rates_size);
memcpy(prates_tlv->rates, rates, rates_size);
ptlv_pos += sizeof(prates_tlv->header) + rates_size;
PRINTM(MINFO, "SCAN_CMD: Rates size = %d\n", rates_size);
if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info)
&& (pmpriv->adapter->config_bands & BAND_GN
|| pmpriv->adapter->config_bands & BAND_AN)) {
pht_cap = (MrvlIETypes_HTCap_t *) ptlv_pos;
memset(pht_cap, 0, sizeof(MrvlIETypes_HTCap_t));
pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY);
pht_cap->header.len = sizeof(HTCap_t);
wlan_fill_cap_info(pmadapter, pht_cap);
pht_cap->ht_cap.ht_cap_info =
wlan_cpu_to_le16(pht_cap->ht_cap.ht_cap_info);
HEXDUMP("SCAN: HT_CAPABILITIES IE", (t_u8 *) pht_cap,
sizeof(MrvlIETypes_HTCap_t));
ptlv_pos += sizeof(MrvlIETypes_HTCap_t);
pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len);
}
/** Append vendor specific IE TLV */
wlan_cmd_append_vsie_tlv(pmpriv, MLAN_VSIE_MASK_SCAN, &ptlv_pos);
/*
* Set the output for the channel TLV to the address in the tlv buffer
* past any TLVs that were added in this function (SSID, num_probes).
* Channel TLVs will be added past this for each scan command, preserving
* the TLVs that were previously added.
*/
*ppchan_list_out = (MrvlIEtypes_ChanListParamSet_t *) ptlv_pos;
if (puser_scan_in && puser_scan_in->chan_list[0].chan_number) {
PRINTM(MINFO, "Scan: Using supplied channel list\n");
for (chan_idx = 0;
chan_idx < WLAN_USER_SCAN_CHAN_MAX
&& puser_scan_in->chan_list[chan_idx].chan_number; chan_idx++) {
channel = puser_scan_in->chan_list[chan_idx].chan_number;
(pscan_chan_list + chan_idx)->chan_number = channel;
radio_type = puser_scan_in->chan_list[chan_idx].radio_type;
(pscan_chan_list + chan_idx)->radio_type = radio_type;
scan_type = puser_scan_in->chan_list[chan_idx].scan_type;
/* Prevent active scanning on a radar controlled channel */
if (radio_type == HostCmd_SCAN_RADIO_TYPE_A) {
if (wlan_11h_radar_detect_required(pmpriv, channel)) {
scan_type = HostCmd_SCAN_TYPE_PASSIVE;
}
}
if (scan_type == HostCmd_SCAN_TYPE_PASSIVE) {
(pscan_chan_list + chan_idx)->chan_scan_mode.passive_scan =
MTRUE;
} else {
(pscan_chan_list + chan_idx)->chan_scan_mode.passive_scan =
MFALSE;
}
if (puser_scan_in->chan_list[chan_idx].scan_time) {
scan_dur = (t_u16) puser_scan_in->chan_list[chan_idx].scan_time;
} else {
if (scan_type == HostCmd_SCAN_TYPE_PASSIVE) {
scan_dur = pmadapter->passive_scan_time;
} else if (*pfiltered_scan) {
scan_dur = pmadapter->specific_scan_time;
} else {
scan_dur = pmadapter->active_scan_time;
}
}
(pscan_chan_list + chan_idx)->min_scan_time =
wlan_cpu_to_le16(scan_dur);
(pscan_chan_list + chan_idx)->max_scan_time =
wlan_cpu_to_le16(scan_dur);
}
/* Check if we are only scanning the current channel */
if ((chan_idx == 1)
&& (puser_scan_in->chan_list[0].chan_number
== pmpriv->curr_bss_params.bss_descriptor.channel)) {
*pscan_current_only = MTRUE;
PRINTM(MINFO, "Scan: Scanning current channel only");
}
} else {
PRINTM(MINFO, "Scan: Creating full region channel list\n");
wlan_scan_create_channel_list(pmpriv, puser_scan_in, pscan_chan_list,
*pfiltered_scan);
}
LEAVE();
}
/**
* @brief Inspect the scan response buffer for pointers to expected TLVs
*
* TLVs can be included at the end of the scan response BSS information.
* Parse the data in the buffer for pointers to TLVs that can potentially
* be passed back in the response
*
* @param pmadapter Pointer to the mlan_adapter structure
* @param ptlv Pointer to the start of the TLV buffer to parse
* @param tlv_buf_size Size of the TLV buffer
* @param req_tlv_type Request TLV's type
* @param pptlv Output parameter: Pointer to the request TLV if found
*
* @return n/a
*/
static t_void
wlan_ret_802_11_scan_get_tlv_ptrs(IN pmlan_adapter pmadapter,
IN MrvlIEtypes_Data_t * ptlv,
IN t_u32 tlv_buf_size,
IN t_u32 req_tlv_type,
OUT MrvlIEtypes_Data_t ** pptlv)
{
MrvlIEtypes_Data_t *pcurrent_tlv;
t_u32 tlv_buf_left;
t_u32 tlv_type;
t_u32 tlv_len;
ENTER();
pcurrent_tlv = ptlv;
tlv_buf_left = tlv_buf_size;
*pptlv = MNULL;
PRINTM(MINFO, "SCAN_RESP: tlv_buf_size = %d\n", tlv_buf_size);
while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) {
tlv_type = wlan_le16_to_cpu(pcurrent_tlv->header.type);
tlv_len = wlan_le16_to_cpu(pcurrent_tlv->header.len);
if (sizeof(ptlv->header) + tlv_len > tlv_buf_left) {
PRINTM(MERROR, "SCAN_RESP: TLV buffer corrupt\n");
break;
}
if (req_tlv_type == tlv_type) {
switch (tlv_type) {
case TLV_TYPE_TSFTIMESTAMP:
PRINTM(MINFO, "SCAN_RESP: TSF Timestamp TLV, len = %d\n",
tlv_len);
*pptlv = (MrvlIEtypes_Data_t *) pcurrent_tlv;
break;
case TLV_TYPE_CHANNELBANDLIST:
PRINTM(MINFO, "SCAN_RESP: CHANNEL BAND LIST TLV, len = %d\n",
tlv_len);
*pptlv = (MrvlIEtypes_Data_t *) pcurrent_tlv;
break;
default:
PRINTM(MERROR, "SCAN_RESP: Unhandled TLV = %d\n", tlv_type);
/* Give up, this seems corrupted */
LEAVE();
return;
}
}
if (*pptlv) {
// HEXDUMP("SCAN_RESP: TLV Buf", (t_u8 *)*pptlv+4, tlv_len);
break;
}
tlv_buf_left -= (sizeof(ptlv->header) + tlv_len);
pcurrent_tlv = (MrvlIEtypes_Data_t *) (pcurrent_tlv->data + tlv_len);
} /* while */
LEAVE();
}
/**
* @brief Interpret a BSS scan response returned from the firmware
*
* Parse the various fixed fields and IEs passed back for a BSS probe
* response or beacon from the scan command. Record information as needed
* in the scan table BSSDescriptor_t for that entry.
*
* @param pmadapter A pointer to mlan_adapter structure
* @param pbss_entry Output parameter: Pointer to the BSS Entry
* @param pbeacon_info Pointer to the Beacon information
* @param bytes_left Number of bytes left to parse
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
wlan_interpret_bss_desc_with_ie(IN pmlan_adapter pmadapter,
OUT BSSDescriptor_t * pbss_entry,
IN t_u8 ** pbeacon_info, IN t_u32 * bytes_left)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
IEEEtypes_ElementId_e element_id;
IEEEtypes_FhParamSet_t *pfh_param_set;
IEEEtypes_DsParamSet_t *pds_param_set;
IEEEtypes_CfParamSet_t *pcf_param_set;
IEEEtypes_IbssParamSet_t *pibss_param_set;
IEEEtypes_CapInfo_t *pcap_info;
WLAN_802_11_FIXED_IEs fixed_ie;
t_u8 *pcurrent_ptr;
t_u8 *prate;
t_u8 element_len;
t_u16 total_ie_len;
t_u8 bytes_to_copy;
t_u8 rate_size;
t_u16 beacon_size;
t_u8 found_data_rate_ie;
t_u32 bytes_left_for_current_beacon;
IEEEtypes_ERPInfo_t *perp_info;
IEEEtypes_VendorSpecific_t *pvendor_ie;
const t_u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 };
IEEEtypes_CountryInfoSet_t *pcountry_info;
ENTER();
found_data_rate_ie = MFALSE;
rate_size = 0;
beacon_size = 0;
if (*bytes_left >= sizeof(beacon_size)) {
/* Extract & convert beacon size from the command buffer */
memcpy(&beacon_size, *pbeacon_info, sizeof(beacon_size));
beacon_size = wlan_le16_to_cpu(beacon_size);
*bytes_left -= sizeof(beacon_size);
*pbeacon_info += sizeof(beacon_size);
}
if (!beacon_size || beacon_size > *bytes_left) {
*pbeacon_info += *bytes_left;
*bytes_left = 0;
LEAVE();
return MLAN_STATUS_FAILURE;
}
/* Initialize the current working beacon pointer for this BSS iteration */
pcurrent_ptr = *pbeacon_info;
/* Advance the return beacon pointer past the current beacon */
*pbeacon_info += beacon_size;
*bytes_left -= beacon_size;
bytes_left_for_current_beacon = beacon_size;
memcpy(pbss_entry->mac_address, pcurrent_ptr, MLAN_MAC_ADDR_LENGTH);
PRINTM(MINFO, "InterpretIE: AP MAC Addr-%02x:%02x:%02x:%02x:%02x:%02x\n",
pbss_entry->mac_address[0], pbss_entry->mac_address[1],
pbss_entry->mac_address[2], pbss_entry->mac_address[3],
pbss_entry->mac_address[4], pbss_entry->mac_address[5]);
pcurrent_ptr += MLAN_MAC_ADDR_LENGTH;
bytes_left_for_current_beacon -= MLAN_MAC_ADDR_LENGTH;
if (bytes_left_for_current_beacon < 12) {
PRINTM(MERROR, "InterpretIE: Not enough bytes left\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
/*
* Next 4 fields are RSSI, time stamp, beacon interval,
* and capability information
*/
/* RSSI is 1 byte long */
pbss_entry->rssi = (t_s32) (*pcurrent_ptr);
PRINTM(MINFO, "InterpretIE: RSSI=%02X\n", *pcurrent_ptr);
pcurrent_ptr += 1;
bytes_left_for_current_beacon -= 1;
/*
* The RSSI is not part of the beacon/probe response. After we have
* advanced pcurrent_ptr past the RSSI field, save the remaining
* data for use at the application layer
*/
pbss_entry->pbeacon_buf = pcurrent_ptr;
pbss_entry->beacon_buf_size = bytes_left_for_current_beacon;
/* Time stamp is 8 bytes long */
memcpy(fixed_ie.time_stamp, pcurrent_ptr, 8);
memcpy(pbss_entry->time_stamp, pcurrent_ptr, 8);
pcurrent_ptr += 8;
bytes_left_for_current_beacon -= 8;
/* Beacon interval is 2 bytes long */
memcpy(&fixed_ie.beacon_interval, pcurrent_ptr, 2);
pbss_entry->beacon_period = wlan_le16_to_cpu(fixed_ie.beacon_interval);
pcurrent_ptr += 2;
bytes_left_for_current_beacon -= 2;
/* Capability information is 2 bytes long */
memcpy(&fixed_ie.capabilities, pcurrent_ptr, 2);
PRINTM(MINFO, "InterpretIE: fixed_ie.capabilities=0x%X\n",
fixed_ie.capabilities);
fixed_ie.capabilities = wlan_le16_to_cpu(fixed_ie.capabilities);
pcap_info = (IEEEtypes_CapInfo_t *) & fixed_ie.capabilities;
memcpy(&pbss_entry->cap_info, pcap_info, sizeof(IEEEtypes_CapInfo_t));
pcurrent_ptr += 2;
bytes_left_for_current_beacon -= 2;
/* Rest of the current buffer are IE's */
PRINTM(MINFO, "InterpretIE: IELength for this AP = %d\n",
bytes_left_for_current_beacon);
HEXDUMP("InterpretIE: IE info", (t_u8 *) pcurrent_ptr,
bytes_left_for_current_beacon);
if (pcap_info->privacy) {
PRINTM(MINFO, "InterpretIE: AP WEP enabled\n");
pbss_entry->privacy = Wlan802_11PrivFilter8021xWEP;
} else {
pbss_entry->privacy = Wlan802_11PrivFilterAcceptAll;
}
if (pcap_info->ibss == 1) {
pbss_entry->bss_mode = MLAN_BSS_MODE_IBSS;
} else {
pbss_entry->bss_mode = MLAN_BSS_MODE_INFRA;
}
if (pcap_info->spectrum_mgmt == 1) {
PRINTM(MINFO, "InterpretIE: 11h- Spectrum Management "
"capability bit found\n");
pbss_entry->wlan_11h_bss_info.sensed_11h = 1;
}
/* Process variable IE */
while (bytes_left_for_current_beacon >= 2) {
element_id = (IEEEtypes_ElementId_e) (*((t_u8 *) pcurrent_ptr));
element_len = *((t_u8 *) pcurrent_ptr + 1);
total_ie_len = element_len + sizeof(IEEEtypes_Header_t);
if (bytes_left_for_current_beacon < total_ie_len) {
PRINTM(MERROR, "InterpretIE: Error in processing IE, "
"bytes left < IE length\n");
bytes_left_for_current_beacon = 0;
ret = MLAN_STATUS_FAILURE;
continue;
}
switch (element_id) {
case SSID:
pbss_entry->ssid.ssid_len = element_len;
memcpy(pbss_entry->ssid.ssid, (pcurrent_ptr + 2), element_len);
PRINTM(MINFO, "InterpretIE: ssid: %-32s\n", pbss_entry->ssid.ssid);
break;
case SUPPORTED_RATES:
memcpy(pbss_entry->data_rates, pcurrent_ptr + 2, element_len);
memcpy(pbss_entry->supported_rates, pcurrent_ptr + 2, element_len);
HEXDUMP("InterpretIE: SupportedRates:",
pbss_entry->supported_rates, element_len);
rate_size = element_len;
found_data_rate_ie = MTRUE;
break;
case FH_PARAM_SET:
pfh_param_set = (IEEEtypes_FhParamSet_t *) pcurrent_ptr;
pbss_entry->network_type_use = Wlan802_11FH;
memcpy(&pbss_entry->phy_param_set.fh_param_set, pfh_param_set,
sizeof(IEEEtypes_FhParamSet_t));
pbss_entry->phy_param_set.fh_param_set.dwell_time
=
wlan_le16_to_cpu(pbss_entry->phy_param_set.fh_param_set.
dwell_time);
break;
case DS_PARAM_SET:
pds_param_set = (IEEEtypes_DsParamSet_t *) pcurrent_ptr;
pbss_entry->network_type_use = Wlan802_11DS;
pbss_entry->channel = pds_param_set->current_chan;
memcpy(&pbss_entry->phy_param_set.ds_param_set, pds_param_set,
sizeof(IEEEtypes_DsParamSet_t));
break;
case CF_PARAM_SET:
pcf_param_set = (IEEEtypes_CfParamSet_t *) pcurrent_ptr;
memcpy(&pbss_entry->ss_param_set.cf_param_set, pcf_param_set,
sizeof(IEEEtypes_CfParamSet_t));
break;
case IBSS_PARAM_SET:
pibss_param_set = (IEEEtypes_IbssParamSet_t *) pcurrent_ptr;
pbss_entry->atim_window =
wlan_le16_to_cpu(pibss_param_set->atim_window);
memcpy(&pbss_entry->ss_param_set.ibss_param_set, pibss_param_set,
sizeof(IEEEtypes_IbssParamSet_t));
break;
/* Handle Country Info IE */
case COUNTRY_INFO:
pcountry_info = (IEEEtypes_CountryInfoSet_t *) pcurrent_ptr;
if (pcountry_info->len < sizeof(pcountry_info->country_code) ||
(unsigned) (pcountry_info->len + 2) >
sizeof(IEEEtypes_CountryInfoFullSet_t)) {
PRINTM(MERROR,
"InterpretIE: 11D- Err "
"country_info len =%d min=%d max=%d\n",
pcountry_info->len, sizeof(pcountry_info->country_code),
sizeof(IEEEtypes_CountryInfoFullSet_t));
LEAVE();
return MLAN_STATUS_FAILURE;
}
memcpy(&pbss_entry->country_info,
pcountry_info, pcountry_info->len + 2);
HEXDUMP("InterpretIE: 11D- country_info:",
(t_u8 *) pcountry_info, (t_u32) (pcountry_info->len + 2));
break;
case ERP_INFO:
perp_info = (IEEEtypes_ERPInfo_t *) pcurrent_ptr;
pbss_entry->erp_flags = perp_info->erp_flags;
break;
case POWER_CONSTRAINT:
case POWER_CAPABILITY:
case TPC_REPORT:
case CHANNEL_SWITCH_ANN:
case QUIET:
case IBSS_DFS:
case SUPPORTED_CHANNELS:
case TPC_REQUEST:
wlan_11h_process_bss_elem(&pbss_entry->wlan_11h_bss_info,
pcurrent_ptr);
break;
case EXTENDED_SUPPORTED_RATES:
/*
* Only process extended supported rate
* if data rate is already found.
* Data rate IE should come before
* extended supported rate IE
*/
if (found_data_rate_ie) {
if ((element_len + rate_size) > WLAN_SUPPORTED_RATES) {
bytes_to_copy = (WLAN_SUPPORTED_RATES - rate_size);
} else {
bytes_to_copy = element_len;
}
prate = (t_u8 *) pbss_entry->data_rates;
prate += rate_size;
memcpy(prate, pcurrent_ptr + 2, bytes_to_copy);
prate = (t_u8 *) pbss_entry->supported_rates;
prate += rate_size;
memcpy(prate, pcurrent_ptr + 2, bytes_to_copy);
}
HEXDUMP("InterpretIE: ExtSupportedRates:",
pbss_entry->supported_rates, element_len + rate_size);
break;
case VENDOR_SPECIFIC_221:
pvendor_ie = (IEEEtypes_VendorSpecific_t *) pcurrent_ptr;
if (!memcmp(pvendor_ie->vend_hdr.oui, wpa_oui, sizeof(wpa_oui))) {
pbss_entry->pwpa_ie =
(IEEEtypes_VendorSpecific_t *) pcurrent_ptr;
pbss_entry->wpa_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp WPA_IE",
(t_u8 *) pbss_entry->pwpa_ie,
((*(pbss_entry->pwpa_ie)).vend_hdr.len +
sizeof(IEEEtypes_Header_t)));
} else
if (!memcmp(pvendor_ie->vend_hdr.oui, wmm_oui, sizeof(wmm_oui)))
{
if (total_ie_len == sizeof(IEEEtypes_WmmParameter_t)
|| total_ie_len == sizeof(IEEEtypes_WmmInfo_t)) {
/*
* Only accept and copy the WMM IE if it matches
* the size expected for the WMM Info IE or the
* WMM Parameter IE.
*/
memcpy((t_u8 *) & pbss_entry->wmm_ie, pcurrent_ptr,
total_ie_len);
HEXDUMP("InterpretIE: Resp WMM_IE",
(t_u8 *) & pbss_entry->wmm_ie, total_ie_len);
}
}
break;
case RSN_IE:
pbss_entry->prsn_ie = (IEEEtypes_Generic_t *) pcurrent_ptr;
pbss_entry->rsn_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp RSN_IE", (t_u8 *) pbss_entry->prsn_ie,
(*(pbss_entry->prsn_ie)).ieee_hdr.len +
sizeof(IEEEtypes_Header_t));
break;
case WAPI_IE:
pbss_entry->pwapi_ie = (IEEEtypes_Generic_t *) pcurrent_ptr;
pbss_entry->wapi_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp WAPI_IE", (t_u8 *) pbss_entry->pwapi_ie,
(*(pbss_entry->pwapi_ie)).ieee_hdr.len +
sizeof(IEEEtypes_Header_t));
break;
case HT_CAPABILITY:
pbss_entry->pht_cap = (IEEEtypes_HTCap_t *) pcurrent_ptr;
pbss_entry->ht_cap_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp HTCAP_IE", (t_u8 *) pbss_entry->pht_cap,
(*(pbss_entry->pht_cap)).ieee_hdr.len +
sizeof(IEEEtypes_Header_t));
break;
case HT_OPERATION:
pbss_entry->pht_info = (IEEEtypes_HTInfo_t *) pcurrent_ptr;
pbss_entry->ht_info_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp HTINFO_IE",
(t_u8 *) pbss_entry->pht_info,
(*(pbss_entry->pht_info)).ieee_hdr.len +
sizeof(IEEEtypes_Header_t));
break;
case BSSCO_2040:
pbss_entry->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *) pcurrent_ptr;
pbss_entry->bss_co_2040_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp 2040BSSCOEXISTANCE_IE",
(t_u8 *) pbss_entry->pbss_co_2040,
(*(pbss_entry->pbss_co_2040)).ieee_hdr.len +
sizeof(IEEEtypes_Header_t));
break;
case EXT_CAPABILITY:
pbss_entry->pext_cap = (IEEEtypes_ExtCap_t *) pcurrent_ptr;
pbss_entry->ext_cap_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp EXTCAP_IE",
(t_u8 *) pbss_entry->pext_cap,
(*(pbss_entry->pext_cap)).ieee_hdr.len +
sizeof(IEEEtypes_Header_t));
break;
case OVERLAPBSSSCANPARAM:
pbss_entry->poverlap_bss_scan_param =
(IEEEtypes_OverlapBSSScanParam_t *) pcurrent_ptr;
pbss_entry->overlap_bss_offset =
(t_u16) (pcurrent_ptr - pbss_entry->pbeacon_buf);
HEXDUMP("InterpretIE: Resp HTCAP_IE",
(t_u8 *) pbss_entry->poverlap_bss_scan_param,
(*(pbss_entry->poverlap_bss_scan_param)).ieee_hdr.len +
sizeof(IEEEtypes_Header_t));
break;
}
pcurrent_ptr += element_len + 2;
/* Need to account for IE ID and IE Len */
bytes_left_for_current_beacon -= (element_len + 2);
} /* while (bytes_left_for_current_beacon > 2) */
LEAVE();
return ret;
}
/**
* @brief Store a beacon or probe response for a BSS returned in the scan
*
* Store a new scan response or an update for a previous scan response. New
* entries need to verify that they do not exceed the total amount of
* memory allocated for the table.
* Replacement entries need to take into consideration the amount of space
* currently allocated for the beacon/probe response and adjust the entry
* as needed.
*
* A small amount of extra pad (SCAN_BEACON_ENTRY_PAD) is generally reserved
* for an entry in case it is a beacon since a probe response for the
* network will by larger per the standard. This helps to reduce the
* amount of memory copying to fit a new probe response into an entry
* already occupied by a network's previously stored beacon.
*
* @param pmpriv A pointer to mlan_private structure
* @param beacon_idx Index in the scan table to store this entry; may be
* replacing an older duplicate entry for this BSS
* @param num_of_ent Number of entries currently in the table
* @param pnew_beacon Pointer to the new beacon/probe response to save
*
* @return n/a
*/
static t_void
wlan_ret_802_11_scan_store_beacon(IN mlan_private * pmpriv,
IN t_u32 beacon_idx,
IN t_u32 num_of_ent,
IN BSSDescriptor_t * pnew_beacon)
{
mlan_adapter *pmadapter = pmpriv->adapter;
t_u8 *pbcn_store;
t_u32 new_bcn_size;
t_u32 old_bcn_size;
t_u32 bcn_space;
t_u32 adj_idx;
ENTER();
if (pmadapter->pscan_table[beacon_idx].pbeacon_buf) {
new_bcn_size = pnew_beacon->beacon_buf_size;
old_bcn_size = pmadapter->pscan_table[beacon_idx].beacon_buf_size;
bcn_space = pmadapter->pscan_table[beacon_idx].beacon_buf_size_max;
pbcn_store = pmadapter->pscan_table[beacon_idx].pbeacon_buf;
/* Set the max to be the same as current entry unless changed below */
pnew_beacon->beacon_buf_size_max = bcn_space;
if (new_bcn_size == old_bcn_size) {
/*
* Beacon is the same size as the previous entry.
* Replace the previous contents with the scan result
*/
memcpy(pbcn_store,
pnew_beacon->pbeacon_buf, pnew_beacon->beacon_buf_size);
} else if (new_bcn_size <= bcn_space) {
/*
* New beacon size will fit in the amount of space
* we have previously allocated for it
*/
/* Copy the new beacon buffer entry over the old one */
memcpy(pbcn_store, pnew_beacon->pbeacon_buf, new_bcn_size);
/*
* If the old beacon size was less than the maximum
* we had alloted for the entry, and the new entry
* is even smaller, reset the max size to the old beacon
* entry and compress the storage space (leaving a new
* pad space of (old_bcn_size - new_bcn_size).
*/
if (old_bcn_size < bcn_space && new_bcn_size <= old_bcn_size) {
/*
* Old Beacon size is smaller than the alloted storage size.
* Shrink the alloted storage space.
*/
PRINTM(MINFO, "AppControl: Smaller Duplicate Beacon (%d), "
"old = %d, new = %d, space = %d, left = %d\n",
beacon_idx, old_bcn_size, new_bcn_size, bcn_space,
(sizeof(pmadapter->bcn_buf) -
(pmadapter->pbcn_buf_end - pmadapter->bcn_buf)));
/*
* memmove (since the memory overlaps) the data
* after the beacon we just stored to the end of
* the current beacon. This cleans up any unused
* space the old larger beacon was using in the buffer
*/
memmove(pbcn_store + old_bcn_size,
pbcn_store + bcn_space,
pmadapter->pbcn_buf_end - (pbcn_store + bcn_space));
/*
* Decrement the end pointer by the difference between
* the old larger size and the new smaller size since
* we are using less space due to the new beacon being
* smaller
*/
pmadapter->pbcn_buf_end -= (bcn_space - old_bcn_size);
/* Set the maximum storage size to the old beacon size */
pnew_beacon->beacon_buf_size_max = old_bcn_size;
/* Adjust beacon buffer pointers that are past the current */
for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) {
if (pmadapter->pscan_table[adj_idx].pbeacon_buf >
pbcn_store) {
pmadapter->pscan_table[adj_idx].pbeacon_buf -=
(bcn_space - old_bcn_size);
if (pmadapter->pscan_table[adj_idx].pwpa_ie) {
pmadapter->pscan_table[adj_idx].pwpa_ie =
(IEEEtypes_VendorSpecific_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].wpa_offset);
}
if (pmadapter->pscan_table[adj_idx].prsn_ie) {
pmadapter->pscan_table[adj_idx].prsn_ie =
(IEEEtypes_Generic_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].rsn_offset);
}
if (pmadapter->pscan_table[adj_idx].pwapi_ie) {
pmadapter->pscan_table[adj_idx].pwapi_ie =
(IEEEtypes_Generic_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].wapi_offset);
}
if (pmadapter->pscan_table[adj_idx].pht_cap) {
pmadapter->pscan_table[adj_idx].pht_cap =
(IEEEtypes_HTCap_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].ht_cap_offset);
}
if (pmadapter->pscan_table[adj_idx].pht_info) {
pmadapter->pscan_table[adj_idx].pht_info =
(IEEEtypes_HTInfo_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].
ht_info_offset);
}
if (pmadapter->pscan_table[adj_idx].pbss_co_2040) {
pmadapter->pscan_table[adj_idx].pbss_co_2040 =
(IEEEtypes_2040BSSCo_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].
bss_co_2040_offset);
}
if (pmadapter->pscan_table[adj_idx].pext_cap) {
pmadapter->pscan_table[adj_idx].pext_cap =
(IEEEtypes_ExtCap_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].
ext_cap_offset);
}
if (pmadapter->pscan_table[adj_idx].
poverlap_bss_scan_param) {
pmadapter->pscan_table[adj_idx].
poverlap_bss_scan_param =
(IEEEtypes_OverlapBSSScanParam_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].
overlap_bss_offset);
}
}
}
}
} else if (pmadapter->pbcn_buf_end + (new_bcn_size - bcn_space)
< (pmadapter->bcn_buf + sizeof(pmadapter->bcn_buf))) {
/*
* Beacon is larger than space previously allocated (bcn_space)
* and there is enough space left in the beaconBuffer to store
* the additional data
*/
PRINTM(MINFO, "AppControl: Larger Duplicate Beacon (%d), "
"old = %d, new = %d, space = %d, left = %d\n",
beacon_idx, old_bcn_size, new_bcn_size, bcn_space,
(sizeof(pmadapter->bcn_buf) -
(pmadapter->pbcn_buf_end - pmadapter->bcn_buf)));
/*
* memmove (since the memory overlaps) the data
* after the beacon we just stored to the end of
* the current beacon. This moves the data for
* the beacons after this further in memory to
* make space for the new larger beacon we are
* about to copy in.
*/
memmove(pbcn_store + new_bcn_size,
pbcn_store + bcn_space,
pmadapter->pbcn_buf_end - (pbcn_store + bcn_space));
/* Copy the new beacon buffer entry over the old one */
memcpy(pbcn_store, pnew_beacon->pbeacon_buf, new_bcn_size);
/* Move the beacon end pointer by the amount of new beacon data we
are adding */
pmadapter->pbcn_buf_end += (new_bcn_size - bcn_space);
/*
* This entry is bigger than the alloted max space
* previously reserved. Increase the max space to
* be equal to the new beacon size
*/
pnew_beacon->beacon_buf_size_max = new_bcn_size;
/* Adjust beacon buffer pointers that are past the current */
for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) {
if (pmadapter->pscan_table[adj_idx].pbeacon_buf > pbcn_store) {
pmadapter->pscan_table[adj_idx].pbeacon_buf
+= (new_bcn_size - bcn_space);
if (pmadapter->pscan_table[adj_idx].pwpa_ie) {
pmadapter->pscan_table[adj_idx].pwpa_ie =
(IEEEtypes_VendorSpecific_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].wpa_offset);
}
if (pmadapter->pscan_table[adj_idx].prsn_ie) {
pmadapter->pscan_table[adj_idx].prsn_ie =
(IEEEtypes_Generic_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].rsn_offset);
}
if (pmadapter->pscan_table[adj_idx].pwapi_ie) {
pmadapter->pscan_table[adj_idx].pwapi_ie =
(IEEEtypes_Generic_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].wapi_offset);
}
if (pmadapter->pscan_table[adj_idx].pht_cap) {
pmadapter->pscan_table[adj_idx].pht_cap =
(IEEEtypes_HTCap_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].ht_cap_offset);
}
if (pmadapter->pscan_table[adj_idx].pht_info) {
pmadapter->pscan_table[adj_idx].pht_info =
(IEEEtypes_HTInfo_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].ht_info_offset);
}
if (pmadapter->pscan_table[adj_idx].pbss_co_2040) {
pmadapter->pscan_table[adj_idx].pbss_co_2040 =
(IEEEtypes_2040BSSCo_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].
bss_co_2040_offset);
}
if (pmadapter->pscan_table[adj_idx].pext_cap) {
pmadapter->pscan_table[adj_idx].pext_cap =
(IEEEtypes_ExtCap_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].ext_cap_offset);
}
if (pmadapter->pscan_table[adj_idx].poverlap_bss_scan_param) {
pmadapter->pscan_table[adj_idx].
poverlap_bss_scan_param =
(IEEEtypes_OverlapBSSScanParam_t *)
(pmadapter->pscan_table[adj_idx].pbeacon_buf +
pmadapter->pscan_table[adj_idx].
overlap_bss_offset);
}
}
}
} else {
/*
* Beacon is larger than the previously allocated space, but
* there is not enough free space to store the additional data
*/
PRINTM(MERROR,
"AppControl: Failed: Larger Duplicate Beacon (%d),"
" old = %d, new = %d, space = %d, left = %d\n",
beacon_idx, old_bcn_size, new_bcn_size, bcn_space,
(sizeof(pmadapter->bcn_buf) -
(pmadapter->pbcn_buf_end - pmadapter->bcn_buf)));
/* Storage failure, keep old beacon intact */
pnew_beacon->beacon_buf_size = old_bcn_size;
if (pnew_beacon->pwpa_ie)
pnew_beacon->wpa_offset =
pmadapter->pscan_table[beacon_idx].wpa_offset;
if (pnew_beacon->prsn_ie)
pnew_beacon->rsn_offset =
pmadapter->pscan_table[beacon_idx].rsn_offset;
if (pnew_beacon->pwapi_ie)
pnew_beacon->wapi_offset =
pmadapter->pscan_table[beacon_idx].wapi_offset;
if (pnew_beacon->pht_cap)
pnew_beacon->ht_cap_offset =
pmadapter->pscan_table[beacon_idx].ht_cap_offset;
if (pnew_beacon->pht_info)
pnew_beacon->ht_info_offset =
pmadapter->pscan_table[beacon_idx].ht_info_offset;
if (pnew_beacon->pbss_co_2040)
pnew_beacon->bss_co_2040_offset =
pmadapter->pscan_table[beacon_idx].bss_co_2040_offset;
if (pnew_beacon->pext_cap)
pnew_beacon->ext_cap_offset =
pmadapter->pscan_table[beacon_idx].ext_cap_offset;
if (pnew_beacon->poverlap_bss_scan_param)
pnew_beacon->overlap_bss_offset =
pmadapter->pscan_table[beacon_idx].overlap_bss_offset;
}
/* Point the new entry to its permanent storage space */
pnew_beacon->pbeacon_buf = pbcn_store;
if (pnew_beacon->pwpa_ie) {
pnew_beacon->pwpa_ie = (IEEEtypes_VendorSpecific_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->wpa_offset);
}
if (pnew_beacon->prsn_ie) {
pnew_beacon->prsn_ie = (IEEEtypes_Generic_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->rsn_offset);
}
if (pnew_beacon->pwapi_ie) {
pnew_beacon->pwapi_ie = (IEEEtypes_Generic_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->wapi_offset);
}
if (pnew_beacon->pht_cap) {
pnew_beacon->pht_cap = (IEEEtypes_HTCap_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->ht_cap_offset);
}
if (pnew_beacon->pht_info) {
pnew_beacon->pht_info = (IEEEtypes_HTInfo_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->ht_info_offset);
}
if (pnew_beacon->pbss_co_2040) {
pnew_beacon->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->bss_co_2040_offset);
}
if (pnew_beacon->pext_cap) {
pnew_beacon->pext_cap = (IEEEtypes_ExtCap_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->ext_cap_offset);
}
if (pnew_beacon->poverlap_bss_scan_param) {
pnew_beacon->poverlap_bss_scan_param =
(IEEEtypes_OverlapBSSScanParam_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->overlap_bss_offset);
}
} else {
/*
* No existing beacon data exists for this entry, check to see
* if we can fit it in the remaining space
*/
if (pmadapter->pbcn_buf_end + pnew_beacon->beacon_buf_size +
SCAN_BEACON_ENTRY_PAD < (pmadapter->bcn_buf +
sizeof(pmadapter->bcn_buf))) {
/*
* Copy the beacon buffer data from the local entry to the
* adapter dev struct buffer space used to store the raw
* beacon data for each entry in the scan table
*/
memcpy(pmadapter->pbcn_buf_end, pnew_beacon->pbeacon_buf,
pnew_beacon->beacon_buf_size);
/* Update the beacon ptr to point to the table save area */
pnew_beacon->pbeacon_buf = pmadapter->pbcn_buf_end;
pnew_beacon->beacon_buf_size_max = (pnew_beacon->beacon_buf_size
+ SCAN_BEACON_ENTRY_PAD);
if (pnew_beacon->pwpa_ie) {
pnew_beacon->pwpa_ie = (IEEEtypes_VendorSpecific_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->wpa_offset);
}
if (pnew_beacon->prsn_ie) {
pnew_beacon->prsn_ie = (IEEEtypes_Generic_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->rsn_offset);
}
if (pnew_beacon->pwapi_ie) {
pnew_beacon->pwapi_ie = (IEEEtypes_Generic_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->wapi_offset);
}
if (pnew_beacon->pht_cap) {
pnew_beacon->pht_cap = (IEEEtypes_HTCap_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->ht_cap_offset);
}
if (pnew_beacon->pht_info) {
pnew_beacon->pht_info = (IEEEtypes_HTInfo_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->ht_info_offset);
}
if (pnew_beacon->pbss_co_2040) {
pnew_beacon->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *)
(pnew_beacon->pbeacon_buf +
pnew_beacon->bss_co_2040_offset);
}
if (pnew_beacon->pext_cap) {
pnew_beacon->pext_cap = (IEEEtypes_ExtCap_t *)
(pnew_beacon->pbeacon_buf + pnew_beacon->ext_cap_offset);
}
if (pnew_beacon->poverlap_bss_scan_param) {
pnew_beacon->poverlap_bss_scan_param =
(IEEEtypes_OverlapBSSScanParam_t *)
(pnew_beacon->pbeacon_buf +
pnew_beacon->overlap_bss_offset);
}
/* Increment the end pointer by the size reserved */
pmadapter->pbcn_buf_end += pnew_beacon->beacon_buf_size_max;
PRINTM(MINFO, "AppControl: Beacon[%02d] sz=%03d,"
" used = %04d, left = %04d\n",
beacon_idx,
pnew_beacon->beacon_buf_size,
(pmadapter->pbcn_buf_end - pmadapter->bcn_buf),
(sizeof(pmadapter->bcn_buf) -
(pmadapter->pbcn_buf_end - pmadapter->bcn_buf)));
} else {
/*
* No space for new beacon
*/
PRINTM(MINFO, "AppControl: No space beacon (%d): "
"%02x:%02x:%02x:%02x:%02x:%02x; sz=%03d, left=%03d\n",
beacon_idx,
pnew_beacon->mac_address[0], pnew_beacon->mac_address[1],
pnew_beacon->mac_address[2], pnew_beacon->mac_address[3],
pnew_beacon->mac_address[4], pnew_beacon->mac_address[5],
pnew_beacon->beacon_buf_size,
(sizeof(pmadapter->bcn_buf) -
(pmadapter->pbcn_buf_end - pmadapter->bcn_buf)));
/* Storage failure; clear storage records for this bcn */
pnew_beacon->pbeacon_buf = MNULL;
pnew_beacon->beacon_buf_size = 0;
pnew_beacon->beacon_buf_size_max = 0;
pnew_beacon->pwpa_ie = MNULL;
pnew_beacon->wpa_offset = 0;
pnew_beacon->prsn_ie = MNULL;
pnew_beacon->rsn_offset = 0;
pnew_beacon->pwapi_ie = MNULL;
pnew_beacon->wapi_offset = 0;
pnew_beacon->pht_cap = MNULL;
pnew_beacon->ht_cap_offset = 0;
pnew_beacon->pht_info = MNULL;
pnew_beacon->ht_info_offset = 0;
pnew_beacon->pbss_co_2040 = MNULL;
pnew_beacon->bss_co_2040_offset = 0;
pnew_beacon->pext_cap = MNULL;
pnew_beacon->ext_cap_offset = 0;
pnew_beacon->poverlap_bss_scan_param = MNULL;
pnew_beacon->overlap_bss_offset = 0;
}
}
LEAVE();
}
/**
* @brief Post process the scan table after a new scan command has completed
*
* Inspect each entry of the scan table and try to find an entry that
* matches our current associated/joined network from the scan. If
* one is found, update the stored copy of the BSSDescriptor for our
* current network.
*
* Debug dump the current scan table contents if compiled accordingly.
*
* @param pmpriv A pointer to mlan_private structure
*
* @return n/a
*/
static t_void
wlan_scan_process_results(IN mlan_private * pmpriv)
{
mlan_adapter *pmadapter = pmpriv->adapter;
t_s32 j;
t_u32 i;
ENTER();
if (pmpriv->media_connected == MTRUE) {
j = wlan_find_ssid_in_list(pmpriv,
&pmpriv->curr_bss_params.bss_descriptor.ssid,
pmpriv->curr_bss_params.bss_descriptor.
mac_address, pmpriv->bss_mode);
if (j >= 0) {
pmadapter->callbacks.moal_spin_lock(pmpriv->curr_bcn_buf_lock);
pmpriv->curr_bss_params.bss_descriptor.pwpa_ie = MNULL;
pmpriv->curr_bss_params.bss_descriptor.wpa_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.prsn_ie = MNULL;
pmpriv->curr_bss_params.bss_descriptor.rsn_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.pwapi_ie = MNULL;
pmpriv->curr_bss_params.bss_descriptor.wapi_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.pht_cap = MNULL;
pmpriv->curr_bss_params.bss_descriptor.ht_cap_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.pht_info = MNULL;
pmpriv->curr_bss_params.bss_descriptor.ht_info_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.pbss_co_2040 = MNULL;
pmpriv->curr_bss_params.bss_descriptor.bss_co_2040_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.pext_cap = MNULL;
pmpriv->curr_bss_params.bss_descriptor.ext_cap_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.poverlap_bss_scan_param =
MNULL;
pmpriv->curr_bss_params.bss_descriptor.overlap_bss_offset = 0;
pmpriv->curr_bss_params.bss_descriptor.pbeacon_buf = MNULL;
pmpriv->curr_bss_params.bss_descriptor.beacon_buf_size = 0;
pmpriv->curr_bss_params.bss_descriptor.beacon_buf_size_max = 0;
PRINTM(MINFO, "Found current ssid/bssid in list @ index #%d\n", j);
/* Make a copy of current BSSID descriptor */
memcpy(&pmpriv->curr_bss_params.bss_descriptor,
&pmadapter->pscan_table[j],
sizeof(pmpriv->curr_bss_params.bss_descriptor));
wlan_save_curr_bcn(pmpriv);
pmadapter->callbacks.moal_spin_unlock(pmpriv->curr_bcn_buf_lock);
} else {
wlan_restore_curr_bcn(pmpriv);
}
}
for (i = 0; i < pmadapter->num_in_scan_table; i++)
PRINTM(MINFO, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, "
"RSSI[%03d], SSID[%s]\n",
i,
pmadapter->pscan_table[i].mac_address[0],
pmadapter->pscan_table[i].mac_address[1],
pmadapter->pscan_table[i].mac_address[2],
pmadapter->pscan_table[i].mac_address[3],
pmadapter->pscan_table[i].mac_address[4],
pmadapter->pscan_table[i].mac_address[5],
(t_s32) pmadapter->pscan_table[i].rssi,
pmadapter->pscan_table[i].ssid.ssid);
/*
* Prepares domain info from scan table and downloads the
* domain info command to the FW.
*/
wlan_11d_prepare_dnld_domain_info_cmd(pmpriv);
LEAVE();
}
/**
* @brief Convert radio type scan parameter to a band config used in join cmd
*
* @param radio_type Scan parameter indicating the radio used for a channel
* in a scan command.
*
* @return Band type conversion of scanBand used in join/assoc cmds
*
*/
static t_u8
radio_type_to_band(t_u8 radio_type)
{
t_u8 ret_band;
switch (radio_type) {
case HostCmd_SCAN_RADIO_TYPE_A:
ret_band = BAND_A;
break;
case HostCmd_SCAN_RADIO_TYPE_BG:
default:
ret_band = BAND_G;
break;
}
return ret_band;
}
/**
* @brief Delete a specific indexed entry from the scan table.
*
* Delete the scan table entry indexed by table_idx. Compact the remaining
* entries and adjust any buffering of beacon/probe response data
* if needed.
*
* @param pmpriv A pointer to mlan_private structure
* @param table_idx Scan table entry index to delete from the table
*
* @return N/A
*
* @pre table_idx must be an index to a valid entry
*/
static t_void
wlan_scan_delete_table_entry(IN mlan_private * pmpriv, IN t_s32 table_idx)
{
mlan_adapter *pmadapter = pmpriv->adapter;
t_u32 del_idx;
t_u32 beacon_buf_adj;
t_u8 *pbeacon_buf;
ENTER();
/*
* Shift the saved beacon buffer data for the scan table back over the
* entry being removed. Update the end of buffer pointer. Save the
* deleted buffer allocation size for pointer adjustments for entries
* compacted after the deleted index.
*/
beacon_buf_adj = pmadapter->pscan_table[table_idx].beacon_buf_size_max;
PRINTM(MINFO, "Scan: Delete Entry %d, beacon buffer removal = %d bytes\n",
table_idx, beacon_buf_adj);
/* Check if the table entry had storage allocated for its beacon */
if (beacon_buf_adj) {
pbeacon_buf = pmadapter->pscan_table[table_idx].pbeacon_buf;
/*
* Remove the entry's buffer space, decrement the table end pointer
* by the amount we are removing
*/
pmadapter->pbcn_buf_end -= beacon_buf_adj;
PRINTM(MINFO,
"Scan: Delete Entry %d, compact data: %p <- %p (sz = %d)\n",
table_idx,
pbeacon_buf,
pbeacon_buf + beacon_buf_adj,
pmadapter->pbcn_buf_end - pbeacon_buf);
/*
* Compact data storage. Copy all data after the deleted entry's
* end address (pbeacon_buf + beacon_buf_adj) back to the original
* start address (pbeacon_buf).
*
* Scan table entries affected by the move will have their entry
* pointer adjusted below.
*
* Use memmove since the dest/src memory regions overlap.
*/
memmove(pbeacon_buf,
pbeacon_buf + beacon_buf_adj,
pmadapter->pbcn_buf_end - pbeacon_buf);
}
PRINTM(MINFO, "Scan: Delete Entry %d, num_in_scan_table = %d\n",
table_idx, pmadapter->num_in_scan_table);
/* Shift all of the entries after the table_idx back by one, compacting the
table and removing the requested entry */
for (del_idx = table_idx; (del_idx + 1) < pmadapter->num_in_scan_table;
del_idx++) {
/* Copy the next entry over this one */
memcpy(pmadapter->pscan_table + del_idx,
pmadapter->pscan_table + del_idx + 1, sizeof(BSSDescriptor_t));
/*
* Adjust this entry's pointer to its beacon buffer based on the
* removed/compacted entry from the deleted index. Don't decrement
* if the buffer pointer is MNULL (no data stored for this entry).
*/
if (pmadapter->pscan_table[del_idx].pbeacon_buf) {
pmadapter->pscan_table[del_idx].pbeacon_buf -= beacon_buf_adj;
if (pmadapter->pscan_table[del_idx].pwpa_ie) {
pmadapter->pscan_table[del_idx].pwpa_ie =
(IEEEtypes_VendorSpecific_t *)
(pmadapter->pscan_table[del_idx].pbeacon_buf +
pmadapter->pscan_table[del_idx].wpa_offset);
}
if (pmadapter->pscan_table[del_idx].prsn_ie) {
pmadapter->pscan_table[del_idx].prsn_ie =
(IEEEtypes_Generic_t *)
(pmadapter->pscan_table[del_idx].pbeacon_buf +
pmadapter->pscan_table[del_idx].rsn_offset);
}
if (pmadapter->pscan_table[del_idx].pwapi_ie) {
pmadapter->pscan_table[del_idx].pwapi_ie =
(IEEEtypes_Generic_t *)
(pmadapter->pscan_table[del_idx].pbeacon_buf +
pmadapter->pscan_table[del_idx].wapi_offset);
}
if (pmadapter->pscan_table[del_idx].pht_cap) {
pmadapter->pscan_table[del_idx].pht_cap =
(IEEEtypes_HTCap_t *) (pmadapter->pscan_table[del_idx].
pbeacon_buf +
pmadapter->pscan_table[del_idx].
ht_cap_offset);
}
if (pmadapter->pscan_table[del_idx].pht_info) {
pmadapter->pscan_table[del_idx].pht_info =
(IEEEtypes_HTInfo_t *) (pmadapter->pscan_table[del_idx].
pbeacon_buf +
pmadapter->pscan_table[del_idx].
ht_info_offset);
}
if (pmadapter->pscan_table[del_idx].pbss_co_2040) {
pmadapter->pscan_table[del_idx].pbss_co_2040 =
(IEEEtypes_2040BSSCo_t *) (pmadapter->pscan_table[del_idx].
pbeacon_buf +
pmadapter->pscan_table[del_idx].
bss_co_2040_offset);
}
if (pmadapter->pscan_table[del_idx].pext_cap) {
pmadapter->pscan_table[del_idx].pext_cap =
(IEEEtypes_ExtCap_t *) (pmadapter->pscan_table[del_idx].
pbeacon_buf +
pmadapter->pscan_table[del_idx].
ext_cap_offset);
}
if (pmadapter->pscan_table[del_idx].poverlap_bss_scan_param) {
pmadapter->pscan_table[del_idx].poverlap_bss_scan_param =
(IEEEtypes_OverlapBSSScanParam_t *) (pmadapter->
pscan_table[del_idx].
pbeacon_buf +
pmadapter->
pscan_table[del_idx].
overlap_bss_offset);
}
}
}
/* The last entry is invalid now that it has been deleted or moved back */
memset(pmadapter->pscan_table + pmadapter->num_in_scan_table - 1,
0x00, sizeof(BSSDescriptor_t));
pmadapter->num_in_scan_table--;
LEAVE();
}
/**
* @brief Delete all occurrences of a given SSID from the scan table
*
* Iterate through the scan table and delete all entries that match a given
* SSID. Compact the remaining scan table entries.
*
* @param pmpriv A pointer to mlan_private structure
* @param pdel_ssid Pointer to an SSID to be used in deleting all
* matching SSIDs from the scan table
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
wlan_scan_delete_ssid_table_entry(IN mlan_private * pmpriv,
IN mlan_802_11_ssid * pdel_ssid)
{
mlan_status ret = MLAN_STATUS_FAILURE;
t_s32 table_idx;
ENTER();
PRINTM(MINFO, "Scan: Delete Ssid Entry: %-32s\n", pdel_ssid->ssid);
/* If the requested SSID is found in the table, delete it. Then keep
searching the table for multiple entires for the SSID until no more are
found */
while ((table_idx = wlan_find_ssid_in_list(pmpriv,
pdel_ssid,
MNULL,
MLAN_BSS_MODE_AUTO)) >= 0) {
PRINTM(MINFO, "Scan: Delete SSID Entry: Found Idx = %d\n", table_idx);
ret = MLAN_STATUS_SUCCESS;
wlan_scan_delete_table_entry(pmpriv, table_idx);
}
LEAVE();
return ret;
}
/********************************************************
Global Functions
********************************************************/
/**
* @brief Internal function used to start a scan based on an input config
*
* Use the input user scan configuration information when provided in
* order to send the appropriate scan commands to firmware to populate or
* update the internal driver scan table
*
* @param pmpriv A pointer to mlan_private structure
* @param pioctl_buf A pointer to MLAN IOCTl Request buffer
* @param puser_scan_in Pointer to the input configuration for the requested
* scan.
*
* @return MLAN_STATUS_SUCCESS or < 0 if error
*/
mlan_status
wlan_scan_networks(IN mlan_private * pmpriv,
IN t_void * pioctl_buf,
IN const wlan_user_scan_cfg * puser_scan_in)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_adapter *pmadapter = pmpriv->adapter;
mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks;
cmd_ctrl_node *pcmd_node = MNULL;
wlan_scan_cmd_config_tlv *pscan_cfg_out = MNULL;
MrvlIEtypes_ChanListParamSet_t *pchan_list_out;
t_u32 buf_size;
ChanScanParamSet_t *pscan_chan_list;
t_u8 keep_previous_scan;
t_u8 filtered_scan;
t_u8 scan_current_chan_only;
t_u8 max_chan_per_scan;
ENTER();
ret = pcb->moal_malloc(sizeof(wlan_scan_cmd_config_tlv),
(t_u8 **) & pscan_cfg_out);
if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg_out) {
PRINTM(MERROR, "Memory allocation for pscan_cfg_out failed!\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
buf_size = sizeof(ChanScanParamSet_t) * WLAN_USER_SCAN_CHAN_MAX;
ret = pcb->moal_malloc(buf_size, (t_u8 **) & pscan_chan_list);
if (ret != MLAN_STATUS_SUCCESS || !pscan_chan_list) {
PRINTM(MERROR, "Failed to allocate scan_chan_list\n");
if (pscan_cfg_out)
pcb->moal_mfree((t_u8 *) pscan_cfg_out);
LEAVE();
return MLAN_STATUS_FAILURE;
}
memset(pscan_chan_list, 0x00, buf_size);
memset(pscan_cfg_out, 0x00, sizeof(wlan_scan_cmd_config_tlv));
keep_previous_scan = MFALSE;
wlan_scan_setup_scan_config(pmpriv,
puser_scan_in,
&pscan_cfg_out->config,
&pchan_list_out,
pscan_chan_list,
&max_chan_per_scan,
&filtered_scan, &scan_current_chan_only);
if (puser_scan_in) {
keep_previous_scan = puser_scan_in->keep_previous_scan;
}
if (keep_previous_scan == MFALSE) {
memset(pmadapter->pscan_table, 0x00,
sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST);
pmadapter->num_in_scan_table = 0;
pmadapter->pbcn_buf_end = pmadapter->bcn_buf;
}
ret = wlan_scan_channel_list(pmpriv,
pioctl_buf,
max_chan_per_scan,
filtered_scan,
&pscan_cfg_out->config,
pchan_list_out, pscan_chan_list);
/* Get scan command from scan_pending_q and put to cmd_pending_q */
if (ret == MLAN_STATUS_SUCCESS) {
if (util_peek_list
(&pmadapter->scan_pending_q, pcb->moal_spin_lock,
pcb->moal_spin_unlock)) {
pcmd_node =
(cmd_ctrl_node *) util_dequeue_list(&pmadapter->scan_pending_q,
pcb->moal_spin_lock,
pcb->moal_spin_unlock);
wlan_request_cmd_lock(pmadapter);
pmadapter->scan_processing = MTRUE;
wlan_release_cmd_lock(pmadapter);
wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE);
}
}
if (pscan_cfg_out)
pcb->moal_mfree((t_u8 *) pscan_cfg_out);
if (pscan_chan_list)
pcb->moal_mfree((t_u8 *) pscan_chan_list);
LEAVE();
return ret;
}
/**
* @brief Prepare a scan command to be sent to the firmware
*
* Use the wlan_scan_cmd_config sent to the command processing module in
* the wlan_prepare_cmd to configure a HostCmd_DS_802_11_SCAN command
* struct to send to firmware.
*
* The fixed fields specifying the BSS type and BSSID filters as well as a
* variable number/length of TLVs are sent in the command to firmware.
*
* @param pmpriv A pointer to mlan_private structure
* @param pcmd A pointer to HostCmd_DS_COMMAND structure to be sent to
* firmware with the HostCmd_DS_801_11_SCAN structure
* @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used
* to set the fields/TLVs for the command sent to firmware
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_cmd_802_11_scan(IN mlan_private * pmpriv,
IN HostCmd_DS_COMMAND * pcmd, IN t_void * pdata_buf)
{
HostCmd_DS_802_11_SCAN *pscan_cmd = &pcmd->params.scan;
wlan_scan_cmd_config *pscan_cfg;
ENTER();
pscan_cfg = (wlan_scan_cmd_config *) pdata_buf;
/* Set fixed field variables in scan command */
pscan_cmd->bss_mode = pscan_cfg->bss_mode;
memcpy(pscan_cmd->bssid, pscan_cfg->specific_bssid,
sizeof(pscan_cmd->bssid));
memcpy(pscan_cmd->tlv_buffer, pscan_cfg->tlv_buf, pscan_cfg->tlv_buf_len);
pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN);
/* Size is equal to the sizeof(fixed portions) + the TLV len + header */
pcmd->size = (t_u16) wlan_cpu_to_le16((t_u16) (sizeof(pscan_cmd->bss_mode)
+ sizeof(pscan_cmd->bssid)
+ pscan_cfg->tlv_buf_len
+ S_DS_GEN));
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of scan
*
* The response buffer for the scan command has the following
* memory layout:
*
* .-------------------------------------------------------------.
* | Header (4 * sizeof(t_u16)): Standard command response hdr |
* .-------------------------------------------------------------.
* | BufSize (t_u16) : sizeof the BSS Description data |
* .-------------------------------------------------------------.
* | NumOfSet (t_u8) : Number of BSS Descs returned |
* .-------------------------------------------------------------.
* | BSSDescription data (variable, size given in BufSize) |
* .-------------------------------------------------------------.
* | TLV data (variable, size calculated using Header->Size, |
* | BufSize and sizeof the fixed fields above) |
* .-------------------------------------------------------------.
*
* @param pmpriv A pointer to mlan_private structure
* @param resp A pointer to HostCmd_DS_COMMAND
* @param pioctl_buf A pointer to mlan_ioctl_req structure
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_ret_802_11_scan(IN mlan_private * pmpriv,
IN HostCmd_DS_COMMAND * resp, IN t_void * pioctl_buf)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_adapter *pmadapter = pmpriv->adapter;
mlan_callbacks *pcb = MNULL;
mlan_ioctl_req *pioctl_req = (mlan_ioctl_req *) pioctl_buf;
cmd_ctrl_node *pcmd_node = MNULL;
HostCmd_DS_802_11_SCAN_RSP *pscan_rsp = MNULL;
BSSDescriptor_t *bss_new_entry = MNULL;
MrvlIEtypes_Data_t *ptlv;
MrvlIEtypes_TsfTimestamp_t *ptsf_tlv;
t_u8 *pbss_info;
t_u32 scan_resp_size;
t_u32 bytes_left;
t_u32 num_in_table;
t_u32 bss_idx;
t_u32 idx;
t_u32 tlv_buf_size;
t_u64 tsf_val;
chan_freq_power_t *cfp;
MrvlIEtypes_ChanBandListParamSet_t *pchan_band_tlv;
ChanBandParamSet_t *pchan_band;
t_u8 band;
t_u8 is_bgscan_resp;
ENTER();
pcb = (pmlan_callbacks) & pmadapter->callbacks;
is_bgscan_resp = (resp->command == HostCmd_CMD_802_11_BG_SCAN_QUERY);
if (is_bgscan_resp) {
pscan_rsp = &resp->params.bg_scan_query_resp.scan_resp;
} else {
pscan_rsp = &resp->params.scan_resp;
}
if (pscan_rsp->number_of_sets > MRVDRV_MAX_BSSID_LIST) {
PRINTM(MERROR, "SCAN_RESP: Invalid number of AP returned (%d)!!\n",
pscan_rsp->number_of_sets);
ret = MLAN_STATUS_FAILURE;
goto done;
}
bytes_left = wlan_le16_to_cpu(pscan_rsp->bss_descript_size);
PRINTM(MINFO, "SCAN_RESP: bss_descript_size %d\n", bytes_left);
scan_resp_size = resp->size;
PRINTM(MINFO, "SCAN_RESP: returned %d APs before parsing\n",
pscan_rsp->number_of_sets);
num_in_table = pmadapter->num_in_scan_table;
pbss_info = pscan_rsp->bss_desc_and_tlv_buffer;
/*
* The size of the TLV buffer is equal to the entire command response
* size (scan_resp_size) minus the fixed fields (sizeof()'s), the
* BSS Descriptions (bss_descript_size as bytesLef) and the command
* response header (S_DS_GEN)
*/
tlv_buf_size = scan_resp_size - (bytes_left
+ sizeof(pscan_rsp->bss_descript_size)
+ sizeof(pscan_rsp->number_of_sets)
+ S_DS_GEN);
ptlv =
(MrvlIEtypes_Data_t *) (pscan_rsp->bss_desc_and_tlv_buffer +
bytes_left);
/* Search the TLV buffer space in the scan response for any valid TLVs */
wlan_ret_802_11_scan_get_tlv_ptrs(pmadapter,
ptlv,
tlv_buf_size,
TLV_TYPE_TSFTIMESTAMP,
(MrvlIEtypes_Data_t **) & ptsf_tlv);
/* Search the TLV buffer space in the scan response for any valid TLVs */
wlan_ret_802_11_scan_get_tlv_ptrs(pmadapter,
ptlv,
tlv_buf_size,
TLV_TYPE_CHANNELBANDLIST,
(MrvlIEtypes_Data_t **) & pchan_band_tlv);
/*
* Process each scan response returned (pscan_rsp->number_of_sets). Save
* the information in the bss_new_entry and then insert into the
* driver scan table either as an update to an existing entry
* or as an addition at the end of the table
*/
ret = pcb->moal_malloc(sizeof(BSSDescriptor_t), (t_u8 **) & bss_new_entry);
if (ret != MLAN_STATUS_SUCCESS || !bss_new_entry) {
PRINTM(MERROR, "Memory allocation for bss_new_entry failed!\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
for (idx = 0; idx < pscan_rsp->number_of_sets && bytes_left; idx++) {
/* Zero out the bss_new_entry we are about to store info in */
memset(bss_new_entry, 0x00, sizeof(BSSDescriptor_t));
/* Process the data fields and IEs returned for this BSS */
if ((wlan_interpret_bss_desc_with_ie(pmadapter,
bss_new_entry,
&pbss_info,
&bytes_left) ==
MLAN_STATUS_SUCCESS)
&& CHECK_SSID_IS_VALID(bss_new_entry.ssid)) {
PRINTM(MINFO, "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n",
bss_new_entry->mac_address[0], bss_new_entry->mac_address[1],
bss_new_entry->mac_address[2], bss_new_entry->mac_address[3],
bss_new_entry->mac_address[4],
bss_new_entry->mac_address[5]);
/*
* Search the scan table for the same bssid
*/
for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) {
if (!memcmp(bss_new_entry->mac_address,
pmadapter->pscan_table[bss_idx].mac_address,
sizeof(bss_new_entry->mac_address))) {
/*
* If the SSID matches as well, it is a duplicate of
* this entry. Keep the bss_idx set to this
* entry so we replace the old contents in the table
*/
if ((bss_new_entry->ssid.ssid_len ==
pmadapter->pscan_table[bss_idx].ssid.ssid_len)
&& (!memcmp(bss_new_entry->ssid.ssid,
pmadapter->pscan_table[bss_idx].ssid.ssid,
bss_new_entry->ssid.ssid_len))) {
PRINTM(MINFO, "SCAN_RESP: Duplicate of index: %d\n",
bss_idx);
break;
}
}
}
/*
* If the bss_idx is equal to the number of entries in the table,
* the new entry was not a duplicate; append it to the scan
* table
*/
if (bss_idx == num_in_table) {
/* Range check the bss_idx, keep it limited to the last entry */
if (bss_idx == MRVDRV_MAX_BSSID_LIST) {
bss_idx--;
} else {
num_in_table++;
}
}
/*
* Save the beacon/probe response returned for later application
* retrieval. Duplicate beacon/probe responses are updated if
* possible
*/
wlan_ret_802_11_scan_store_beacon(pmpriv,
bss_idx,
num_in_table, bss_new_entry);
/*
* If the TSF TLV was appended to the scan results, save
* this entry's TSF value in the networkTSF field. The
* networkTSF is the firmware's TSF value at the time the
* beacon or probe response was received.
*/
if (ptsf_tlv) {
memcpy(&tsf_val, &ptsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
sizeof(tsf_val));
tsf_val = wlan_le64_to_cpu(tsf_val);
memcpy(&bss_new_entry->network_tsf,
&tsf_val, sizeof(bss_new_entry->network_tsf));
}
band = BAND_G;
if (pchan_band_tlv) {
pchan_band = &pchan_band_tlv->chan_band_param[idx];
band =
radio_type_to_band(pchan_band->
radio_type & (MBIT(0) | MBIT(1)));
}
/* Save the band designation for this entry for use in join */
bss_new_entry->bss_band = band;
cfp =
wlan_find_cfp_by_band_and_channel(pmadapter,
(t_u8) bss_new_entry->
bss_band,
(t_u16) bss_new_entry->
channel);
if (cfp)
bss_new_entry->freq = cfp->freq;
else
bss_new_entry->freq = 0;
/* Copy the locally created bss_new_entry to the scan table */
memcpy(&pmadapter->pscan_table[bss_idx],
bss_new_entry, sizeof(pmadapter->pscan_table[bss_idx]));
} else {
/* Error parsing/interpreting the scan response, skipped */
PRINTM(MERROR, "SCAN_RESP: "
"wlan_interpret_bss_desc_with_ie returned ERROR\n");
}
}
PRINTM(MINFO, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n",
pscan_rsp->number_of_sets,
num_in_table - pmadapter->num_in_scan_table, num_in_table);
/* Update the total number of BSSIDs in the scan table */
pmadapter->num_in_scan_table = num_in_table;
if (!util_peek_list
(&pmadapter->scan_pending_q, pcb->moal_spin_lock,
pcb->moal_spin_unlock)) {
wlan_request_cmd_lock(pmadapter);
pmadapter->scan_processing = MFALSE;
wlan_release_cmd_lock(pmadapter);
/*
* Process the resulting scan table:
* - Remove any bad ssids
* - Update our current BSS information from scan data
*/
wlan_scan_process_results(pmpriv);
/* Need to indicate IOCTL complete */
if (pioctl_req != MNULL) {
pioctl_req->status_code = MLAN_ERROR_NO_ERROR;
/* Indicate ioctl complete */
pcb->moal_ioctl_complete(pmadapter->pmoal_handle,
(pmlan_ioctl_req) pioctl_buf,
MLAN_STATUS_SUCCESS);
}
wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL);
} else {
/* Get scan command from scan_pending_q and put to cmd_pending_q */
pcmd_node =
(cmd_ctrl_node *) util_dequeue_list(&pmadapter->scan_pending_q,
pcb->moal_spin_lock,
pcb->moal_spin_unlock);
wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE);
}
done:
if (bss_new_entry)
pcb->moal_mfree((t_u8 *) bss_new_entry);
LEAVE();
return ret;
}
/**
* @brief This function prepares command of bg_scan_query.
*
* @param pmpriv A pointer to mlan_private structure
* @param pcmd A pointer to HostCmd_DS_COMMAND structure
* @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used
* to set the fields/TLVs for the command sent to firmware
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_cmd_802_11_bg_scan_query(IN mlan_private * pmpriv,
IN HostCmd_DS_COMMAND * pcmd,
IN t_void * pdata_buf)
{
HostCmd_DS_802_11_BG_SCAN_QUERY *bg_query = &pcmd->params.bg_scan_query;
ENTER();
pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY);
pcmd->size =
wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_BG_SCAN_QUERY) + S_DS_GEN);
bg_query->flush = 1;
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function finds ssid in ssid list.
*
* @param pmpriv A pointer to mlan_private structure
* @param ssid SSID to find in the list
* @param bssid BSSID to qualify the SSID selection (if provided)
* @param mode Network mode: Infrastructure or IBSS
*
* @return index in BSSID list or < 0 if error
*/
t_s32
wlan_find_ssid_in_list(IN mlan_private * pmpriv,
IN mlan_802_11_ssid * ssid,
IN t_u8 * bssid, IN t_u32 mode)
{
mlan_adapter *pmadapter = pmpriv->adapter;
t_s32 net = -1, j;
t_u8 best_rssi = 0;
t_u32 i;
ENTER();
PRINTM(MINFO, "Num of Entries in Table = %d\n",
pmadapter->num_in_scan_table);
/*
* Loop through the table until the maximum is reached or until a match
* is found based on the bssid field comparison
*/
for (i = 0;
i < pmadapter->num_in_scan_table && (!bssid || (bssid && net < 0));
i++) {
if (!wlan_ssid_cmp(pmadapter, &pmadapter->pscan_table[i].ssid, ssid) &&
(!bssid
|| !memcmp(pmadapter->pscan_table[i].mac_address, bssid,
MLAN_MAC_ADDR_LENGTH))) {
switch (mode) {
case MLAN_BSS_MODE_INFRA:
case MLAN_BSS_MODE_IBSS:
j = wlan_is_network_compatible(pmpriv, i, mode);
if (j >= 0) {
if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > best_rssi) {
best_rssi = SCAN_RSSI(pmadapter->pscan_table[i].rssi);
net = i;
}
} else {
if (net == -1) {
net = j;
}
}
break;
case MLAN_BSS_MODE_AUTO:
default:
/*
* Do not check compatibility if the mode requested is
* Auto/Unknown. Allows generic find to work without
* verifying against the Adapter security settings
*/
if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > best_rssi) {
best_rssi = SCAN_RSSI(pmadapter->pscan_table[i].rssi);
net = i;
}
break;
}
}
}
if (net >= 0) {
if (!wlan_find_cfp_by_band_and_channel(pmadapter,
(t_u8) pmadapter->
pscan_table[net].bss_band,
(t_u16) pmadapter->
pscan_table[net].channel)) {
net = -1;
}
}
LEAVE();
return net;
}
/**
* @brief This function finds a specific compatible BSSID in the scan list
*
* @param pmpriv A pointer to mlan_private structure
* @param bssid BSSID to find in the scan list
* @param mode Network mode: Infrastructure or IBSS
*
* @return index in BSSID list or < 0 if error
*/
t_s32
wlan_find_bssid_in_list(IN mlan_private * pmpriv,
IN t_u8 * bssid, IN t_u32 mode)
{
mlan_adapter *pmadapter = pmpriv->adapter;
t_s32 net = -1;
t_u32 i;
ENTER();
if (!bssid) {
LEAVE();
return -1;
}
PRINTM(MINFO, "FindBSSID: Num of BSSIDs = %d\n",
pmadapter->num_in_scan_table);
/*
* Look through the scan table for a compatible match. The ret return
* variable will be equal to the index in the scan table (greater
* than zero) if the network is compatible. The loop will continue
* past a matched bssid that is not compatible in case there is an
* AP with multiple SSIDs assigned to the same BSSID
*/
for (i = 0; net < 0 && i < pmadapter->num_in_scan_table; i++) {
if (!memcmp
(pmadapter->pscan_table[i].mac_address, bssid,
MLAN_MAC_ADDR_LENGTH)) {
switch (mode) {
case MLAN_BSS_MODE_INFRA:
case MLAN_BSS_MODE_IBSS:
net = wlan_is_network_compatible(pmpriv, i, mode);
break;
default:
net = i;
break;
}
}
}
if (net >= 0) {
if (!wlan_find_cfp_by_band_and_channel(pmadapter,
(t_u8) pmadapter->
pscan_table[net].bss_band,
(t_u16) pmadapter->
pscan_table[net].channel)) {
net = -1;
}
}
LEAVE();
return net;
}
/**
* @brief Compare two SSIDs
*
* @param pmadapter A pointer to mlan_adapter structure
* @param ssid1 A pointer to ssid to compare
* @param ssid2 A pointer to ssid to compare
*
* @return 0--ssid is same, otherwise is different
*/
t_s32
wlan_ssid_cmp(IN pmlan_adapter pmadapter,
IN mlan_802_11_ssid * ssid1, IN mlan_802_11_ssid * ssid2)
{
ENTER();
if (!ssid1 || !ssid2) {
LEAVE();
return -1;
}
if (ssid1->ssid_len != ssid2->ssid_len) {
LEAVE();
return -1;
}
LEAVE();
return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len);
}
/**
* @brief This function inserts scan command node to scan_pending_q.
*
* @param pmpriv A pointer to mlan_private structure
* @param pcmd_node A pointer to cmd_ctrl_node structure
* @return n/a
*/
t_void
wlan_queue_scan_cmd(IN mlan_private * pmpriv, IN cmd_ctrl_node * pcmd_node)
{
mlan_adapter *pmadapter = pmpriv->adapter;
ENTER();
if (pcmd_node == MNULL)
goto done;
util_enqueue_list_tail(&pmadapter->scan_pending_q,
(pmlan_linked_list) pcmd_node,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock);
done:
LEAVE();
}
/**
* @brief Find the AP with specific ssid in the scan list
*
* @param pmpriv A pointer to mlan_private structure
* @param preq_ssid_bssid A pointer to AP's ssid returned
*
* @return MLAN_STATUS_SUCCESS--success, otherwise--fail
*/
mlan_status
wlan_find_best_network(IN mlan_private * pmpriv,
OUT mlan_ssid_bssid * preq_ssid_bssid)
{
mlan_adapter *pmadapter = pmpriv->adapter;
mlan_status ret = MLAN_STATUS_SUCCESS;
BSSDescriptor_t *preq_bss;
t_s32 i;
ENTER();
memset(preq_ssid_bssid, 0, sizeof(mlan_ssid_bssid));
i = wlan_find_best_network_in_list(pmpriv);
if (i >= 0) {
preq_bss = &pmadapter->pscan_table[i];
memcpy(&preq_ssid_bssid->ssid, &preq_bss->ssid,
sizeof(mlan_802_11_ssid));
memcpy((t_u8 *) & preq_ssid_bssid->bssid,
(t_u8 *) & preq_bss->mac_address, MLAN_MAC_ADDR_LENGTH);
/* Make sure we are in the right mode */
if (pmpriv->bss_mode == MLAN_BSS_MODE_AUTO)
pmpriv->bss_mode = preq_bss->bss_mode;
}
if (!preq_ssid_bssid->ssid.ssid_len) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
PRINTM(MINFO, "Best network found = [%s], "
"[%02x:%02x:%02x:%02x:%02x:%02x]\n",
preq_ssid_bssid->ssid.ssid,
preq_ssid_bssid->bssid[0], preq_ssid_bssid->bssid[1],
preq_ssid_bssid->bssid[2], preq_ssid_bssid->bssid[3],
preq_ssid_bssid->bssid[4], preq_ssid_bssid->bssid[5]);
done:
LEAVE();
return ret;
}
/**
* @brief Send a scan command for all available channels filtered on a spec
*
* @param pmpriv A pointer to mlan_private structure
* @param pioctl_buf A pointer to MLAN IOCTl Request buffer
* @param preq_ssid A pointer to AP's ssid returned
*
* @return MLAN_STATUS_SUCCESS--success, otherwise--fail
*/
mlan_status
wlan_scan_specific_ssid(IN mlan_private * pmpriv,
IN t_void * pioctl_buf, IN mlan_802_11_ssid * preq_ssid)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_callbacks *pcb = (mlan_callbacks *) & pmpriv->adapter->callbacks;
wlan_user_scan_cfg *pscan_cfg;
ENTER();
if (!preq_ssid) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
wlan_scan_delete_ssid_table_entry(pmpriv, preq_ssid);
ret = pcb->moal_malloc(sizeof(wlan_user_scan_cfg), (t_u8 **) & pscan_cfg);
if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg) {
PRINTM(MERROR, "Memory allocation for pscan_cfg failed!\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
memset(pscan_cfg, 0x00, sizeof(wlan_user_scan_cfg));
memcpy(pscan_cfg->ssid_list[0].ssid, preq_ssid->ssid, preq_ssid->ssid_len);
pscan_cfg->keep_previous_scan = MTRUE;
ret = wlan_scan_networks(pmpriv, pioctl_buf, pscan_cfg);
if (pscan_cfg)
pcb->moal_mfree((t_u8 *) pscan_cfg);
done:
LEAVE();
return ret;
}
/**
* @brief This function append the vendor specific IE TLV
*
* @param pmpriv A pointer to mlan_private structure
* @param vsie_mask VSIE bit mask
* @param ppbuffer A Pointer to command buffer pointer
*
* @return bytes added to the buffer
*/
int
wlan_cmd_append_vsie_tlv(IN mlan_private * pmpriv,
IN t_u16 vsie_mask, OUT t_u8 ** ppbuffer)
{
int id, ret_len = 0;
MrvlIETypes_VendorParamSet_t *pvs_param_set;
ENTER();
/* Null Checks */
if (ppbuffer == 0) {
LEAVE();
return 0;
}
if (*ppbuffer == 0) {
LEAVE();
return 0;
}
/**
* Traverse through the saved vendor specific IE array and append
* the selected(scan/assoc/adhoc) IE as TLV to the command
*/
for (id = 0; id < MLAN_MAX_VSIE_NUM; id++) {
if (pmpriv->vs_ie[id].mask & vsie_mask) {
pvs_param_set = (MrvlIETypes_VendorParamSet_t *) * ppbuffer;
pvs_param_set->header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH);
pvs_param_set->header.len =
(((t_u16) pmpriv->vs_ie[id].ie[1]) & 0x00FF) + 2;
memcpy(pvs_param_set->ie, pmpriv->vs_ie[id].ie,
pvs_param_set->header.len);
HEXDUMP("VENDOR_SPEC_IE", (t_u8 *) pvs_param_set,
sizeof(MrvlIEtypesHeader_t) + pvs_param_set->header.len);
*ppbuffer +=
pvs_param_set->header.len + sizeof(MrvlIEtypesHeader_t);
ret_len += pvs_param_set->header.len + sizeof(MrvlIEtypesHeader_t);
pvs_param_set->header.len =
wlan_cpu_to_le16(pvs_param_set->header.len);
}
}
LEAVE();
return ret_len;
}
/**
* @brief Save a beacon buffer of the current bss descriptor
* Save the current beacon buffer to restore in the following cases that
* makes the bcn_buf not to contain the current ssid's beacon buffer.
* - the current ssid was not found somehow in the last scan.
* - the current ssid was the last entry of the scan table and overloaded.
*
* @param pmpriv A pointer to mlan_private structure
*
* @return n/a
*/
t_void
wlan_save_curr_bcn(IN mlan_private * pmpriv)
{
mlan_adapter *pmadapter = pmpriv->adapter;
mlan_callbacks *pcb = (pmlan_callbacks) & pmadapter->callbacks;
BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor;
mlan_status ret = MLAN_STATUS_SUCCESS;
/* save the beacon buffer if it is not saved or updated */
if ((pmpriv->pcurr_bcn_buf == MNULL) ||
(pmpriv->curr_bcn_size != pcurr_bss->beacon_buf_size) ||
(memcmp(pmpriv->pcurr_bcn_buf, pcurr_bss->pbeacon_buf,
pcurr_bss->beacon_buf_size))) {
if (pmpriv->pcurr_bcn_buf) {
pcb->moal_mfree(pmpriv->pcurr_bcn_buf);
pmpriv->pcurr_bcn_buf = MNULL;
}
pmpriv->curr_bcn_size = pcurr_bss->beacon_buf_size;
ret = pcb->moal_malloc(pcurr_bss->beacon_buf_size,
&pmpriv->pcurr_bcn_buf);
if ((ret == MLAN_STATUS_SUCCESS) && pmpriv->pcurr_bcn_buf) {
memcpy(pmpriv->pcurr_bcn_buf, pcurr_bss->pbeacon_buf,
pcurr_bss->beacon_buf_size);
PRINTM(MINFO, "current beacon saved %d\n", pmpriv->curr_bcn_size);
}
}
}
/**
* @brief Restore a beacon buffer of the current bss descriptor
*
* @param pmpriv A pointer to mlan_private structure
*
* @return n/a
*/
t_void
wlan_restore_curr_bcn(IN mlan_private * pmpriv)
{
mlan_adapter *pmadapter = pmpriv->adapter;
mlan_callbacks *pcb = (pmlan_callbacks) & pmadapter->callbacks;
BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor;
if (pmpriv->pcurr_bcn_buf &&
((pmadapter->pbcn_buf_end + pmpriv->curr_bcn_size) <
(pmadapter->bcn_buf + sizeof(pmadapter->bcn_buf)))) {
pcb->moal_spin_lock(pmpriv->curr_bcn_buf_lock);
/* restore the current beacon buffer */
memcpy(pmadapter->pbcn_buf_end, pmpriv->pcurr_bcn_buf,
pmpriv->curr_bcn_size);
pcurr_bss->pbeacon_buf = pmadapter->pbcn_buf_end;
pcurr_bss->beacon_buf_size = pmpriv->curr_bcn_size;
pmadapter->pbcn_buf_end += pmpriv->curr_bcn_size;
/* adjust the pointers in the current bss descriptor */
if (pcurr_bss->pwpa_ie) {
pcurr_bss->pwpa_ie = (IEEEtypes_VendorSpecific_t *)
(pcurr_bss->pbeacon_buf + pcurr_bss->wpa_offset);
}
if (pcurr_bss->prsn_ie) {
pcurr_bss->prsn_ie = (IEEEtypes_Generic_t *)
(pcurr_bss->pbeacon_buf + pcurr_bss->rsn_offset);
}
if (pcurr_bss->pht_cap) {
pcurr_bss->pht_cap = (IEEEtypes_HTCap_t *)
(pcurr_bss->pbeacon_buf + pcurr_bss->ht_cap_offset);
}
if (pcurr_bss->pht_info) {
pcurr_bss->pht_info = (IEEEtypes_HTInfo_t *)
(pcurr_bss->pbeacon_buf + pcurr_bss->ht_info_offset);
}
if (pcurr_bss->pbss_co_2040) {
pcurr_bss->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *)
(pcurr_bss->pbeacon_buf + pcurr_bss->bss_co_2040_offset);
}
if (pcurr_bss->pext_cap) {
pcurr_bss->pext_cap = (IEEEtypes_ExtCap_t *)
(pcurr_bss->pbeacon_buf + pcurr_bss->ext_cap_offset);
}
if (pcurr_bss->poverlap_bss_scan_param) {
pcurr_bss->poverlap_bss_scan_param =
(IEEEtypes_OverlapBSSScanParam_t *)
(pcurr_bss->pbeacon_buf + pcurr_bss->overlap_bss_offset);
}
pcb->moal_spin_unlock(pmpriv->curr_bcn_buf_lock);
PRINTM(MINFO, "current beacon restored %d\n", pmpriv->curr_bcn_size);
} else {
PRINTM(MWARN, "curr_bcn_buf not saved or bcn_buf has no space\n");
}
}
/**
* @brief Free a beacon buffer of the current bss descriptor
*
* @param pmpriv A pointer to mlan_private structure
*
* @return n/a
*/
t_void
wlan_free_curr_bcn(IN mlan_private * pmpriv)
{
mlan_adapter *pmadapter = pmpriv->adapter;
mlan_callbacks *pcb = (pmlan_callbacks) & pmadapter->callbacks;
if (pmpriv->pcurr_bcn_buf) {
pcb->moal_mfree(pmpriv->pcurr_bcn_buf);
pmpriv->pcurr_bcn_buf = MNULL;
}
}