diff options
Diffstat (limited to 'drivers/net/wireless/sd8797/mlan/mlan_scan.c')
-rw-r--r-- | drivers/net/wireless/sd8797/mlan/mlan_scan.c | 4155 |
1 files changed, 4155 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlan/mlan_scan.c b/drivers/net/wireless/sd8797/mlan/mlan_scan.c new file mode 100644 index 000000000000..6123365422c7 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_scan.c @@ -0,0 +1,4155 @@ +/** @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-2011, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +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)) + +/** WPS TLV MAX size is MAX IE size plus 2 bytes for t_u16 MRVL TLV extension */ +#define WPS_TLV_MAX_SIZE (sizeof(IEEEtypes_VendorSpecific_t) + 2) +/** 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 \ + + WPS_TLV_MAX_SIZE) + +/******************************************************** + 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 +********************************************************/ +/** Cipher suite definition */ +enum cipher_suite +{ + CIPHER_SUITE_TKIP, + CIPHER_SUITE_CCMP, + CIPHER_SUITE_MAX +}; + +static t_u8 wpa_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x50, 0xf2, 0x02}, /* TKIP */ + {0x00, 0x50, 0xf2, 0x04}, /* AES */ +}; + +static t_u8 rsn_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x0f, 0xac, 0x02}, /* TKIP */ + {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(mlan_adapter * pmadapter, IEBody * ie_body, t_u8 * oui) +{ + t_u8 count; + + count = ie_body->PtkCnt[0]; + + ENTER(); + /* 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(pmadapter, ie_body->PtkBody, oui, sizeof(ie_body->PtkBody))) { + LEAVE(); + return MLAN_OUI_PRESENT; + } + + --count; + if (count) { + ie_body = (IEBody *) ((t_u8 *) ie_body + sizeof(ie_body->PtkBody)); + } + } + + PRINTM(MINFO, "The OUI %x:%x:%x:%x is not found in PTK \n", oui[0], oui[1], + oui[2], oui[3]); + LEAVE(); + 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_oui_present(mlan_adapter * pmadapter, BSSDescriptor_t * pbss_desc, + t_u32 cipher_suite) +{ + t_u8 *oui = MNULL; + IEBody *ie_body = MNULL; + t_u8 ret = MLAN_OUI_NOT_PRESENT; + + ENTER(); + 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[cipher_suite][0]; + if ((ret = search_oui_in_ie(pmadapter, ie_body, oui))) { + LEAVE(); + return ret; + } + } + LEAVE(); + 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_oui_present(mlan_adapter * pmadapter, BSSDescriptor_t * pbss_desc, + t_u32 cipher_suite) +{ + t_u8 *oui = MNULL; + IEBody *ie_body = MNULL; + t_u8 ret = MLAN_OUI_NOT_PRESENT; + + ENTER(); + 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[cipher_suite][0]; + if ((ret = search_oui_in_ie(pmadapter, ie_body, oui))) { + LEAVE(); + return ret; + } + } + LEAVE(); + return ret; +} + +/** + * @brief compare config band and a band from the scan result, + * which is defined by functiion radio_type_to_band(t_u8 radio_type) above + * + * @param cfg_band: band configured + * scan_band: band from scan result + * + * @return matched: non-zero. unmatched: 0 + * + */ +static t_u8 +wlan_is_band_compatible(t_u8 cfg_band, t_u8 scan_band) +{ + t_u8 band; + switch (scan_band) { + case BAND_A: + band = BAND_A | BAND_AN; + break; + case BAND_G: + default: + band = BAND_B | BAND_G | BAND_GN; + } + return cfg_band & band; +} + +/** + * @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 = 0; + t_u32 next_chan; + t_u8 scan_type; + t_u8 radio_type; + + ENTER(); + + for (region_idx = 0; + region_idx < NELEMENTS(pmadapter->region_channel); region_idx++) { + + if (wlan_11d_is_enabled(pmpriv) && 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]; + } + + if (puser_scan_in && !puser_scan_in->chan_list[0].chan_number && + puser_scan_in->chan_list[0].radio_type & BAND_SPECIFIED) { + radio_type = + puser_scan_in->chan_list[0].radio_type & ~BAND_SPECIFIED; + if (!radio_type && (pscan_region->band != BAND_B) && + (pscan_region->band != BAND_G)) + continue; + if (radio_type && (pscan_region->band != BAND_A)) + continue; + } + if (!wlan_is_band_compatible + (pmpriv->config_bands | pmadapter->adhoc_start_band, + pscan_region->band)) + continue; + for (next_chan = 0; + next_chan < pscan_region->num_cfp; next_chan++, chan_idx++) { + /* 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; + cfp = pscan_region->pcfp + next_chan; + + if (scan_type == MLAN_SCAN_TYPE_ACTIVE + && wlan_11d_is_enabled(pmpriv)) { + scan_type = wlan_11d_get_scan_type(pmadapter, + pscan_region->band, + (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_11d_is_enabled(pmpriv)) { + /* 11D not available... play it safe on DFS channels */ + if (wlan_11h_radar_detect_required + (pmpriv, (t_u8) cfp->channel)) + scan_type = MLAN_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 == MLAN_SCAN_TYPE_PASSIVE) { + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter->passive_scan_time); + } else if (filtered_scan) { + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter->specific_scan_time); + } else { + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter->active_scan_time); + } + + if (scan_type == MLAN_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].chan_scan_mode.disable_chan_filt = + MTRUE; + } + } + } + + LEAVE(); +} + +/** + * @brief Add WPS IE to probe request frame + * + * @param pmpriv A pointer to mlan_private structure + * @param pptlv_out A pointer to TLV to fill in + * + * @return N/A + */ +static void +wlan_add_wps_probe_request_ie(IN mlan_private * pmpriv, OUT t_u8 ** pptlv_out) +{ + MrvlIEtypesHeader_t *tlv; + + ENTER(); + + if (pmpriv->wps.wps_ie.vend_hdr.len) { + tlv = (MrvlIEtypesHeader_t *) * pptlv_out; + tlv->type = wlan_cpu_to_le16(VENDOR_SPECIFIC_221); + tlv->len = wlan_cpu_to_le16(pmpriv->wps.wps_ie.vend_hdr.len); + *pptlv_out += sizeof(MrvlIEtypesHeader_t); + memcpy(pmpriv->adapter, *pptlv_out, + pmpriv->wps.wps_ie.vend_hdr.oui, + pmpriv->wps.wps_ie.vend_hdr.len); + *pptlv_out += (pmpriv->wps.wps_ie.vend_hdr.len + + sizeof(MrvlIEtypesHeader_t)); + } + 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; + mlan_adapter *pmadapter = pmpriv->adapter; + ChanScanParamSet_t *ptmp_chan_list; + ChanScanParamSet_t *pstart_chan; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; + + t_u32 tlv_idx; + t_u32 total_scan_time; + t_u32 done_early; + t_u32 cmd_no; + + 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); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (!pscan_chan_list->chan_number) { + PRINTM(MERROR, "Scan: No channel configured\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + 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(pmadapter, 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); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + break; + } + + pchan_tlv_out->header.len = wlan_cpu_to_le16(pchan_tlv_out->header.len); + + pmadapter->pscan_channels = pstart_chan; + + /* Send the scan command to the firmware with the specified cfg */ + if (pmadapter->ext_scan) + cmd_no = HostCmd_CMD_802_11_SCAN_EXT; + else + cmd_no = HostCmd_CMD_802_11_SCAN; + ret = wlan_prepare_cmd(pmpriv, + cmd_no, + 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 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 config 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 MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +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; + mlan_status ret = MLAN_STATUS_SUCCESS; + 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) { + + ssid_filter = MFALSE; + + /* 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(pmadapter, 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); + + 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(pmadapter, pwildcard_ssid_tlv->ssid, + puser_scan_in->ssid_list[ssid_idx].ssid, + MIN(MLAN_MAX_SSID_LENGTH, 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); + + if (ssid_len) { + ssid_filter = MTRUE; + } + } + + /* + * 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(pmadapter, 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(pmadapter, rates, 0, sizeof(rates)); + + rates_size = wlan_get_supported_rates(pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == + MLAN_BSS_MODE_INFRA) ? pmadapter-> + config_bands : pmadapter-> + adhoc_start_band, 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(pmadapter, 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(pmadapter, 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_ht_cap_tlv(pmpriv, pht_cap, pmpriv->adapter->config_bands); + 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); + } + + wlan_add_wps_probe_request_ie(pmpriv, &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; + if (scan_type == MLAN_SCAN_TYPE_UNCHANGED) + scan_type = pmadapter->scan_type; + + if (radio_type == HostCmd_SCAN_RADIO_TYPE_A) { + if (pmadapter->fw_bands & BAND_A) + PRINTM(MINFO, "UserScan request for A Band channel %d!!\n", + channel); + else { + PRINTM(MERROR, "Scan in A band is not allowed!!\n"); + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + + } + } + + /* 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 = MLAN_SCAN_TYPE_PASSIVE; + } + } + if (scan_type == MLAN_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 == MLAN_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\n"); + } + + } 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(); + return ret; +} + +/** + * @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(pmadapter, &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; + + if (bytes_left_for_current_beacon < + (MLAN_MAC_ADDR_LENGTH + sizeof(t_u8) + sizeof(WLAN_802_11_FIXED_IEs))) { + PRINTM(MERROR, "InterpretIE: Not enough bytes left\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memcpy(pmadapter, 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; + + /* + * Next 4 fields are RSSI (for legacy scan only), time stamp, + * beacon interval, and capability information + */ + if (!pmadapter->ext_scan) { + /* 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(pmadapter, fixed_ie.time_stamp, pcurrent_ptr, 8); + memcpy(pmadapter, pbss_entry->time_stamp, pcurrent_ptr, 8); + pcurrent_ptr += 8; + bytes_left_for_current_beacon -= 8; + + /* Beacon interval is 2 bytes long */ + memcpy(pmadapter, &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(pmadapter, &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(pmadapter, &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; + continue; + } + + switch (element_id) { + + case SSID: + if (element_len > MRVDRV_MAX_SSID_LENGTH) { + bytes_left_for_current_beacon = 0; + continue; + } + pbss_entry->ssid.ssid_len = element_len; + memcpy(pmadapter, pbss_entry->ssid.ssid, (pcurrent_ptr + 2), + element_len); + PRINTM(MINFO, "InterpretIE: ssid: %-32s\n", pbss_entry->ssid.ssid); + break; + + case SUPPORTED_RATES: + if (element_len > WLAN_SUPPORTED_RATES) { + bytes_left_for_current_beacon = 0; + continue; + } + memcpy(pmadapter, pbss_entry->data_rates, pcurrent_ptr + 2, + element_len); + memcpy(pmadapter, 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(pmadapter, &pbss_entry->phy_param_set.fh_param_set, + pfh_param_set, MIN(total_ie_len, + sizeof(IEEEtypes_FhParamSet_t))); + pbss_entry->phy_param_set.fh_param_set.len = + MIN(element_len, (sizeof(IEEEtypes_FhParamSet_t) + - sizeof(IEEEtypes_Header_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(pmadapter, &pbss_entry->phy_param_set.ds_param_set, + pds_param_set, MIN(total_ie_len, + sizeof(IEEEtypes_DsParamSet_t))); + pbss_entry->phy_param_set.ds_param_set.len = + MIN(element_len, (sizeof(IEEEtypes_DsParamSet_t) + - sizeof(IEEEtypes_Header_t))); + break; + + case CF_PARAM_SET: + pcf_param_set = (IEEEtypes_CfParamSet_t *) pcurrent_ptr; + memcpy(pmadapter, &pbss_entry->ss_param_set.cf_param_set, + pcf_param_set, MIN(total_ie_len, + sizeof(IEEEtypes_CfParamSet_t))); + pbss_entry->ss_param_set.cf_param_set.len = + MIN(element_len, (sizeof(IEEEtypes_CfParamSet_t) + - sizeof(IEEEtypes_Header_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(pmadapter, &pbss_entry->ss_param_set.ibss_param_set, + pibss_param_set, MIN(total_ie_len, + sizeof(IEEEtypes_IbssParamSet_t))); + pbss_entry->ss_param_set.ibss_param_set.len = + MIN(element_len, (sizeof(IEEEtypes_IbssParamSet_t) + - sizeof(IEEEtypes_Header_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(pmadapter, &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(pmadapter, &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(pmadapter, prate, pcurrent_ptr + 2, bytes_to_copy); + + prate = (t_u8 *) pbss_entry->supported_rates; + prate += rate_size; + memcpy(pmadapter, 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 + (pmadapter, 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 + (pmadapter, 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(pmadapter, (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 Adjust ie's position in BSSDescriptor_t + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_entry A pointer to BSSDescriptor_t structure + * + * @return N/A + */ +static t_void +wlan_adjust_ie_in_bss_entry(IN mlan_private * pmpriv, + IN BSSDescriptor_t * pbss_entry) +{ + ENTER(); + if (pbss_entry->pbeacon_buf) { + if (pbss_entry->pwpa_ie) { + pbss_entry->pwpa_ie = (IEEEtypes_VendorSpecific_t *) + (pbss_entry->pbeacon_buf + pbss_entry->wpa_offset); + } + if (pbss_entry->prsn_ie) { + pbss_entry->prsn_ie = (IEEEtypes_Generic_t *) + (pbss_entry->pbeacon_buf + pbss_entry->rsn_offset); + } + if (pbss_entry->pwapi_ie) { + pbss_entry->pwapi_ie = (IEEEtypes_Generic_t *) + (pbss_entry->pbeacon_buf + pbss_entry->wapi_offset); + } + if (pbss_entry->pht_cap) { + pbss_entry->pht_cap = (IEEEtypes_HTCap_t *) + (pbss_entry->pbeacon_buf + pbss_entry->ht_cap_offset); + } + if (pbss_entry->pht_info) { + pbss_entry->pht_info = (IEEEtypes_HTInfo_t *) + (pbss_entry->pbeacon_buf + pbss_entry->ht_info_offset); + } + if (pbss_entry->pbss_co_2040) { + pbss_entry->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *) + (pbss_entry->pbeacon_buf + pbss_entry->bss_co_2040_offset); + } + if (pbss_entry->pext_cap) { + pbss_entry->pext_cap = (IEEEtypes_ExtCap_t *) + (pbss_entry->pbeacon_buf + pbss_entry->ext_cap_offset); + } + if (pbss_entry->poverlap_bss_scan_param) { + pbss_entry->poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t *) + (pbss_entry->pbeacon_buf + pbss_entry->overlap_bss_offset); + } + } else { + pbss_entry->pwpa_ie = MNULL; + pbss_entry->wpa_offset = 0; + pbss_entry->prsn_ie = MNULL; + pbss_entry->rsn_offset = 0; + pbss_entry->pwapi_ie = MNULL; + pbss_entry->wapi_offset = 0; + pbss_entry->pht_cap = MNULL; + pbss_entry->ht_cap_offset = 0; + pbss_entry->pht_info = MNULL; + pbss_entry->ht_info_offset = 0; + pbss_entry->pbss_co_2040 = MNULL; + pbss_entry->bss_co_2040_offset = 0; + pbss_entry->pext_cap = MNULL; + pbss_entry->ext_cap_offset = 0; + pbss_entry->poverlap_bss_scan_param = MNULL; + pbss_entry->overlap_bss_offset = 0; + } + LEAVE(); + return; +} + +/** + * @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; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *tmp_buf; + t_u16 bcn_size = 0; + t_u32 bcn_offset = 0; + + 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(pmadapter, 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(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, + new_bcn_size); + + /* + * If the old beacon size was less than the maximum + * we had allotted 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 allotted storage size. + * Shrink the allotted 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, + (pmadapter->bcn_buf_size - + (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(pmadapter, + (void *) ((t_ptr) pbcn_store + (t_ptr) old_bcn_size), + (void *) ((t_ptr) pbcn_store + (t_ptr) bcn_space), + (t_u32) ((t_ptr) pmadapter->pbcn_buf_end - + ((t_ptr) pbcn_store + (t_ptr) 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); + wlan_adjust_ie_in_bss_entry(pmpriv, + &pmadapter-> + pscan_table[adj_idx]); + } + } + } + } else if (pmadapter->pbcn_buf_end + (new_bcn_size - bcn_space) + < (pmadapter->bcn_buf + pmadapter->bcn_buf_size)) { + /* + * 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, + (pmadapter->bcn_buf_size - + (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(pmadapter, + (void *) ((t_ptr) pbcn_store + (t_ptr) new_bcn_size), + (void *) ((t_ptr) pbcn_store + (t_ptr) bcn_space), + (t_u32) ((t_ptr) pmadapter->pbcn_buf_end - + ((t_ptr) pbcn_store + (t_ptr) bcn_space))); + + /* Copy the new beacon buffer entry over the old one */ + memcpy(pmadapter, 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 allotted 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); + wlan_adjust_ie_in_bss_entry(pmpriv, + &pmadapter-> + pscan_table[adj_idx]); + } + } + } 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, + (pmadapter->bcn_buf_size - + (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; + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + } else { + if ((pmadapter->pbcn_buf_end + pnew_beacon->beacon_buf_size + + SCAN_BEACON_ENTRY_PAD > (pmadapter->bcn_buf + + pmadapter->bcn_buf_size)) && + (pmadapter->bcn_buf_size < MAX_SCAN_BEACON_BUFFER)) { + /* no space for this entry, realloc bcn buffer */ + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + pmadapter->bcn_buf_size + + DEFAULT_SCAN_BEACON_BUFFER, + MLAN_MEM_DEF, + (t_u8 **) & tmp_buf); + if ((ret == MLAN_STATUS_SUCCESS) && (tmp_buf)) { + PRINTM(MCMND, + "Realloc Beacon buffer, old size=%d, new_size=%d\n", + pmadapter->bcn_buf_size, + pmadapter->bcn_buf_size + DEFAULT_SCAN_BEACON_BUFFER); + bcn_size = pmadapter->pbcn_buf_end - pmadapter->bcn_buf; + memcpy(pmadapter, tmp_buf, pmadapter->bcn_buf, bcn_size); + /* Adjust beacon buffer pointers that are past the current */ + for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) { + bcn_offset = + pmadapter->pscan_table[adj_idx].pbeacon_buf - + pmadapter->bcn_buf; + pmadapter->pscan_table[adj_idx].pbeacon_buf = + tmp_buf + bcn_offset; + wlan_adjust_ie_in_bss_entry(pmpriv, + &pmadapter-> + pscan_table[adj_idx]); + } + pmadapter->pbcn_buf_end = tmp_buf + bcn_size; + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *) pmadapter->bcn_buf); + pmadapter->bcn_buf = tmp_buf; + pmadapter->bcn_buf_size += DEFAULT_SCAN_BEACON_BUFFER; + } + } + /* + * 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 + + pmadapter->bcn_buf_size)) { + + /* + * 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, 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); + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + + /* 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), + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - pmadapter->bcn_buf))); + } else { + /* + * No space for new beacon + */ + PRINTM(MCMND, "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, + (pmadapter->bcn_buf_size - + (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; + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + } + } + + LEAVE(); +} + +/** + * @brief Restore a beacon buffer of the current bss descriptor + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +static 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; + + ENTER(); + + if (pmpriv->pcurr_bcn_buf && + ((pmadapter->pbcn_buf_end + pmpriv->curr_bcn_size) < + (pmadapter->bcn_buf + pmadapter->bcn_buf_size))) { + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmpriv->curr_bcn_buf_lock); + + /* restore the current beacon buffer */ + memcpy(pmadapter, 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(pmadapter->pmoal_handle, + 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"); + } + + 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(pmadapter->pmoal_handle, + 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(pmadapter, &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(pmadapter->pmoal_handle, + 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(pmadapter, pbeacon_buf, + (void *) ((t_ptr) pbeacon_buf + (t_ptr) beacon_buf_adj), + (t_u32) ((t_ptr) pmadapter->pbcn_buf_end - + (t_ptr) 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, 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, 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 entries 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 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 (disable HT if no AES) + * 0 0 1 0 x 1x x 1 yes WPA2 (disable HT if no AES) + * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES + * 1 0 0 0 NONE 1 0 0 yes Static WEP (disable HT) + * 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 + */ +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]; + pbss_desc->disable_11n = MFALSE; + + /* 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->wlan_11h_bss_info.chan_switch_ann.element_id == + CHANNEL_SWITCH_ANN) { + PRINTM(MINFO, "Don't connect to AP with CHANNEL_SWITCH_ANN IE.\n"); + LEAVE(); + return -1; + } + + if (pmpriv->wps.session_enable == MTRUE) { + PRINTM(MINFO, "Return success directly in WPS period\n"); + 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) + && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP) + && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP)) { + + if (is_wpa_oui_present + (pmpriv->adapter, pbss_desc, CIPHER_SUITE_TKIP) + || is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_TKIP)) { + PRINTM(MINFO, + "Disable 11n if AES is not supported by AP\n"); + pbss_desc->disable_11n = MTRUE; + } else { + LEAVE(); + return -1; + } + } + LEAVE(); + return index; + } else { + PRINTM(MINFO, "ewpa_enabled: Ignore none WPA/WPA2 AP\n"); + LEAVE(); + return -1; + } + } + + 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 */ + PRINTM(MINFO, "Disable 11n in WEP mode\n"); + pbss_desc->disable_11n = MTRUE; + 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 " + "rsn_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) + && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP)) { + if (is_wpa_oui_present + (pmpriv->adapter, pbss_desc, CIPHER_SUITE_TKIP)) { + PRINTM(MINFO, + "Disable 11n if AES is not supported by AP\n"); + pbss_desc->disable_11n = MTRUE; + } else { + 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 " + "rsn_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) + && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP)) { + if (is_rsn_oui_present + (pmpriv->adapter, pbss_desc, CIPHER_SUITE_TKIP)) { + PRINTM(MINFO, + "Disable 11n if AES is not supported by AP\n"); + pbss_desc->disable_11n = MTRUE; + } else { + 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 rsn_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 " + "rsn_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 Internal function used to flush the scan list + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_flush_scan_table(IN pmlan_adapter pmadapter) +{ + ENTER(); + + PRINTM(MINFO, "Flushing scan table\n"); + + memset(pmadapter, pmadapter->pscan_table, 0, + (sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST)); + pmadapter->num_in_scan_table = 0; + + memset(pmadapter, pmadapter->bcn_buf, 0, pmadapter->bcn_buf_size); + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; + + 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(pmadapter->pmoal_handle, + sizeof(wlan_scan_cmd_config_tlv), MLAN_MEM_DEF, + (t_u8 **) & pscan_cfg_out); + if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg_out) { + PRINTM(MERROR, "Memory allocation for pscan_cfg_out failed!\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + buf_size = sizeof(ChanScanParamSet_t) * WLAN_USER_SCAN_CHAN_MAX; + ret = + pcb->moal_malloc(pmadapter->pmoal_handle, buf_size, MLAN_MEM_DEF, + (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(pmadapter->pmoal_handle, (t_u8 *) pscan_cfg_out); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmadapter, pscan_chan_list, 0x00, buf_size); + memset(pmadapter, pscan_cfg_out, 0x00, sizeof(wlan_scan_cmd_config_tlv)); + + keep_previous_scan = MFALSE; + + ret = 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 (ret != MLAN_STATUS_SUCCESS) { + + PRINTM(MERROR, "Failed to setup scan config\n"); + if (pscan_cfg_out) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *) pscan_cfg_out); + if (pscan_chan_list) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *) pscan_chan_list); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (puser_scan_in) { + keep_previous_scan = puser_scan_in->keep_previous_scan; + } + + if (keep_previous_scan == MFALSE) { + memset(pmadapter, 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->pmoal_handle, &pmadapter->scan_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock)) { + pcmd_node = + (cmd_ctrl_node *) util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + pcb->moal_spin_lock, + pcb->moal_spin_unlock); + pmadapter->pext_scan_ioctl_req = pioctl_req; + 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(pmadapter->pmoal_handle, (t_u8 *) pscan_cfg_out); + + if (pscan_chan_list) + pcb->moal_mfree(pmadapter->pmoal_handle, (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 + */ +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(pmpriv->adapter, pscan_cmd->bssid, pscan_cfg->specific_bssid, + sizeof(pscan_cmd->bssid)); + memcpy(pmpriv->adapter, 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 = MNULL; + 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 = MNULL; + ChanBandParamSet_t *pchan_band; + t_u8 band; + t_u8 is_bgscan_resp; + t_u32 age_ts_usec; + + 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); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + 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(pmadapter->pmoal_handle, sizeof(BSSDescriptor_t), + MLAN_MEM_DEF, (t_u8 **) & bss_new_entry); + + if (ret != MLAN_STATUS_SUCCESS || !bss_new_entry) { + PRINTM(MERROR, "Memory allocation for bss_new_entry failed!\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + 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(pmadapter, 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) { + 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(pmadapter, 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(pmadapter, 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 (bss_new_entry->pbeacon_buf == MNULL) { + PRINTM(MCMND, "No space for beacon, drop this entry\n"); + num_in_table--; + continue; + } + /* + * 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(pmpriv->adapter, &tsf_val, + &ptsf_tlv->tsf_data[idx * TSF_DATA_SIZE], + sizeof(tsf_val)); + tsf_val = wlan_le64_to_cpu(tsf_val); + memcpy(pmpriv->adapter, &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, &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; + /* Update the age_in_second */ + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->age_in_secs, + &age_ts_usec); + + if (!util_peek_list + (pmadapter->pmoal_handle, &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 { + /* If firmware not ready, do not issue any more scan commands */ + if (pmadapter->hw_status != WlanHardwareStatusReady) { + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + /* Indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_ERROR_FW_NOT_READY; + + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + (pmlan_ioctl_req) pioctl_buf, + MLAN_STATUS_FAILURE); + } + } else { + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + pcmd_node = + (cmd_ctrl_node *) util_dequeue_list(pmadapter->pmoal_handle, + &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(pmadapter->pmoal_handle, (t_u8 *) bss_new_entry); + + LEAVE(); + return ret; +} + +/** + * @brief Prepare an extended 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_EXT command + * struct to send 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_802_11_SCAN_EXT 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 + */ +mlan_status +wlan_cmd_802_11_scan_ext(IN mlan_private * pmpriv, + IN HostCmd_DS_COMMAND * pcmd, IN t_void * pdata_buf) +{ + HostCmd_DS_802_11_SCAN_EXT *pext_scan_cmd = &pcmd->params.ext_scan; + wlan_scan_cmd_config *pscan_cfg = MNULL; + + ENTER(); + + pscan_cfg = (wlan_scan_cmd_config *) pdata_buf; + + memcpy(pmpriv->adapter, pext_scan_cmd->tlv_buffer, + pscan_cfg->tlv_buf, pscan_cfg->tlv_buf_len); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + pcmd->size = wlan_cpu_to_le16((t_u16) (sizeof(pext_scan_cmd->reserved) + + pscan_cfg->tlv_buf_len + + S_DS_GEN)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of extended scan + * + * @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_ext(IN mlan_private * pmpriv, + IN HostCmd_DS_COMMAND * resp, IN t_void * pioctl_buf) +{ + ENTER(); + + PRINTM(MINFO, "EXT scan returns successfully\n"); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function parse and store the extended scan results + * + * @param pmpriv A pointer to mlan_private structure + * @param number_of_sets Number of BSS + * @param pscan_resp A pointer to scan response buffer + * @param scan_resp_size Size of scan response buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_parse_ext_scan_result(IN mlan_private * pmpriv, + IN t_u8 number_of_sets, + IN t_u8 * pscan_resp, IN t_u16 scan_resp_size) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = MNULL; + BSSDescriptor_t *bss_new_entry = MNULL; + t_u8 *pbss_info; + t_u32 bytes_left; + t_u32 bytes_left_for_tlv; + t_u32 num_in_table; + t_u32 bss_idx; + t_u32 idx; + t_u64 tsf_val; + chan_freq_power_t *cfp; + t_u16 tlv_type, tlv_len; + MrvlIEtypes_Data_t *ptlv = MNULL; + MrvlIEtypes_Bss_Scan_Rsp_t *pscan_rsp_tlv = MNULL; + MrvlIEtypes_Bss_Scan_Info_t *pscan_info_tlv = MNULL; + t_u8 band; + t_u32 age_ts_usec; + + ENTER(); + pcb = (pmlan_callbacks) & pmadapter->callbacks; + + if (number_of_sets > MRVDRV_MAX_BSSID_LIST) { + PRINTM(MERROR, "EXT_SCAN: Invalid number of AP returned (%d)!!\n", + number_of_sets); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bytes_left = scan_resp_size; + PRINTM(MINFO, "EXT_SCAN: bss_descript_size %d\n", scan_resp_size); + PRINTM(MINFO, "EXT_SCAN: returned %d APs before parsing\n", number_of_sets); + + num_in_table = pmadapter->num_in_scan_table; + ptlv = (MrvlIEtypes_Data_t *) pscan_resp; + + /* + * Process each scan response returned 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(pmadapter->pmoal_handle, sizeof(BSSDescriptor_t), + MLAN_MEM_DEF, (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 < number_of_sets && bytes_left > + sizeof(MrvlIEtypesHeader_t); idx++) { + tlv_type = wlan_le16_to_cpu(ptlv->header.type); + tlv_len = wlan_le16_to_cpu(ptlv->header.len); + if (bytes_left < sizeof(MrvlIEtypesHeader_t) + tlv_len) { + PRINTM(MERROR, "EXT_SCAN: Error bytes left < TLV length\n"); + break; + } + pscan_rsp_tlv = MNULL; + pscan_info_tlv = MNULL; + bytes_left_for_tlv = bytes_left; + /* BSS response TLV with beacon or probe response buffer at the initial + position of each descriptor */ + if (tlv_type == TLV_TYPE_BSS_SCAN_RSP) { + pbss_info = (t_u8 *) ptlv; + pscan_rsp_tlv = (MrvlIEtypes_Bss_Scan_Rsp_t *) ptlv; + ptlv = (MrvlIEtypes_Data_t *) (ptlv->data + tlv_len); + bytes_left_for_tlv -= (tlv_len + sizeof(MrvlIEtypesHeader_t)); + } else + break; + + /* Process variable TLV */ + while (bytes_left_for_tlv >= sizeof(MrvlIEtypesHeader_t) && + wlan_le16_to_cpu(ptlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { + tlv_type = wlan_le16_to_cpu(ptlv->header.type); + tlv_len = wlan_le16_to_cpu(ptlv->header.len); + if (bytes_left_for_tlv < sizeof(MrvlIEtypesHeader_t) + tlv_len) { + PRINTM(MERROR, "EXT_SCAN: Error in processing TLV, " + "bytes left < TLV length\n"); + pscan_rsp_tlv = MNULL; + bytes_left_for_tlv = 0; + continue; + } + switch (tlv_type) { + case TLV_TYPE_BSS_SCAN_INFO: + pscan_info_tlv = (MrvlIEtypes_Bss_Scan_Info_t *) ptlv; + if (tlv_len != sizeof(MrvlIEtypes_Bss_Scan_Info_t) - + sizeof(MrvlIEtypesHeader_t)) { + bytes_left_for_tlv = 0; + continue; + } + break; + default: + break; + } + ptlv = (MrvlIEtypes_Data_t *) (ptlv->data + tlv_len); + bytes_left -= (tlv_len + sizeof(MrvlIEtypesHeader_t)); + bytes_left_for_tlv -= (tlv_len + sizeof(MrvlIEtypesHeader_t)); + } + /* No BSS response TLV */ + if (pscan_rsp_tlv == MNULL) + break; + + /* Advance pointer to the beacon buffer length and update the bytes + count so that the function wlan_interpret_bss_desc_with_ie() can + handle the scan buffer withut any change */ + pbss_info += sizeof(t_u16); + bytes_left -= sizeof(t_u16); + + /* Zero out the bss_new_entry we are about to store info in */ + memset(pmadapter, 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) { + PRINTM(MINFO, "EXT_SCAN: 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(pmadapter, 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(pmadapter, bss_new_entry->ssid.ssid, + pmadapter->pscan_table[bss_idx].ssid.ssid, + bss_new_entry->ssid.ssid_len))) { + PRINTM(MINFO, "EXT_SCAN: 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++; + } + } + + band = BAND_G; + /* + * If the BSS info 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 (pscan_info_tlv) { + /* RSSI is 2 byte long */ + bss_new_entry->rssi = + -(t_s32) (wlan_le16_to_cpu(pscan_info_tlv->rssi)); + PRINTM(MINFO, "EXT_SCAN: RSSI=%d\n", bss_new_entry->rssi); + memcpy(pmpriv->adapter, &tsf_val, &pscan_info_tlv->tsf, + sizeof(tsf_val)); + tsf_val = wlan_le64_to_cpu(tsf_val); + memcpy(pmpriv->adapter, &bss_new_entry->network_tsf, + &tsf_val, sizeof(bss_new_entry->network_tsf)); + band = radio_type_to_band(pscan_info_tlv->band); + } + /* + * 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 (bss_new_entry->pbeacon_buf == MNULL) { + PRINTM(MCMND, "No space for beacon, drop this entry\n"); + num_in_table--; + continue; + } + /* 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, &pmadapter->pscan_table[bss_idx], + bss_new_entry, sizeof(pmadapter->pscan_table[bss_idx])); + } else { + /* Error parsing/interpreting the scan response, skipped */ + PRINTM(MERROR, + "EXT_SCAN: wlan_interpret_bss_desc_with_ie returned error\n"); + } + } + + PRINTM(MINFO, "EXT_SCAN: Scanned %2d APs, %d valid, %d total\n", + 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; + /* Update the age_in_second */ + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->age_in_secs, + &age_ts_usec); + + done: + if (bss_new_entry) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *) bss_new_entry); + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the event extended scan report + * + * @param pmpriv A pointer to mlan_private structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_handle_event_ext_scan_report(IN mlan_private * pmpriv, + IN mlan_buffer * pmbuf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = &pmadapter->callbacks; + mlan_ioctl_req *pioctl_req = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + mlan_event_scan_result *pevent_scan = (pmlan_event_scan_result) + (pmbuf->pbuf + pmbuf->data_offset); + t_u8 *ptlv = (pmbuf->pbuf + pmbuf->data_offset + + sizeof(mlan_event_scan_result)); + t_u16 tlv_buf_left = wlan_cpu_to_le16(pevent_scan->buf_size); + + DBG_HEXDUMP(MCMD_D, "EVENT EXT_SCAN", pmbuf->pbuf + + pmbuf->data_offset, pmbuf->data_len); + wlan_parse_ext_scan_result(pmpriv, pevent_scan->num_of_set, + ptlv, tlv_buf_left); + if (!pevent_scan->more_event) { + pioctl_req = pmadapter->pext_scan_ioctl_req; + if (!util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, pcb->moal_spin_lock, + pcb->moal_spin_unlock)) { + pmadapter->pext_scan_ioctl_req = MNULL; + 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_req, + MLAN_STATUS_SUCCESS); + } + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + } else { + /* If firmware not ready, do not issue any more scan commands */ + if (pmadapter->hw_status != WlanHardwareStatusReady) { + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + /* Indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_ERROR_FW_NOT_READY; + + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + (pmlan_ioctl_req) pioctl_req, + MLAN_STATUS_FAILURE); + } + } else { + /* Get scan command from scan_pending_q and put to + cmd_pending_q */ + pcmd_node = + (cmd_ctrl_node *) util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter-> + scan_pending_q, + pcb->moal_spin_lock, + pcb->moal_spin_unlock); + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); + } + } + } + + 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 + */ +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 = MTRUE; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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 pbg_scan_in pointer to scan configuration parameters + * @param tlv_chan_list A pointer to structure MrvlIEtypes_ChanListParamSet_t + * + * @return channel number + */ +static t_u8 +wlan_bgscan_create_channel_list(IN mlan_private * pmpriv, + IN const wlan_bgscan_cfg * pbg_scan_in, + MrvlIEtypes_ChanListParamSet_t * tlv_chan_list) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + region_chan_t *pscan_region; + chan_freq_power_t *cfp; + t_u32 region_idx; + t_u32 chan_idx = 0; + t_u32 next_chan; + t_u8 scan_type; + t_u8 radio_type; + + ENTER(); + + for (region_idx = 0; + region_idx < NELEMENTS(pmadapter->region_channel); region_idx++) { + + if (wlan_11d_is_enabled(pmpriv) && 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]; + } + + if (pbg_scan_in && !pbg_scan_in->chan_list[0].chan_number && + pbg_scan_in->chan_list[0].radio_type & BAND_SPECIFIED) { + radio_type = pbg_scan_in->chan_list[0].radio_type & ~BAND_SPECIFIED; + if (!radio_type && (pscan_region->band != BAND_B) && + (pscan_region->band != BAND_G)) + continue; + if (radio_type && (pscan_region->band != BAND_A)) + continue; + } + if (!wlan_is_band_compatible + (pmpriv->config_bands | pmadapter->adhoc_start_band, + pscan_region->band)) + continue; + for (next_chan = 0; + next_chan < pscan_region->num_cfp; next_chan++, chan_idx++) { + if (chan_idx >= WLAN_BG_SCAN_CHAN_MAX) + break; + /* Set the default scan type to ACTIVE SCAN type, will later be + changed to passive on a per channel basis if restricted by + regulatory requirements (11d or 11h) */ + scan_type = MLAN_SCAN_TYPE_ACTIVE; + cfp = pscan_region->pcfp + next_chan; + if (scan_type == MLAN_SCAN_TYPE_ACTIVE + && wlan_11d_is_enabled(pmpriv)) { + scan_type = wlan_11d_get_scan_type(pmadapter, + pscan_region->band, + (t_u8) cfp->channel, + &pmadapter-> + parsed_region_chan); + } + switch (pscan_region->band) { + case BAND_A: + tlv_chan_list->chan_scan_param[chan_idx].radio_type = + HostCmd_SCAN_RADIO_TYPE_A; + if (!wlan_11d_is_enabled(pmpriv)) { + /* 11D not available... play it safe on DFS channels */ + if (wlan_11h_radar_detect_required + (pmpriv, (t_u8) cfp->channel)) + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + break; + case BAND_B: + case BAND_G: + default: + tlv_chan_list->chan_scan_param[chan_idx].radio_type = + HostCmd_SCAN_RADIO_TYPE_BG; + break; + } + + if (pbg_scan_in && pbg_scan_in->chan_list[0].scan_time) { + tlv_chan_list->chan_scan_param[chan_idx].max_scan_time = + wlan_cpu_to_le16((t_u16) pbg_scan_in->chan_list[0]. + scan_time); + tlv_chan_list->chan_scan_param[chan_idx].min_scan_time = + wlan_cpu_to_le16((t_u16) pbg_scan_in->chan_list[0]. + scan_time); + } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter->passive_scan_time); + tlv_chan_list->chan_scan_param[chan_idx].min_scan_time = + wlan_cpu_to_le16(pmadapter->passive_scan_time); + } else { + tlv_chan_list->chan_scan_param[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter->specific_scan_time); + tlv_chan_list->chan_scan_param[chan_idx].min_scan_time = + wlan_cpu_to_le16(pmadapter->specific_scan_time); + } + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_idx].chan_scan_mode. + passive_scan = MTRUE; + } else { + tlv_chan_list->chan_scan_param[chan_idx].chan_scan_mode. + passive_scan = MFALSE; + } + + tlv_chan_list->chan_scan_param[chan_idx].chan_number = + (t_u8) cfp->channel; + tlv_chan_list->chan_scan_param[chan_idx].chan_scan_mode. + disable_chan_filt = MTRUE; + } + } + + LEAVE(); + return chan_idx; +} + +/** + * @brief This function prepares command of bg_scan_config + * + * @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 + */ +mlan_status +wlan_cmd_bgscan_config(IN mlan_private * pmpriv, + IN HostCmd_DS_COMMAND * pcmd, IN t_void * pdata_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = &pcmd->params.bg_scan_config; + wlan_bgscan_cfg *bg_scan_in = (wlan_bgscan_cfg *) pdata_buf; + t_u16 cmd_size = 0; + MrvlIEtypes_NumProbes_t *pnum_probes_tlv = MNULL; + MrvlIEtypes_BeaconLowRssiThreshold_t *rssi_tlv = MNULL; + MrvlIEtypes_BeaconLowSnrThreshold_t *snr_tlv = MNULL; + MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv = MNULL; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + t_u8 *tlv = MNULL; + t_u16 num_probes = 0; + t_u32 ssid_idx; + t_u32 ssid_len = 0; + t_u32 chan_idx; + t_u32 chan_num; + t_u8 radio_type; + t_u16 scan_dur; + t_u8 scan_type; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG); + bg_scan->action = wlan_cpu_to_le16(bg_scan_in->action); + bg_scan->enable = bg_scan_in->enable; + bg_scan->bss_type = bg_scan_in->bss_type; + cmd_size = sizeof(HostCmd_DS_802_11_BG_SCAN_CONFIG) + S_DS_GEN; + if (bg_scan_in->chan_per_scan) + bg_scan->chan_per_scan = bg_scan_in->chan_per_scan; + else + bg_scan->chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + if (bg_scan_in->scan_interval) + bg_scan->scan_interval = wlan_cpu_to_le32(bg_scan_in->scan_interval); + else + bg_scan->scan_interval = wlan_cpu_to_le32(DEFAULT_BGSCAN_INTERVAL); + bg_scan->report_condition = wlan_cpu_to_le32(bg_scan_in->report_condition); + + if ((bg_scan_in->action == BG_SCAN_ACT_GET) || + (bg_scan_in->action == BG_SCAN_ACT_GET_PPS_UAPSD) || (!bg_scan->enable)) + goto done; + + tlv = (t_u8 *) bg_scan + sizeof(HostCmd_DS_802_11_BG_SCAN_CONFIG); + num_probes = (bg_scan_in->num_probes ? bg_scan_in->num_probes : + pmadapter->scan_probes); + if (num_probes) { + pnum_probes_tlv = (MrvlIEtypes_NumProbes_t *) tlv; + pnum_probes_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_NUMPROBES); + pnum_probes_tlv->header.len = + wlan_cpu_to_le16(sizeof(pnum_probes_tlv->num_probes)); + pnum_probes_tlv->num_probes = wlan_cpu_to_le16((t_u16) num_probes); + tlv += sizeof(MrvlIEtypes_NumProbes_t); + cmd_size += sizeof(MrvlIEtypes_NumProbes_t); + } + if (bg_scan_in->rssi_threshold) { + rssi_tlv = (MrvlIEtypes_BeaconLowRssiThreshold_t *) tlv; + rssi_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + rssi_tlv->value = bg_scan_in->rssi_threshold; + rssi_tlv->frequency = 0; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + if (bg_scan_in->snr_threshold) { + snr_tlv = (MrvlIEtypes_BeaconLowSnrThreshold_t *) tlv; + snr_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_BeaconLowSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + snr_tlv->value = bg_scan_in->snr_threshold; + snr_tlv->frequency = 0; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + for (ssid_idx = 0; ((ssid_idx < NELEMENTS(bg_scan_in->ssid_list)) + && (*bg_scan_in->ssid_list[ssid_idx].ssid || + bg_scan_in->ssid_list[ssid_idx].max_len)); + ssid_idx++) { + ssid_len = wlan_strlen((t_s8 *) bg_scan_in->ssid_list[ssid_idx].ssid); + pwildcard_ssid_tlv = (MrvlIEtypes_WildCardSsIdParamSet_t *) tlv; + 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 = + bg_scan_in->ssid_list[ssid_idx].max_len; + memcpy(pmadapter, pwildcard_ssid_tlv->ssid, + bg_scan_in->ssid_list[ssid_idx].ssid, MIN(MLAN_MAX_SSID_LENGTH, + ssid_len)); + tlv += + sizeof(pwildcard_ssid_tlv->header) + pwildcard_ssid_tlv->header.len; + cmd_size += + 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); + } + if (bg_scan_in->chan_list[0].chan_number) { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *) tlv; + PRINTM(MINFO, "Scan: Using supplied channel list\n"); + chan_num = 0; + for (chan_idx = 0; chan_idx < WLAN_BG_SCAN_CHAN_MAX + && bg_scan_in->chan_list[chan_idx].chan_number; chan_idx++) { + radio_type = bg_scan_in->chan_list[chan_idx].radio_type; + if (!wlan_is_band_compatible + (pmpriv->config_bands | pmadapter->adhoc_start_band, + radio_type_to_band(radio_type))) + continue; + scan_type = bg_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, bg_scan_in->chan_list[chan_idx].chan_number)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + } + tlv_chan_list->chan_scan_param[chan_num].chan_number = + bg_scan_in->chan_list[chan_idx].chan_number; + tlv_chan_list->chan_scan_param[chan_num].radio_type = + bg_scan_in->chan_list[chan_idx].radio_type; + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_num].chan_scan_mode. + passive_scan = MTRUE; + } else { + tlv_chan_list->chan_scan_param[chan_num].chan_scan_mode. + passive_scan = MFALSE; + } + if (bg_scan_in->chan_list[chan_idx].scan_time) { + scan_dur = (t_u16) bg_scan_in->chan_list[chan_idx].scan_time; + } else { + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + scan_dur = pmadapter->passive_scan_time; + } else { + scan_dur = pmadapter->specific_scan_time; + } + } + tlv_chan_list->chan_scan_param[chan_num].min_scan_time = + wlan_cpu_to_le16(scan_dur); + tlv_chan_list->chan_scan_param[chan_num].max_scan_time = + wlan_cpu_to_le16(scan_dur); + chan_num++; + } + tlv_chan_list->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); + tlv += + sizeof(MrvlIEtypesHeader_t) + sizeof(ChanScanParamSet_t) * chan_num; + cmd_size += + sizeof(MrvlIEtypesHeader_t) + sizeof(ChanScanParamSet_t) * chan_num; + } else { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *) tlv; + chan_num = + wlan_bgscan_create_channel_list(pmpriv, bg_scan_in, tlv_chan_list); + tlv_chan_list->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); + tlv += + sizeof(MrvlIEtypesHeader_t) + sizeof(ChanScanParamSet_t) * chan_num; + cmd_size += + sizeof(MrvlIEtypesHeader_t) + sizeof(ChanScanParamSet_t) * chan_num; + } + done: + pcmd->size = wlan_cpu_to_le16(cmd_size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of extended scan + * + * @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_bgscan_config(IN mlan_private * pmpriv, + IN HostCmd_DS_COMMAND * resp, + IN mlan_ioctl_req * pioctl_buf) +{ + mlan_ds_scan *pscan = MNULL; + HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = &resp->params.bg_scan_config; + wlan_bgscan_cfg *bg_scan_out = MNULL; + + ENTER(); + if (pioctl_buf) { + pscan = (mlan_ds_scan *) pioctl_buf->pbuf; + bg_scan_out = (wlan_bgscan_cfg *) pscan->param.user_scan.scan_cfg_buf; + bg_scan_out->action = wlan_le16_to_cpu(bg_scan->action); + if ((bg_scan_out->action == BG_SCAN_ACT_GET) && + (bg_scan_out->action == BG_SCAN_ACT_GET_PPS_UAPSD)) { + bg_scan_out->enable = bg_scan->enable; + bg_scan_out->bss_type = bg_scan->bss_type; + bg_scan_out->chan_per_scan = bg_scan->chan_per_scan; + bg_scan_out->scan_interval = + wlan_le32_to_cpu(bg_scan->scan_interval); + bg_scan_out->report_condition = + wlan_le32_to_cpu(bg_scan->report_condition); + pioctl_buf->data_read_written = + sizeof(mlan_ds_scan) + MLAN_SUB_COMMAND_SIZE; + } + } + 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 scan 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, pmadapter->pscan_table[i].mac_address, bssid, + MLAN_MAC_ADDR_LENGTH))) { + + if (((mode == MLAN_BSS_MODE_INFRA) && + !wlan_is_band_compatible(pmadapter->config_bands, + pmadapter->pscan_table[i].bss_band)) + || + (wlan_find_cfp_by_band_and_channel + (pmadapter, (t_u8) pmadapter->pscan_table[i].bss_band, + (t_u16) pmadapter->pscan_table[i].channel) == MNULL)) { + continue; + } + + 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; + } + } + } + + 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, pmadapter->pscan_table[i].mac_address, bssid, + MLAN_MAC_ADDR_LENGTH)) { + if (((mode == MLAN_BSS_MODE_INFRA) && + !wlan_is_band_compatible(pmadapter->config_bands, + pmadapter->pscan_table[i].bss_band)) + || + (wlan_find_cfp_by_band_and_channel + (pmadapter, (t_u8) pmadapter->pscan_table[i].bss_band, + (t_u16) pmadapter->pscan_table[i].channel) == MNULL)) { + continue; + } + 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; + } + } + } + + 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(pmadapter, 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->pmoal_handle, &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(pmadapter, 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(pmadapter, &preq_ssid_bssid->ssid, &preq_bss->ssid, + sizeof(mlan_802_11_ssid)); + memcpy(pmadapter, (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; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; + + ENTER(); + + if (!preq_ssid) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + wlan_scan_delete_ssid_table_entry(pmpriv, preq_ssid); + + ret = + pcb->moal_malloc(pmpriv->adapter->pmoal_handle, + sizeof(wlan_user_scan_cfg), MLAN_MEM_DEF, + (t_u8 **) & pscan_cfg); + + if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg) { + PRINTM(MERROR, "Memory allocation for pscan_cfg failed!\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(pmpriv->adapter, pscan_cfg, 0x00, sizeof(wlan_user_scan_cfg)); + + memcpy(pmpriv->adapter, 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(pmpriv->adapter->pmoal_handle, (t_u8 *) pscan_cfg); + + done: + LEAVE(); + return ret; +} + +/** + * @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; + + ENTER(); + /* 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->adapter, pmpriv->pcurr_bcn_buf, pcurr_bss->pbeacon_buf, + pcurr_bss->beacon_buf_size))) { + + if (pmpriv->pcurr_bcn_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, pmpriv->pcurr_bcn_buf); + pmpriv->pcurr_bcn_buf = MNULL; + } + pmpriv->curr_bcn_size = pcurr_bss->beacon_buf_size; + + if (pmpriv->curr_bcn_size) { + ret = + pcb->moal_malloc(pmadapter->pmoal_handle, + pcurr_bss->beacon_buf_size, MLAN_MEM_DEF, + &pmpriv->pcurr_bcn_buf); + + if ((ret == MLAN_STATUS_SUCCESS) && pmpriv->pcurr_bcn_buf) { + memcpy(pmpriv->adapter, pmpriv->pcurr_bcn_buf, + pcurr_bss->pbeacon_buf, pcurr_bss->beacon_buf_size); + PRINTM(MINFO, "current beacon saved %d\n", + pmpriv->curr_bcn_size); + } + } + } + LEAVE(); +} + +/** + * @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; + + ENTER(); + if (pmpriv->pcurr_bcn_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, pmpriv->pcurr_bcn_buf); + pmpriv->pcurr_bcn_buf = MNULL; + } + LEAVE(); +} |