/** @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 2008-2022 NXP * * NXP CONFIDENTIAL * The source code contained or described herein and all documents related to * the source code (Materials) are owned by NXP, its * suppliers and/or its licensors. Title to the Materials remains with NXP, * its suppliers and/or its licensors. The Materials contain * trade secrets and proprietary and confidential information of NXP, its * suppliers and/or its licensors. The Materials are protected by worldwide * copyright and trade secret laws and treaty provisions. No part of the * Materials may be used, copied, reproduced, modified, published, uploaded, * posted, transmitted, distributed, or disclosed in any way without NXP's * prior express written permission. * * No license under any patent, copyright, trade secret or other intellectual * property right is granted to or conferred upon you by disclosure or delivery * of the Materials, either expressly, by implication, inducement, estoppel or * otherwise. Any license under such intellectual property rights must be * express and approved by NXP in writing. * * Alternatively, this software may be distributed under the terms of GPL v2. * SPDX-License-Identifier: GPL-2.0 * * */ /****************************************************** 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_11ac.h" #include "mlan_11ax.h" #include "mlan_11h.h" #ifdef DRV_EMBEDDED_SUPPLICANT #include "authenticator_api.h" #endif /******************************************************** Local Constants ********************************************************/ /** minimum scan time for passive to active scan */ #define MIN_PASSIVE_TO_ACTIVE_SCAN_TIME 150 #define MRVDRV_MAX_CHANNELS_PER_SCAN 38 /** The maximum number of channels the firmware can scan per command */ #define MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN 4 /** * 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)) /** Memory needed to store a max number/size BSSID TLV for a firmware scan */ #define BSSID_LIST_TLV_MAX_SIZE \ (sizeof(MrvlIEtypesHeader_t) + \ (MRVDRV_MAX_BSSID_LIST * MLAN_MAC_ADDR_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 + BSSID_LIST_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_WEP40, CIPHER_SUITE_TKIP, CIPHER_SUITE_CCMP, CIPHER_SUITE_WEP104, CIPHER_SUITE_GCMP, CIPHER_SUITE_GCMP_256, CIPHER_SUITE_CCMP_256, CIPHER_SUITE_MAX }; static t_u8 wpa_ouis[CIPHER_SUITE_MAX][4] = { {0x00, 0x50, 0xf2, 0x01}, /* WEP40 */ {0x00, 0x50, 0xf2, 0x02}, /* TKIP */ {0x00, 0x50, 0xf2, 0x04}, /* AES */ {0x00, 0x50, 0xf2, 0x05}, /* WEP104 */ }; static t_u8 rsn_oui[CIPHER_SUITE_MAX][4] = { {0x00, 0x0f, 0xac, 0x01}, /* WEP40 */ {0x00, 0x0f, 0xac, 0x02}, /* TKIP */ {0x00, 0x0f, 0xac, 0x04}, /* AES */ {0x00, 0x0f, 0xac, 0x05}, /* WEP104 */ {0x00, 0x0f, 0xac, 0x08}, /* GCMP */ {0x00, 0x0f, 0xac, 0x09}, /* GCMP-256 */ {0x00, 0x0f, 0xac, 0x0a}, /* CCMP-256 */ }; /** * @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 * */ t_u16 radio_type_to_band(t_u8 radio_type) { t_u16 ret_band; switch (radio_type) { case BAND_5GHZ: ret_band = BAND_A; break; case BAND_2GHZ: default: ret_band = BAND_G; break; } return ret_band; } /** * @brief This function will update the channel statistics from scan result * * @param pmpriv A pointer to mlan_private structure * @param pchanstats_tlv A pointer to MrvlIEtypes_ChannelStats_t tlv * * @return NA */ static void wlan_update_chan_statistics(mlan_private *pmpriv, MrvlIEtypes_ChannelStats_t *pchanstats_tlv) { mlan_adapter *pmadapter = pmpriv->adapter; t_u8 i; chan_statistics_t *pchan_stats = (chan_statistics_t *)((t_u8 *)pchanstats_tlv + sizeof(MrvlIEtypesHeader_t)); t_u8 num_chan = wlan_le16_to_cpu(pchanstats_tlv->header.len) / sizeof(chan_statistics_t); ENTER(); for (i = 0; i < num_chan; i++) { if (pmadapter->idx_chan_stats >= pmadapter->num_in_chan_stats) { PRINTM(MERROR, "Over flow: idx_chan_stats=%d, num_in_chan_stats=%d\n", pmadapter->idx_chan_stats, pmadapter->num_in_chan_stats); break; } pchan_stats->total_networks = wlan_le16_to_cpu(pchan_stats->total_networks); pchan_stats->cca_scan_duration = wlan_le16_to_cpu(pchan_stats->cca_scan_duration); pchan_stats->cca_busy_duration = wlan_le16_to_cpu(pchan_stats->cca_busy_duration); PRINTM(MCMND, "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", pchan_stats->chan_num, pchan_stats->noise, pchan_stats->total_networks, pchan_stats->cca_scan_duration, pchan_stats->cca_busy_duration); memcpy_ext(pmadapter, (chan_statistics_t *)&pmadapter ->pchan_stats[pmadapter->idx_chan_stats], pchan_stats, sizeof(chan_statistics_t), sizeof(chan_statistics_t)); pmadapter->idx_chan_stats++; pchan_stats++; } LEAVE(); return; } /** * @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) && (pbss_desc->prsn_ie->ieee_hdr.len > RSN_GTK_OUI_OFFSET)) { ie_body = (IEBody *)(((t_u8 *)pbss_desc->prsn_ie->data) + RSN_GTK_OUI_OFFSET); oui = &rsn_oui[cipher_suite][0]; ret = search_oui_in_ie(pmadapter, ie_body, oui); if (ret) { LEAVE(); return ret; } } LEAVE(); return ret; } /** * @brief This function will pass the correct ie and oui to search_oui_in_ie * * Check the wpa_ie 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 pmadapter A pointer to mlan adapter. * @return 0 on failure to find AES OUI, 1 on success. */ static t_u8 is_rsn_oui_present_in_wpa_ie(mlan_private *pmpriv, t_u32 cipher_suite) { mlan_adapter *pmadapter = pmpriv->adapter; t_u8 *oui = MNULL; IEBody *ie_body = MNULL; IEEEtypes_Generic_t *prsn_ie = MNULL; t_u8 ret = MLAN_OUI_NOT_PRESENT; ENTER(); prsn_ie = (IEEEtypes_Generic_t *)pmpriv->wpa_ie; if (prsn_ie && (prsn_ie->ieee_hdr.element_id == RSN_IE) && (prsn_ie->ieee_hdr.len > RSN_GTK_OUI_OFFSET)) { ie_body = (IEBody *)(prsn_ie->data + RSN_GTK_OUI_OFFSET); oui = &rsn_oui[cipher_suite][0]; ret = search_oui_in_ie(pmadapter, ie_body, oui); if (ret) { 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_ouis[cipher_suite][0]; ret = search_oui_in_ie(pmadapter, ie_body, oui); if (ret) { 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_u16 band; switch (scan_band) { case BAND_A: band = BAND_A | BAND_AN | BAND_AAC; break; case BAND_G: default: band = BAND_B | BAND_G | BAND_GN | BAND_GAC; } 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(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 num of channel */ static t_u8 wlan_scan_create_channel_list( mlan_private *pmpriv, const wlan_user_scan_cfg *puser_scan_in, ChanScanParamSet_t *pscan_chan_list, 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; t_u16 band; t_u16 scan_dur = 0; 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; } PRINTM(MCMD_D, "create_channel_list: region=%d band=%d num_cfp=%d\n", pscan_region->region, pscan_region->band, pscan_region->num_cfp); if ((puser_scan_in && (puser_scan_in->bss_mode == MLAN_SCAN_MODE_IBSS)) || pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) band = pmadapter->adhoc_start_band; else band = pmpriv->config_bands; if (!wlan_is_band_compatible(band, pscan_region->band)) continue; for (next_chan = 0; next_chan < pscan_region->num_cfp; next_chan++) { /* 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 (cfp->dynamic.flags & NXP_CHANNEL_DISABLED) continue; if (wlan_is_chan_passive(pmpriv, pscan_region->band, (t_u8)cfp->channel)) { /* do not send probe requests on this channel */ scan_type = MLAN_SCAN_TYPE_PASSIVE; } switch (pscan_region->band) { case BAND_A: pscan_chan_list[chan_idx].bandcfg.chanBand = BAND_5GHZ; /* Passive scan on DFS channels */ if (wlan_11h_radar_detect_required( pmpriv, (t_u8)cfp->channel) && scan_type != MLAN_SCAN_TYPE_PASSIVE) scan_type = MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE; break; case BAND_B: case BAND_G: if (wlan_bg_scan_type_is_passive( pmpriv, (t_u8)cfp->channel)) { scan_type = MLAN_SCAN_TYPE_PASSIVE; } pscan_chan_list[chan_idx].bandcfg.chanBand = BAND_2GHZ; break; default: pscan_chan_list[chan_idx].bandcfg.chanBand = BAND_2GHZ; break; } if (puser_scan_in && puser_scan_in->chan_list[0].scan_time) { scan_dur = (t_u16)puser_scan_in->chan_list[0] .scan_time; } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE || scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { scan_dur = pmadapter->passive_scan_time; } else if (filtered_scan) { scan_dur = pmadapter->specific_scan_time; } else { scan_dur = pmadapter->active_scan_time; } if (scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE && pmadapter->passive_to_active_scan == MLAN_PASS_TO_ACT_SCAN_EN) { scan_dur = MAX(scan_dur, MIN_PASSIVE_TO_ACTIVE_SCAN_TIME); pscan_chan_list[chan_idx] .chan_scan_mode.passive_to_active_scan = MTRUE; } pscan_chan_list[chan_idx].max_scan_time = wlan_cpu_to_le16(scan_dur); if (scan_type == MLAN_SCAN_TYPE_PASSIVE || scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { pscan_chan_list[chan_idx] .chan_scan_mode.passive_scan = MTRUE; pscan_chan_list[chan_idx] .chan_scan_mode.hidden_ssid_report = MTRUE; } else { pscan_chan_list[chan_idx] .chan_scan_mode.passive_scan = MFALSE; } pscan_chan_list[chan_idx].chan_number = (t_u8)cfp->channel; PRINTM(MCMD_D, "chan=%d, mode=%d, passive_to_active=%d\n", pscan_chan_list[chan_idx].chan_number, pscan_chan_list[chan_idx] .chan_scan_mode.passive_scan, pscan_chan_list[chan_idx] .chan_scan_mode.passive_to_active_scan); chan_idx++; } } LEAVE(); return chan_idx; } /** * @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(mlan_private *pmpriv, 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_ext(pmpriv->adapter, *pptlv_out, pmpriv->wps.wps_ie.vend_hdr.oui, pmpriv->wps.wps_ie.vend_hdr.len, 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(mlan_private *pmpriv, t_void *pioctl_buf, t_u32 max_chan_per_scan, t_u8 filtered_scan, wlan_scan_cmd_config *pscan_cfg_out, MrvlIEtypes_ChanListParamSet_t *pchan_tlv_out, 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_u8 *pchan_tlv_out_temp = MNULL; t_u8 *ptlv_temp = MNULL; t_bool foundJPch14 = MFALSE; t_u16 tlv_buf_len = 0; t_u32 tlv_idx; t_u32 total_scan_time; t_u32 done_early; t_u32 cmd_no; t_u32 first_chan = 1; t_u8 *ptlv_pos; MrvlIETypes_HTCap_t *pht_cap; MrvlIETypes_VHTCap_t *pvht_cap; MrvlIEtypes_Extension_t *phe_cap; t_u16 len = 0; t_u8 radio_type = 0; mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; 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; } /* check expiry before preparing scan list - may affect blacklist */ wlan_11h_get_csa_closed_channel(pmpriv); 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) { if (wlan_is_chan_blacklisted( pmpriv, radio_type_to_band( ptmp_chan_list->bandcfg.chanBand), ptmp_chan_list->chan_number) || wlan_is_chan_disabled( pmpriv, radio_type_to_band( ptmp_chan_list->bandcfg.chanBand), ptmp_chan_list->chan_number)) { PRINTM(MCMND, "Block scan chan = %d\n", ptmp_chan_list->chan_number); ptmp_chan_list++; continue; } if (first_chan) { ptmp_chan_list->chan_scan_mode.first_chan = MTRUE; first_chan = 0; } radio_type = ptmp_chan_list->bandcfg.chanBand; PRINTM(MCMD_D, "Scan: Chan(%3d), bandcfg(%x), Mode(%d,%d), Dur(%d)\n", ptmp_chan_list->chan_number, ptmp_chan_list->bandcfg, 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)); if (foundJPch14 == MTRUE) { foundJPch14 = MFALSE; /* Restore the TLV buffer */ pchan_tlv_out = (MrvlIEtypes_ChanListParamSet_t *) pchan_tlv_out_temp; pchan_tlv_out->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); pchan_tlv_out->header.len = 0; if (ptlv_temp) { memcpy_ext(pmadapter, pscan_cfg_out->tlv_buf, ptlv_temp, tlv_buf_len, tlv_buf_len); pcb->moal_mfree(pmadapter->pmoal_handle, ptlv_temp); ptlv_temp = MNULL; } } /* Special Case: For Japan, Scan on CH14 for 11G rates is not allowed Hence Rates TLV needs to be updated to support only 11B rates */ if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || pmadapter->region_code == COUNTRY_CODE_JP_FF) && (ptmp_chan_list->chan_number == 14) && (pmadapter->ext_scan_type != EXT_SCAN_ENHANCE)) { t_u8 *ptlv_pos = pscan_cfg_out->tlv_buf; t_u16 old_ratetlv_len, new_ratetlv_len; MrvlIEtypesHeader_t *header; MrvlIEtypes_RatesParamSet_t *prates_tlv; /* Preserve the current TLV buffer */ ret = pcb->moal_malloc( pmadapter->pmoal_handle, MAX_SCAN_CFG_ALLOC - CHAN_TLV_MAX_SIZE, MLAN_MEM_DEF, (t_u8 **)&ptlv_temp); if (ret != MLAN_STATUS_SUCCESS || !ptlv_temp) { 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; } pchan_tlv_out_temp = (t_u8 *)pchan_tlv_out; tlv_buf_len = (t_u32)(pchan_tlv_out_temp - pscan_cfg_out->tlv_buf); memcpy_ext(pmadapter, ptlv_temp, ptlv_pos, tlv_buf_len, MAX_SCAN_CFG_ALLOC - CHAN_TLV_MAX_SIZE); /* Search for Rates TLV */ while ((!foundJPch14) && (ptlv_pos < pchan_tlv_out_temp)) { header = (MrvlIEtypesHeader_t *)ptlv_pos; if (header->type == wlan_cpu_to_le16(TLV_TYPE_RATES)) foundJPch14 = MTRUE; else ptlv_pos += (sizeof(MrvlIEtypesHeader_t) + wlan_le16_to_cpu( header->len)); } if (foundJPch14) { /* Update the TLV buffer with *new* * Rates TLV and rearrange remaining TLV * buffer*/ prates_tlv = (MrvlIEtypes_RatesParamSet_t *) ptlv_pos; old_ratetlv_len = sizeof(MrvlIEtypesHeader_t) + wlan_le16_to_cpu( prates_tlv->header.len); prates_tlv->header.len = wlan_copy_rates( prates_tlv->rates, 0, SupportedRates_B, sizeof(SupportedRates_B)); new_ratetlv_len = sizeof(MrvlIEtypesHeader_t) + prates_tlv->header.len; prates_tlv->header.len = wlan_cpu_to_le16( prates_tlv->header.len); memmove(pmadapter, ptlv_pos + new_ratetlv_len, ptlv_pos + old_ratetlv_len, (t_u32)(pchan_tlv_out_temp - (ptlv_pos + old_ratetlv_len))); pchan_tlv_out = (MrvlIEtypes_ChanListParamSet_t *)(pchan_tlv_out_temp - (old_ratetlv_len - new_ratetlv_len)); pchan_tlv_out->header.type = wlan_cpu_to_le16( TLV_TYPE_CHANLIST); pchan_tlv_out->header.len = 0; } } /* Copy the current channel TLV to the command being * prepared */ memcpy_ext(pmadapter, pchan_tlv_out->chan_scan_param + tlv_idx, ptmp_chan_list, sizeof(pchan_tlv_out->chan_scan_param), 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; } /* * Stop the loop if the *current* channel is 14 * and region code is Japan (0x40 or 0xFF) */ if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || pmadapter->region_code == COUNTRY_CODE_JP_FF) && (ptmp_chan_list->chan_number == 14)) { 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; } /* * Stop the loop if the *next* channel is 14 * and region code is Japan (0x40 or 0xFF) */ if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || pmadapter->region_code == COUNTRY_CODE_JP_FF) && (ptmp_chan_list->chan_number == 14)) { done_early = MTRUE; } if (pmadapter->ext_scan && pmadapter->ext_scan_enh && pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) done_early = MFALSE; } /* The total scan time should be less than scan command timeout * value */ if (!total_scan_time || total_scan_time > MRVDRV_MAX_TOTAL_SCAN_TIME) { PRINTM(MMSG, "Total scan time %d ms is invalid, 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; } ptlv_pos = (t_u8 *)pchan_tlv_out + pchan_tlv_out->header.len + sizeof(MrvlIEtypesHeader_t); pchan_tlv_out->header.len = wlan_cpu_to_le16(pchan_tlv_out->header.len); if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info) && (pmpriv->config_bands & BAND_GN || pmpriv->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->config_bands, MTRUE); 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); } if (ISSUPP_11ACENABLED(pmpriv->adapter->fw_cap_info) && (pmpriv->config_bands & BAND_AAC)) { pvht_cap = (MrvlIETypes_VHTCap_t *)ptlv_pos; memset(pmadapter, pvht_cap, 0, sizeof(MrvlIETypes_VHTCap_t)); pvht_cap->header.type = wlan_cpu_to_le16(VHT_CAPABILITY); pvht_cap->header.len = sizeof(VHT_capa_t); wlan_fill_vht_cap_tlv(pmpriv, pvht_cap, pmpriv->config_bands, MFALSE, MFALSE); HEXDUMP("SCAN: VHT_CAPABILITIES IE", (t_u8 *)pvht_cap, sizeof(MrvlIETypes_VHTCap_t)); ptlv_pos += sizeof(MrvlIETypes_VHTCap_t); pvht_cap->header.len = wlan_cpu_to_le16(pvht_cap->header.len); } if (IS_FW_SUPPORT_11AX(pmadapter)) { phe_cap = (MrvlIEtypes_Extension_t *)ptlv_pos; len = wlan_fill_he_cap_tlv(pmpriv, pmpriv->config_bands, phe_cap, MFALSE); HEXDUMP("SCAN: HE_CAPABILITIES IE", (t_u8 *)phe_cap, len); ptlv_pos += len; } pscan_cfg_out->tlv_buf_len = (t_u32)((t_u8 *)ptlv_pos - pscan_cfg_out->tlv_buf); pmadapter->pscan_channels = pstart_chan; /* Send the scan command to the firmware with the specified cfg */ if (pmadapter->ext_scan #ifdef USB8801 && !IS_USB8801(pmadapter->card_type) #endif ) 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, MNULL, pscan_cfg_out); if (ret) break; } LEAVE(); if (ptlv_temp) pcb->moal_mfree(pmadapter->pmoal_handle, ptlv_temp); 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( mlan_private *pmpriv, wlan_user_scan_cfg *puser_scan_in, wlan_scan_cmd_config *pscan_cfg_out, MrvlIEtypes_ChanListParamSet_t **ppchan_list_out, ChanScanParamSet_t *pscan_chan_list, t_u8 *pmax_chan_per_scan, t_u8 *pfiltered_scan, 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; MrvlIEtypes_Bssid_List_t *pbssid_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 chan_list_idx = 0; 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_ScanChanGap_t *pscan_gap_tlv; MrvlIEtypes_BssMode_t *pbss_mode; t_u8 num_of_channel = 0; 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_ext(pmadapter, pscan_cfg_out->specific_bssid, puser_scan_in->specific_bssid, sizeof(pscan_cfg_out->specific_bssid), sizeof(pscan_cfg_out->specific_bssid)); if (pmadapter->ext_scan) { if (puser_scan_in->bssid_num) { pbssid_tlv = (MrvlIEtypes_Bssid_List_t *)ptlv_pos; pbssid_tlv->header.type = TLV_TYPE_BSSID; pbssid_tlv->header.len = wlan_cpu_to_le16( MLAN_MAC_ADDR_LENGTH * puser_scan_in->bssid_num); memcpy_ext(pmadapter, pbssid_tlv->bssid, puser_scan_in->bssid_list, MLAN_MAC_ADDR_LENGTH * puser_scan_in->bssid_num, MLAN_MAC_ADDR_LENGTH * puser_scan_in->bssid_num); ptlv_pos += sizeof(MrvlIEtypesHeader_t) + MLAN_MAC_ADDR_LENGTH * puser_scan_in->bssid_num; DBG_HEXDUMP( MCMD_D, "scan bssid filter", pbssid_tlv, sizeof(MrvlIEtypesHeader_t) + MLAN_MAC_ADDR_LENGTH * puser_scan_in ->bssid_num); } else if (memcmp(pmadapter, pscan_cfg_out->specific_bssid, &zero_mac, sizeof(zero_mac))) { pbssid_tlv = (MrvlIEtypes_Bssid_List_t *)ptlv_pos; pbssid_tlv->header.type = TLV_TYPE_BSSID; pbssid_tlv->header.len = wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); memcpy_ext(pmadapter, pbssid_tlv->bssid, puser_scan_in->specific_bssid, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); ptlv_pos += sizeof(MrvlIEtypes_Bssid_List_t); } } 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( (char *)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_ext(pmadapter, pwildcard_ssid_tlv->ssid, puser_scan_in->ssid_list[ssid_idx].ssid, ssid_len, MLAN_MAX_SSID_LENGTH); 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; if (!puser_scan_in->ssid_list[ssid_idx].max_len) { PRINTM(MCMND, "user scan: %s\n", pwildcard_ssid_tlv->ssid); puser_scan_in->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 (puser_scan_in) { if (puser_scan_in->scan_chan_gap) { *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; PRINTM(MCMND, "Scan: channel gap = 0x%x\n", puser_scan_in->scan_chan_gap); pscan_gap_tlv = (MrvlIEtypes_ScanChanGap_t *)ptlv_pos; pscan_gap_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); pscan_gap_tlv->header.len = sizeof(pscan_gap_tlv->gap); pscan_gap_tlv->gap = wlan_cpu_to_le16( (t_u16)puser_scan_in->scan_chan_gap); ptlv_pos += sizeof(pscan_gap_tlv->header) + pscan_gap_tlv->header.len; pscan_gap_tlv->header.len = wlan_cpu_to_le16(pscan_gap_tlv->header.len); } } else if (pmadapter->scan_chan_gap) { *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; PRINTM(MCMND, "Scan: channel gap = 0x%x\n", pmadapter->scan_chan_gap); pscan_gap_tlv = (MrvlIEtypes_ScanChanGap_t *)ptlv_pos; pscan_gap_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); pscan_gap_tlv->header.len = sizeof(pscan_gap_tlv->gap); pscan_gap_tlv->gap = wlan_cpu_to_le16((t_u16)pmadapter->scan_chan_gap); ptlv_pos += sizeof(pscan_gap_tlv->header) + pscan_gap_tlv->header.len; } if (pmadapter->ext_scan) { pbss_mode = (MrvlIEtypes_BssMode_t *)ptlv_pos; pbss_mode->header.type = wlan_cpu_to_le16(TLV_TYPE_BSS_MODE); pbss_mode->header.len = sizeof(pbss_mode->bss_mode); pbss_mode->bss_mode = pscan_cfg_out->bss_mode; ptlv_pos += sizeof(pbss_mode->header) + pbss_mode->header.len; pbss_mode->header.len = wlan_cpu_to_le16(pbss_mode->header.len); if (pmadapter->ext_scan_enh) { if (puser_scan_in) { if (puser_scan_in->ext_scan_type == EXT_SCAN_ENHANCE) pmadapter->ext_scan_type = EXT_SCAN_ENHANCE; else pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; } else if (pmadapter->ext_scan == EXT_SCAN_TYPE_ENH) pmadapter->ext_scan_type = EXT_SCAN_ENHANCE; else pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; if (pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SCAN; } } /* 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) ? pmpriv->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_ext(pmadapter, prates_tlv->rates, rates, rates_size, rates_size); ptlv_pos += sizeof(prates_tlv->header) + rates_size; PRINTM(MINFO, "SCAN_CMD: Rates size = %d\n", rates_size); if (wlan_is_ext_capa_support(pmpriv)) wlan_add_ext_capa_info_ie(pmpriv, MNULL, &ptlv_pos); if (pmpriv->adapter->ecsa_enable) { t_u8 bandwidth = BW_20MHZ; t_u8 oper_class = 1; t_u32 usr_dot_11n_dev_cap; if (pmpriv->media_connected) { if (pmpriv->config_bands & BAND_A) usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_a; else usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_bg; if (usr_dot_11n_dev_cap & MBIT(17)) { bandwidth = BW_40MHZ; if (ISSUPP_11ACENABLED( pmadapter->fw_cap_info) && (pmpriv->config_bands & BAND_AAC)) bandwidth = BW_80MHZ; } wlan_get_curr_oper_class( pmpriv, pmpriv->curr_bss_params.bss_descriptor.channel, bandwidth, &oper_class); } wlan_add_supported_oper_class_ie(pmpriv, &ptlv_pos, oper_class); } wlan_add_wps_probe_request_ie(pmpriv, &ptlv_pos); if (puser_scan_in && puser_scan_in->proberesp_only) { MrvlIEtypes_OnlyProberesp_t *proberesp_only = (MrvlIEtypes_OnlyProberesp_t *)ptlv_pos; memset(pmadapter, proberesp_only, 0, sizeof(MrvlIEtypes_OnlyProberesp_t)); proberesp_only->header.type = wlan_cpu_to_le16(TLV_TYPE_ONLYPROBERESP); proberesp_only->header.len = wlan_cpu_to_le16(sizeof(t_u8)); proberesp_only->proberesp_only = puser_scan_in->proberesp_only; ptlv_pos += sizeof(MrvlIEtypes_OnlyProberesp_t); } if (puser_scan_in && memcmp(pmadapter, puser_scan_in->random_mac, zero_mac, MLAN_MAC_ADDR_LENGTH)) { MrvlIEtypes_MacAddr_t *randomMacParam = (MrvlIEtypes_MacAddr_t *)ptlv_pos; memset(pmadapter, randomMacParam, 0, sizeof(MrvlIEtypes_MacAddr_t)); randomMacParam->header.type = wlan_cpu_to_le16(TLV_TYPE_RANDOM_MAC); randomMacParam->header.len = wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); memcpy_ext(pmadapter, randomMacParam->mac, puser_scan_in->random_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); ptlv_pos += sizeof(MrvlIEtypes_MacAddr_t); } /* * 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++) { radio_type = puser_scan_in->chan_list[chan_idx].radio_type; /*Ignore 5G/2G channels if radio_type do not match * band*/ if (!wlan_is_band_compatible( pmpriv->config_bands, radio_type_to_band(radio_type))) continue; (pscan_chan_list + chan_list_idx)->bandcfg.chanBand = radio_type; channel = puser_scan_in->chan_list[chan_idx].chan_number; (pscan_chan_list + chan_list_idx)->chan_number = channel; 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 == BAND_5GHZ) { 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; } } if (wlan_is_chan_passive(pmpriv, radio_type_to_band(radio_type), channel)) { /* do not send probe requests on this channel */ scan_type = MLAN_SCAN_TYPE_PASSIVE; } /* Prevent active scanning on a radar controlled channel */ if (radio_type == BAND_5GHZ && scan_type != MLAN_SCAN_TYPE_PASSIVE) { if (pmadapter->active_scan_triggered == MFALSE) if (wlan_11h_radar_detect_required( pmpriv, channel)) { scan_type = MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE; } } if (radio_type == BAND_2GHZ && scan_type != MLAN_SCAN_TYPE_PASSIVE) { if (pmadapter->active_scan_triggered == MFALSE) if (wlan_bg_scan_type_is_passive( pmpriv, channel)) { scan_type = MLAN_SCAN_TYPE_PASSIVE; } } if (scan_type == MLAN_SCAN_TYPE_PASSIVE || scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { (pscan_chan_list + chan_list_idx) ->chan_scan_mode.passive_scan = MTRUE; (pscan_chan_list + chan_list_idx) ->chan_scan_mode.hidden_ssid_report = MTRUE; } else { (pscan_chan_list + chan_list_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_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { scan_dur = pmadapter->passive_scan_time; } else if (*pfiltered_scan) { scan_dur = pmadapter->specific_scan_time; } else { scan_dur = pmadapter->active_scan_time; } } if (pmadapter->coex_scan && pmadapter->coex_min_scan_time && (pmadapter->coex_min_scan_time > scan_dur)) scan_dur = pmadapter->coex_min_scan_time; if (scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE && pmadapter->passive_to_active_scan == MLAN_PASS_TO_ACT_SCAN_EN) { (pscan_chan_list + chan_list_idx) ->chan_scan_mode.passive_to_active_scan = MTRUE; scan_dur = MAX(MIN_PASSIVE_TO_ACTIVE_SCAN_TIME, scan_dur); } PRINTM(MINFO, "chan=%d, mode=%d, passive_to_active=%d\n", (pscan_chan_list + chan_list_idx)->chan_number, (pscan_chan_list + chan_list_idx) ->chan_scan_mode.passive_scan, (pscan_chan_list + chan_list_idx) ->chan_scan_mode.passive_to_active_scan); (pscan_chan_list + chan_list_idx)->min_scan_time = wlan_cpu_to_le16(scan_dur); (pscan_chan_list + chan_list_idx)->max_scan_time = wlan_cpu_to_le16(scan_dur); chan_list_idx++; } /* 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 { num_of_channel = wlan_scan_create_channel_list(pmpriv, puser_scan_in, pscan_chan_list, *pfiltered_scan); PRINTM(MCMND, "Scan: Creating full region channel list %d\n", num_of_channel); } 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(pmlan_adapter pmadapter, MrvlIEtypes_Data_t *ptlv, t_u32 tlv_buf_size, t_u32 req_tlv_type, 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; case TLV_TYPE_CHANNEL_STATS: PRINTM(MINFO, "SCAN_RESP: CHANNEL STATS 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 * @param ext_scan extended scan * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_interpret_bss_desc_with_ie(pmlan_adapter pmadapter, BSSDescriptor_t *pbss_entry, t_u8 **pbeacon_info, t_u32 *bytes_left, t_u8 ext_scan) { 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}; const t_u8 osen_oui[] = {0x50, 0x6f, 0x9a, 0x12}; IEEEtypes_CountryInfoSet_t *pcountry_info; IEEEtypes_Extension_t *pext_tlv; 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_ext(pmadapter, &beacon_size, *pbeacon_info, sizeof(beacon_size), 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_ext(pmadapter, pbss_entry->mac_address, pcurrent_ptr, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); PRINTM(MINFO, "InterpretIE: AP MAC Addr-" MACSTR "\n", MAC2STR(pbss_entry->mac_address)); 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 (!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_ext(pmadapter, fixed_ie.time_stamp, pcurrent_ptr, 8, sizeof(fixed_ie.time_stamp)); memcpy_ext(pmadapter, pbss_entry->time_stamp, pcurrent_ptr, 8, sizeof(pbss_entry->time_stamp)); pcurrent_ptr += 8; bytes_left_for_current_beacon -= 8; /* Beacon interval is 2 bytes long */ memcpy_ext(pmadapter, &fixed_ie.beacon_interval, pcurrent_ptr, 2, sizeof(fixed_ie.beacon_interval)); 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_ext(pmadapter, &fixed_ie.capabilities, pcurrent_ptr, 2, sizeof(fixed_ie.capabilities)); 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_ext(pmadapter, &pbss_entry->cap_info, pcap_info, sizeof(IEEEtypes_CapInfo_t), sizeof(IEEEtypes_CapInfo_t)); pcurrent_ptr += 2; bytes_left_for_current_beacon -= 2; /* Rest of the current buffer are IE's */ PRINTM(MINFO, "InterpretIE: IELength for this AP = %d\n", bytes_left_for_current_beacon); HEXDUMP("InterpretIE: IE info", (t_u8 *)pcurrent_ptr, bytes_left_for_current_beacon); if (pcap_info->privacy) { PRINTM(MINFO, "InterpretIE: AP WEP enabled\n"); pbss_entry->privacy = Wlan802_11PrivFilter8021xWEP; } else { pbss_entry->privacy = Wlan802_11PrivFilterAcceptAll; } if (pcap_info->ibss == 1) pbss_entry->bss_mode = MLAN_BSS_MODE_IBSS; else pbss_entry->bss_mode = MLAN_BSS_MODE_INFRA; if (pcap_info->spectrum_mgmt == 1) { PRINTM(MINFO, "InterpretIE: 11h- Spectrum Management " "capability bit found\n"); pbss_entry->wlan_11h_bss_info.sensed_11h = 1; } /* Process variable IE */ while (bytes_left_for_current_beacon >= 2) { element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); element_len = *((t_u8 *)pcurrent_ptr + 1); total_ie_len = element_len + sizeof(IEEEtypes_Header_t); if (bytes_left_for_current_beacon < total_ie_len) { PRINTM(MERROR, "InterpretIE: Error in processing IE, " "bytes left < IE length\n"); bytes_left_for_current_beacon = 0; ret = MLAN_STATUS_FAILURE; continue; } switch (element_id) { case SSID: if (element_len > MRVDRV_MAX_SSID_LENGTH) { bytes_left_for_current_beacon = 0; ret = MLAN_STATUS_FAILURE; continue; } if (!pbss_entry->ssid.ssid_len) { pbss_entry->ssid.ssid_len = element_len; memcpy_ext(pmadapter, pbss_entry->ssid.ssid, (pcurrent_ptr + 2), element_len, sizeof(pbss_entry->ssid.ssid)); } 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; ret = MLAN_STATUS_FAILURE; continue; } memcpy_ext(pmadapter, pbss_entry->data_rates, pcurrent_ptr + 2, element_len, sizeof(pbss_entry->data_rates)); memcpy_ext(pmadapter, pbss_entry->supported_rates, pcurrent_ptr + 2, element_len, sizeof(pbss_entry->supported_rates)); 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_ext(pmadapter, &pbss_entry->phy_param_set.fh_param_set, pfh_param_set, 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_ext(pmadapter, &pbss_entry->phy_param_set.ds_param_set, pds_param_set, 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_ext(pmadapter, &pbss_entry->ss_param_set.cf_param_set, pcf_param_set, 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_ext(pmadapter, &pbss_entry->ss_param_set.ibss_param_set, pibss_param_set, 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_ext(pmadapter, &pbss_entry->country_info, pcountry_info, pcountry_info->len + 2, sizeof(pbss_entry->country_info)); 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_ext(pmadapter, prate, pcurrent_ptr + 2, bytes_to_copy, bytes_to_copy); prate = (t_u8 *)pbss_entry->supported_rates; prate += rate_size; memcpy_ext(pmadapter, prate, pcurrent_ptr + 2, bytes_to_copy, 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_ext(pmadapter, (t_u8 *)&pbss_entry->wmm_ie, pcurrent_ptr, total_ie_len, sizeof(pbss_entry->wmm_ie)); HEXDUMP("InterpretIE: Resp WMM_IE", (t_u8 *)&pbss_entry->wmm_ie, total_ie_len); } } else if (!memcmp(pmadapter, pvendor_ie->vend_hdr.oui, osen_oui, sizeof(osen_oui))) { pbss_entry->posen_ie = (IEEEtypes_Generic_t *)pcurrent_ptr; pbss_entry->osen_offset = (t_u16)( pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp OSEN_IE", (t_u8 *)pbss_entry->posen_ie, (*(pbss_entry->posen_ie)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); } 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 RSNX_IE: pbss_entry->prsnx_ie = (IEEEtypes_Generic_t *)pcurrent_ptr; pbss_entry->rsnx_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp RSNX_IE", (t_u8 *)pbss_entry->prsnx_ie, (*(pbss_entry->prsnx_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 MULTI_BSSID: if (IS_FW_SUPPORT_MULTIBSSID(pmadapter)) { pbss_entry->multi_bssid_ap = MULTI_BSSID_AP; HEXDUMP("InterpretIE: Multi BSSID IE", (t_u8 *)pcurrent_ptr, total_ie_len); } 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 OBSS_IE", (t_u8 *)pbss_entry->poverlap_bss_scan_param, (*(pbss_entry->poverlap_bss_scan_param)) .ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case VHT_CAPABILITY: pbss_entry->pvht_cap = (IEEEtypes_VHTCap_t *)pcurrent_ptr; pbss_entry->vht_cap_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp VHTCAP_IE", (t_u8 *)pbss_entry->pvht_cap, (*(pbss_entry->pvht_cap)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case VHT_OPERATION: pbss_entry->pvht_oprat = (IEEEtypes_VHTOprat_t *)pcurrent_ptr; pbss_entry->vht_oprat_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp VHTOPER_IE", (t_u8 *)pbss_entry->pvht_oprat, (*(pbss_entry->pvht_oprat)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case EXT_BSS_LOAD: pbss_entry->pext_bssload = (IEEEtypes_ExtBSSload_t *)pcurrent_ptr; pbss_entry->ext_bssload_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp EXTBSSLOAD_IE", (t_u8 *)pbss_entry->pext_bssload, (*(pbss_entry->pext_bssload)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case VHT_TX_POWER_ENV: pbss_entry->pvht_txpower = (IEEEtypes_VHTtxpower_t *)pcurrent_ptr; pbss_entry->vht_txpower_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp TXPOW_IE", (t_u8 *)pbss_entry->pvht_txpower, (*(pbss_entry->pvht_txpower)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case EXT_POWER_CONSTR: pbss_entry->pext_pwer = (IEEEtypes_ExtPwerCons_t *)pcurrent_ptr; pbss_entry->ext_pwer_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp EXTPOW_IE", (t_u8 *)pbss_entry->pext_pwer, (*(pbss_entry->pext_pwer)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case QUIET_CHAN: pbss_entry->pquiet_chan = (IEEEtypes_QuietChan_t *)pcurrent_ptr; pbss_entry->quiet_chan_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp QUIETCHAN_IE", (t_u8 *)pbss_entry->pquiet_chan, (*(pbss_entry->pquiet_chan)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case BW_CHANNEL_SWITCH: /* RANDYTODO */ break; case AID_INFO: break; case OPER_MODE_NTF: pbss_entry->poper_mode = (IEEEtypes_OperModeNtf_t *)pcurrent_ptr; pbss_entry->oper_mode_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp OPERMODENTF_IE", (t_u8 *)pbss_entry->poper_mode, (*(pbss_entry->poper_mode)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case EXTENSION: pext_tlv = (IEEEtypes_Extension_t *)pcurrent_ptr; switch (pext_tlv->ext_id) { case HE_CAPABILITY: pbss_entry->phe_cap = (IEEEtypes_HECap_t *)pcurrent_ptr; pbss_entry->he_cap_offset = (t_u16)( pcurrent_ptr - pbss_entry->pbeacon_buf); break; case HE_OPERATION: pbss_entry->phe_oprat = pext_tlv; pbss_entry->he_oprat_offset = (t_u16)( pcurrent_ptr - pbss_entry->pbeacon_buf); break; default: break; } break; case MOBILITY_DOMAIN: PRINTM(MCMND, "Mobility Domain IE received in Scan\n"); pbss_entry->pmd_ie = (IEEEtypes_MobilityDomain_t *)pcurrent_ptr; pbss_entry->md_offset = (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); HEXDUMP("InterpretIE: Resp Mobility Domain IE", (t_u8 *)pbss_entry->pmd_ie, (*(pbss_entry->pmd_ie)).ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; default: 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(mlan_private *pmpriv, 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->posen_ie) { pbss_entry->posen_ie = (IEEEtypes_Generic_t *)(pbss_entry->pbeacon_buf + pbss_entry->osen_offset); } if (pbss_entry->pmd_ie) { pbss_entry->pmd_ie = (IEEEtypes_MobilityDomain_t *)(pbss_entry->pbeacon_buf + pbss_entry->md_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); } if (pbss_entry->pvht_cap) { pbss_entry->pvht_cap = (IEEEtypes_VHTCap_t *)(pbss_entry->pbeacon_buf + pbss_entry->vht_cap_offset); } if (pbss_entry->pvht_oprat) { pbss_entry->pvht_oprat = (IEEEtypes_VHTOprat_t *)(pbss_entry->pbeacon_buf + pbss_entry->vht_oprat_offset); } if (pbss_entry->pvht_txpower) { pbss_entry->pvht_txpower = (IEEEtypes_VHTtxpower_t *)(pbss_entry->pbeacon_buf + pbss_entry->vht_txpower_offset); } if (pbss_entry->pext_pwer) { pbss_entry->pext_pwer = (IEEEtypes_ExtPwerCons_t *)(pbss_entry->pbeacon_buf + pbss_entry->ext_pwer_offset); } if (pbss_entry->pext_bssload) { pbss_entry->pext_bssload = (IEEEtypes_ExtBSSload_t *)(pbss_entry->pbeacon_buf + pbss_entry->ext_bssload_offset); } if (pbss_entry->pquiet_chan) { pbss_entry->pquiet_chan = (IEEEtypes_QuietChan_t *)(pbss_entry->pbeacon_buf + pbss_entry->quiet_chan_offset); } if (pbss_entry->poper_mode) { pbss_entry->poper_mode = (IEEEtypes_OperModeNtf_t *)(pbss_entry->pbeacon_buf + pbss_entry->oper_mode_offset); } if (pbss_entry->phe_cap) { pbss_entry->phe_cap = (IEEEtypes_HECap_t *)(pbss_entry->pbeacon_buf + pbss_entry->he_cap_offset); } if (pbss_entry->phe_oprat) { pbss_entry->phe_oprat = (IEEEtypes_Extension_t *)(pbss_entry->pbeacon_buf + pbss_entry->he_oprat_offset); } if (pbss_entry->prsnx_ie) { pbss_entry->prsnx_ie = (IEEEtypes_Generic_t *)(pbss_entry->pbeacon_buf + pbss_entry->rsnx_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->posen_ie = MNULL; pbss_entry->osen_offset = 0; pbss_entry->pmd_ie = MNULL; pbss_entry->md_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(mlan_private *pmpriv, t_u32 beacon_idx, t_u32 num_of_ent, 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_u32 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_ext(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, pnew_beacon->beacon_buf_size, 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_ext(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, new_bcn_size, 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_ext(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, new_bcn_size, 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->posen_ie) pnew_beacon->osen_offset = pmadapter->pscan_table[beacon_idx] .osen_offset; if (pnew_beacon->pmd_ie) pnew_beacon->md_offset = pmadapter->pscan_table[beacon_idx] .md_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; if (pnew_beacon->pvht_cap) pnew_beacon->vht_cap_offset = pmadapter->pscan_table[beacon_idx] .vht_cap_offset; if (pnew_beacon->pvht_oprat) pnew_beacon->vht_oprat_offset = pmadapter->pscan_table[beacon_idx] .vht_oprat_offset; if (pnew_beacon->pvht_txpower) pnew_beacon->vht_txpower_offset = pmadapter->pscan_table[beacon_idx] .vht_txpower_offset; if (pnew_beacon->pext_pwer) pnew_beacon->ext_pwer_offset = pmadapter->pscan_table[beacon_idx] .ext_pwer_offset; if (pnew_beacon->pext_bssload) pnew_beacon->ext_bssload_offset = pmadapter->pscan_table[beacon_idx] .ext_bssload_offset; if (pnew_beacon->pquiet_chan) pnew_beacon->quiet_chan_offset = pmadapter->pscan_table[beacon_idx] .quiet_chan_offset; if (pnew_beacon->poper_mode) pnew_beacon->oper_mode_offset = pmadapter->pscan_table[beacon_idx] .oper_mode_offset; if (pnew_beacon->phe_cap) pnew_beacon->he_cap_offset = pmadapter->pscan_table[beacon_idx] .he_cap_offset; if (pnew_beacon->phe_oprat) pnew_beacon->he_oprat_offset = pmadapter->pscan_table[beacon_idx] .he_oprat_offset; if (pnew_beacon->prsnx_ie) pnew_beacon->rsnx_offset = pmadapter->pscan_table[beacon_idx] .rsnx_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 */ if (pmadapter->callbacks.moal_vmalloc && pmadapter->callbacks.moal_vfree) ret = pmadapter->callbacks.moal_vmalloc( pmadapter->pmoal_handle, pmadapter->bcn_buf_size + DEFAULT_SCAN_BEACON_BUFFER, (t_u8 **)&tmp_buf); else 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_ext(pmadapter, tmp_buf, pmadapter->bcn_buf, bcn_size, 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; if (pmadapter->callbacks.moal_vmalloc && pmadapter->callbacks.moal_vfree) pmadapter->callbacks.moal_vfree( pmadapter->pmoal_handle, (t_u8 *)pmadapter->bcn_buf); else 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_ext(pmadapter, pmadapter->pbcn_buf_end, pnew_beacon->pbeacon_buf, pnew_beacon->beacon_buf_size, 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): " MACSTR "; sz=%03d, left=%03d\n", beacon_idx, MAC2STR(pnew_beacon->mac_address), 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 update beacon buffer of the current bss descriptor * * @param pmpriv A pointer to mlan_private structure * * @return MLAN_STATUS_SUCCESS, otherwise failure */ static mlan_status wlan_update_curr_bcn(mlan_private *pmpriv) { BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor; mlan_status ret = MLAN_STATUS_SUCCESS; ENTER(); if (pmpriv->pcurr_bcn_buf && pmpriv->curr_bcn_size) { pcurr_bss->pbeacon_buf = pmpriv->pcurr_bcn_buf; pcurr_bss->beacon_buf_size = pmpriv->curr_bcn_size; pcurr_bss->beacon_buf_size_max = 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->pmd_ie) { pcurr_bss->pmd_ie = (IEEEtypes_MobilityDomain_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->md_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); } if (pcurr_bss->pvht_cap) { pcurr_bss->pvht_cap = (IEEEtypes_VHTCap_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->vht_cap_offset); } if (pcurr_bss->pvht_oprat) { pcurr_bss->pvht_oprat = (IEEEtypes_VHTOprat_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->vht_oprat_offset); } if (pcurr_bss->pvht_txpower) { pcurr_bss->pvht_txpower = (IEEEtypes_VHTtxpower_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->vht_txpower_offset); } if (pcurr_bss->pext_pwer) { pcurr_bss->pext_pwer = (IEEEtypes_ExtPwerCons_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->ext_pwer_offset); } if (pcurr_bss->pext_bssload) { pcurr_bss->pext_bssload = (IEEEtypes_ExtBSSload_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->ext_bssload_offset); } if (pcurr_bss->pquiet_chan) { pcurr_bss->pquiet_chan = (IEEEtypes_QuietChan_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->quiet_chan_offset); } if (pcurr_bss->poper_mode) { pcurr_bss->poper_mode = (IEEEtypes_OperModeNtf_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->oper_mode_offset); } if (pcurr_bss->phe_cap) { pcurr_bss->phe_cap = (IEEEtypes_HECap_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->he_cap_offset); } if (pcurr_bss->phe_oprat) { pcurr_bss->phe_oprat = (IEEEtypes_Extension_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->he_oprat_offset); } if (pcurr_bss->prsnx_ie) { pcurr_bss->prsnx_ie = (IEEEtypes_Generic_t *)(pcurr_bss->pbeacon_buf + pcurr_bss->rsnx_offset); } PRINTM(MINFO, "current beacon restored %d\n", pmpriv->curr_bcn_size); } else { PRINTM(MERROR, "curr_bcn_buf not saved\n"); ret = MLAN_STATUS_FAILURE; } LEAVE(); return ret; } /** * @brief get the chan load from chan stats. * * @param pmadapter A pointer to mlan_adapter structure * @param channel channel * * * @return channel load */ static t_u16 wlan_get_chan_load(mlan_adapter *pmadapter, t_u8 channel) { t_u16 chan_load = 0; int i; for (i = 0; i < (int)pmadapter->num_in_chan_stats; i++) { if ((pmadapter->pchan_stats[i].chan_num == channel) && pmadapter->pchan_stats[i].cca_scan_duration) { chan_load = (pmadapter->pchan_stats[i].cca_busy_duration * 100) / pmadapter->pchan_stats[i].cca_scan_duration; break; } } return chan_load; } /** * @brief get the chan min/max rssi * * @param pmadapter A pointer to mlan_adapter structure * @param channel channel * * @param min_flag flag to get min rssi * @return rssi */ static t_u8 wlan_get_chan_rssi(mlan_adapter *pmadapter, t_u8 channel, t_u8 min_flag) { t_u8 rssi = 0; int i; for (i = 0; i < (int)pmadapter->num_in_scan_table; i++) { if (pmadapter->pscan_table[i].channel == channel) { if (rssi == 0) rssi = (t_s32)pmadapter->pscan_table[i].rssi; else { if (min_flag) rssi = MIN( rssi, pmadapter->pscan_table[i].rssi); else rssi = MAX( rssi, pmadapter->pscan_table[i].rssi); } } } return rssi; } /** * @brief update the min/max rssi for channel statistics. * * @param pmadapter A pointer to mlan_adapter structure * @return N/A */ static t_void wlan_update_chan_rssi(mlan_adapter *pmadapter) { int i; t_s8 min_rssi = 0; t_s8 max_rssi = 0; t_s8 rss = 0; for (i = 0; i < (int)pmadapter->num_in_chan_stats; i++) { if (pmadapter->pchan_stats[i].chan_num && pmadapter->pchan_stats[i].cca_scan_duration) { min_rssi = -wlan_get_chan_rssi( pmadapter, pmadapter->pchan_stats[i].chan_num, MFALSE); max_rssi = -wlan_get_chan_rssi( pmadapter, pmadapter->pchan_stats[i].chan_num, MTRUE); rss = min_rssi - pmadapter->pchan_stats[i].noise; // rss should always > 0, FW need fix the wrong // rssi/noise in scantable if (rss > 0) pmadapter->pchan_stats[i].min_rss = rss; else pmadapter->pchan_stats[i].min_rss = 0; rss = max_rssi - pmadapter->pchan_stats[i].noise; if (rss > 0) pmadapter->pchan_stats[i].max_rss = rss; else pmadapter->pchan_stats[i].max_rss = 0; PRINTM(MCMND, "chan=%d, min_rssi=%d, max_rssi=%d noise=%d min_rss=%d, max_rss=%d\n", pmadapter->pchan_stats[i].chan_num, min_rssi, max_rssi, pmadapter->pchan_stats[i].noise, pmadapter->pchan_stats[i].min_rss, pmadapter->pchan_stats[i].max_rss); } } return; } /** * @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(mlan_private *pmpriv) { mlan_adapter *pmadapter = pmpriv->adapter; t_s32 j; t_u32 i; mlan_status ret = MLAN_STATUS_SUCCESS; BSSDescriptor_t *bss_new_entry = MNULL; pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); if (pmpriv->media_connected == MTRUE) { j = wlan_find_bssid_in_list( pmpriv, pmpriv->curr_bss_params.bss_descriptor.mac_address, pmpriv->bss_mode); if (j >= 0) { memcpy_ext(pmadapter, &pmadapter->pscan_table[j].ssid, &pmpriv->curr_bss_params.bss_descriptor.ssid, sizeof(mlan_802_11_ssid), sizeof(mlan_802_11_ssid)); 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.posen_ie = MNULL; pmpriv->curr_bss_params.bss_descriptor.osen_offset = 0; pmpriv->curr_bss_params.bss_descriptor.pmd_ie = MNULL; pmpriv->curr_bss_params.bss_descriptor.md_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.pvht_cap = MNULL; pmpriv->curr_bss_params.bss_descriptor.vht_cap_offset = 0; pmpriv->curr_bss_params.bss_descriptor.pvht_oprat = MNULL; pmpriv->curr_bss_params.bss_descriptor.vht_oprat_offset = 0; pmpriv->curr_bss_params.bss_descriptor.pvht_txpower = MNULL; pmpriv->curr_bss_params.bss_descriptor .vht_txpower_offset = 0; pmpriv->curr_bss_params.bss_descriptor.pext_pwer = MNULL; pmpriv->curr_bss_params.bss_descriptor.ext_pwer_offset = 0; pmpriv->curr_bss_params.bss_descriptor.pext_bssload = MNULL; pmpriv->curr_bss_params.bss_descriptor .ext_bssload_offset = 0; pmpriv->curr_bss_params.bss_descriptor.pquiet_chan = MNULL; pmpriv->curr_bss_params.bss_descriptor .quiet_chan_offset = 0; pmpriv->curr_bss_params.bss_descriptor.poper_mode = MNULL; pmpriv->curr_bss_params.bss_descriptor.oper_mode_offset = 0; pmpriv->curr_bss_params.bss_descriptor.phe_cap = MNULL; pmpriv->curr_bss_params.bss_descriptor.he_cap_offset = 0; pmpriv->curr_bss_params.bss_descriptor.phe_oprat = MNULL; pmpriv->curr_bss_params.bss_descriptor.he_oprat_offset = 0; pmpriv->curr_bss_params.bss_descriptor.prsnx_ie = MNULL; pmpriv->curr_bss_params.bss_descriptor.rsnx_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_ext( pmadapter, &pmpriv->curr_bss_params.bss_descriptor, &pmadapter->pscan_table[j], sizeof(pmpriv->curr_bss_params.bss_descriptor), sizeof(pmpriv->curr_bss_params.bss_descriptor)); pmadapter->callbacks.moal_spin_unlock( pmadapter->pmoal_handle, pmpriv->curr_bcn_buf_lock); wlan_save_curr_bcn(pmpriv); } else { // Apend to the end of scan table if (pmpriv->pcurr_bcn_buf && pmpriv->curr_bcn_size) { 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) { memcpy_ext( pmadapter, bss_new_entry, &pmpriv->curr_bss_params .bss_descriptor, sizeof(pmpriv->curr_bss_params .bss_descriptor), sizeof(BSSDescriptor_t)); if (pmadapter->num_in_scan_table < MRVDRV_MAX_BSSID_LIST) pmadapter->num_in_scan_table++; pmadapter ->pscan_table [pmadapter->num_in_scan_table - 1] .pbeacon_buf = MNULL; wlan_ret_802_11_scan_store_beacon( pmpriv, pmadapter->num_in_scan_table - 1, pmadapter->num_in_scan_table, bss_new_entry); if (bss_new_entry->pbeacon_buf == MNULL) pmadapter->num_in_scan_table--; else memcpy_ext( pmadapter, &pmadapter->pscan_table [pmadapter->num_in_scan_table - 1], bss_new_entry, sizeof(BSSDescriptor_t), sizeof(BSSDescriptor_t)); pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)bss_new_entry); } } } } for (i = 0; i < pmadapter->num_in_scan_table; i++) { PRINTM(MINFO, "Scan:(%02d) " MACSTR ", " "RSSI[%03d], SSID[%s]\n", i, MAC2STR(pmadapter->pscan_table[i].mac_address), (t_s32)pmadapter->pscan_table[i].rssi, pmadapter->pscan_table[i].ssid.ssid); pmadapter->pscan_table[i].chan_load = wlan_get_chan_load( pmadapter, pmadapter->pscan_table[i].channel); } wlan_update_chan_rssi(pmadapter); /* * Prepares domain info from scan table and downloads the * domain info command to the FW. */ wlan_11d_prepare_dnld_domain_info_cmd(pmpriv); PRINTM(MMSG, "wlan: SCAN COMPLETED: scanned AP count=%d\n", pmadapter->num_in_scan_table); LEAVE(); } /** * @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(mlan_private *pmpriv, 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_ext(pmadapter, pmadapter->pscan_table + del_idx, pmadapter->pscan_table + del_idx + 1, sizeof(BSSDescriptor_t), 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].posen_ie) { pmadapter->pscan_table[del_idx].posen_ie = (IEEEtypes_Generic_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .osen_offset); } if (pmadapter->pscan_table[del_idx].pmd_ie) { pmadapter->pscan_table[del_idx].pmd_ie = (IEEEtypes_MobilityDomain_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .md_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); } if (pmadapter->pscan_table[del_idx].pvht_cap) { pmadapter->pscan_table[del_idx].pvht_cap = (IEEEtypes_VHTCap_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .vht_cap_offset); } if (pmadapter->pscan_table[del_idx].pvht_oprat) { pmadapter->pscan_table[del_idx].pvht_oprat = (IEEEtypes_VHTOprat_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .vht_oprat_offset); } if (pmadapter->pscan_table[del_idx].pvht_txpower) { pmadapter->pscan_table[del_idx].pvht_txpower = (IEEEtypes_VHTtxpower_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .vht_txpower_offset); } if (pmadapter->pscan_table[del_idx].pext_pwer) { pmadapter->pscan_table[del_idx].pext_pwer = (IEEEtypes_ExtPwerCons_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .ext_pwer_offset); } if (pmadapter->pscan_table[del_idx].pext_bssload) { pmadapter->pscan_table[del_idx].pext_bssload = (IEEEtypes_ExtBSSload_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .ext_bssload_offset); } if (pmadapter->pscan_table[del_idx].pquiet_chan) { pmadapter->pscan_table[del_idx].pquiet_chan = (IEEEtypes_QuietChan_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .quiet_chan_offset); } if (pmadapter->pscan_table[del_idx].poper_mode) { pmadapter->pscan_table[del_idx].poper_mode = (IEEEtypes_OperModeNtf_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .oper_mode_offset); } if (pmadapter->pscan_table[del_idx].phe_cap) { pmadapter->pscan_table[del_idx].phe_cap = (IEEEtypes_HECap_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .he_cap_offset); } if (pmadapter->pscan_table[del_idx].phe_oprat) { pmadapter->pscan_table[del_idx].phe_oprat = (IEEEtypes_Extension_t *)(pmadapter ->pscan_table[del_idx] .pbeacon_buf + pmadapter ->pscan_table[del_idx] .he_oprat_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 entry's age out * * @param pmpriv A pointer to mlan_private structure * * @return N/A */ static void wlan_scan_delete_ageout_entry(mlan_private *pmpriv) { BSSDescriptor_t *pbss_entry; mlan_adapter *pmadapter = pmpriv->adapter; t_s32 table_idx = pmadapter->num_in_scan_table - 1; t_u32 i = 0; t_u32 age_in_secs = 0; t_u32 age_ts_usec = 0; ENTER(); #define SCAN_RESULT_AGEOUT 10 pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &age_in_secs, &age_ts_usec); for (i = 0; i < pmadapter->num_in_scan_table; i++) { pbss_entry = &pmadapter->pscan_table[table_idx]; if (age_in_secs > (pbss_entry->age_in_secs + SCAN_RESULT_AGEOUT)) { PRINTM(MCMND, "SCAN: ageout AP MAC Addr-" MACSTR " ssid: %-32s\n", MAC2STR(pbss_entry->mac_address), pbss_entry->ssid.ssid); wlan_scan_delete_table_entry(pmpriv, table_idx); } table_idx--; } LEAVE(); return; } /** * @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(mlan_private *pmpriv, 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(mlan_private *pmpriv, t_u32 index, t_u32 mode) { mlan_adapter *pmadapter = pmpriv->adapter; BSSDescriptor_t *pbss_desc; ENTER(); pbss_desc = &pmadapter->pscan_table[index]; /* Don't check for compatibility if roaming */ if ((pmpriv->media_connected == MTRUE) && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) && (pbss_desc->bss_mode == MLAN_BSS_MODE_INFRA)) { LEAVE(); return index; } pbss_desc->disable_11n = MFALSE; /* if the HE CAP IE exists, HT CAP IE should exist too */ /* 2.4G AX AP, don't have VHT CAP */ if (pbss_desc->phe_cap && !pbss_desc->pht_cap) { PRINTM(MINFO, "Disable 11n if VHT CAP/HT CAP IE is not found from the 11AX AP\n"); pbss_desc->disable_11n = MTRUE; } /* if the VHT CAP IE exists, the HT CAP IE should exist too */ if (pbss_desc->pvht_cap && !pbss_desc->pht_cap) { PRINTM(MINFO, "Disable 11n if HT CAP IE is not found from the 11AC AP\n"); pbss_desc->disable_11n = MTRUE; } 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 (pmpriv->sec_info.osen_enabled && pbss_desc->posen_ie && ((*(pbss_desc->posen_ie)).ieee_hdr.element_id == VENDOR_SPECIFIC_221)) { /* Hotspot 2.0 OSEN AKM */ PRINTM(MMSG, "Return success directly in Hotspot OSEN: index=%d " "encryption_mode=%#x\n", index, pmpriv->sec_info.encryption_mode); LEAVE(); return index; } if ((pbss_desc->bss_mode == mode) && (pmpriv->sec_info.ewpa_enabled == MTRUE #ifdef DRV_EMBEDDED_SUPPLICANT || supplicantIsEnabled(pmpriv->psapriv) #endif )) { 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) && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_GCMP) && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_GCMP_256) && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_CCMP_256)) { 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->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 && pbss_desc->privacy) { /* Static WEP enabled */ PRINTM(MINFO, "Disable 11n in WEP mode\n"); pbss_desc->disable_11n = MTRUE; /* Reject the following cases: */ /* * case 1: RSN IE w/o WEP OUI and WPA IE w/o WEP OUI * case 2: RSN IE w/o WEP OUI and No WPA IE * case 3: WPA IE w/o WEP OUI and No RSN IE */ if (((pbss_desc->prsn_ie) && ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE)) || ((pbss_desc->pwpa_ie) && ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE))) { if (!is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_WEP40) && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_WEP104) && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_WEP40) && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_WEP104)) index = -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)) /* * Privacy bit may NOT be set in some APs like * LinkSys WRT54G && pbss_desc->privacy */ ) { 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)) /* * 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) && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_GCMP) && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_GCMP_256) && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, CIPHER_SUITE_CCMP_256)) { 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 if (is_rsn_oui_present_in_wpa_ie( pmpriv, CIPHER_SUITE_CCMP) || is_rsn_oui_present_in_wpa_ie( pmpriv, CIPHER_SUITE_GCMP) || is_rsn_oui_present_in_wpa_ie( pmpriv, CIPHER_SUITE_GCMP_256) || is_rsn_oui_present_in_wpa_ie( pmpriv, CIPHER_SUITE_CCMP_256)) { LEAVE(); return index; } 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->sec_info.encryption_mode != MLAN_ENCRYPTION_MODE_NONE && pbss_desc->privacy) { /* Dynamic WEP enabled */ pbss_desc->disable_11n = MTRUE; 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(pmlan_adapter pmadapter) { t_u8 i = 0; 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; for (i = 0; i < pmadapter->num_in_chan_stats; i++) pmadapter->pchan_stats[i].cca_scan_duration = 0; pmadapter->idx_chan_stats = 0; 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(mlan_private *pmpriv, t_void *pioctl_buf, 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; t_u8 i; 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; } else { wlan_scan_delete_ageout_entry(pmpriv); } for (i = 0; i < pmadapter->num_in_chan_stats; i++) pmadapter->pchan_stats[i].cca_scan_duration = 0; pmadapter->idx_chan_stats = 0; 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) { wlan_request_cmd_lock(pmadapter); if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->scan_pending_q, MNULL, MNULL)) { pcmd_node = (cmd_ctrl_node *)util_dequeue_list( pmadapter->pmoal_handle, &pmadapter->scan_pending_q, MNULL, MNULL); pmadapter->pscan_ioctl_req = pioctl_req; pmadapter->scan_processing = MTRUE; wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); } wlan_release_cmd_lock(pmadapter); } 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(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd, 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_ext(pmpriv->adapter, pscan_cmd->bssid, pscan_cfg->specific_bssid, sizeof(pscan_cmd->bssid), sizeof(pscan_cmd->bssid)); memcpy_ext(pmpriv->adapter, pscan_cmd->tlv_buffer, pscan_cfg->tlv_buf, pscan_cfg->tlv_buf_len, 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 Check if any hidden SSID found in passive scan channels * and do specific SSID active scan for those channels * * @param pmpriv A pointer to mlan_private structure * @param pioctl_buf A pointer to mlan_ioctl_req structure * * @return MTRUE/MFALSE */ static t_bool wlan_active_scan_req_for_passive_chan(mlan_private *pmpriv, mlan_ioctl_req *pioctl_buf) { t_bool ret = MFALSE; mlan_adapter *pmadapter = pmpriv->adapter; t_bool scan_reqd = MFALSE; t_bool chan_listed = MFALSE; t_u8 id = 0; t_u32 bss_idx, i; t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = {0}; mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; wlan_user_scan_cfg *user_scan_cfg; mlan_ds_scan *pscan = (mlan_ds_scan *)pioctl_buf->pbuf; mlan_scan_req *pscan_req = MNULL; wlan_user_scan_cfg *puser_scan_in = MNULL; ENTER(); if (pscan->sub_command == MLAN_OID_SCAN_USER_CONFIG) { puser_scan_in = (wlan_user_scan_cfg *) pscan->param.user_scan.scan_cfg_buf; if (!puser_scan_in->ssid_filter) goto done; } if (pmadapter->active_scan_triggered) { pmadapter->active_scan_triggered = MFALSE; goto done; } if ((pcb->moal_malloc(pmadapter->pmoal_handle, sizeof(wlan_user_scan_cfg), MLAN_MEM_DEF, (t_u8 **)&user_scan_cfg) != MLAN_STATUS_SUCCESS) || !user_scan_cfg) { PRINTM(MERROR, "Memory allocation for user_scan_cfg failed\n"); goto done; } memset(pmadapter, user_scan_cfg, 0, sizeof(wlan_user_scan_cfg)); for (bss_idx = 0; bss_idx < pmadapter->num_in_scan_table; bss_idx++) { scan_reqd = MFALSE; if (!memcmp(pmadapter, pmadapter->pscan_table[bss_idx].ssid.ssid, null_ssid, pmadapter->pscan_table[bss_idx].ssid.ssid_len)) { if (puser_scan_in && puser_scan_in->chan_list[0].chan_number) { for (i = 0; i < WLAN_USER_SCAN_CHAN_MAX && puser_scan_in->chan_list[i].chan_number; i++) { if (puser_scan_in->chan_list[i] .chan_number == pmadapter->pscan_table[bss_idx] .channel) { if (puser_scan_in->chan_list[i] .scan_type == MLAN_SCAN_TYPE_PASSIVE) scan_reqd = MTRUE; break; } } } else if (pmadapter->scan_type == MLAN_SCAN_TYPE_PASSIVE) { scan_reqd = MTRUE; } else { if ((pmadapter->pscan_table[bss_idx].bss_band & BAND_A) && wlan_11h_radar_detect_required( pmpriv, pmadapter->pscan_table[bss_idx] .channel)) scan_reqd = MTRUE; if (pmadapter->pscan_table[bss_idx].bss_band & (BAND_B | BAND_G) && wlan_bg_scan_type_is_passive( pmpriv, pmadapter->pscan_table[bss_idx] .channel)) scan_reqd = MTRUE; } if (scan_reqd) { chan_listed = MFALSE; for (i = 0; i < id; i++) { if ((user_scan_cfg->chan_list[i] .chan_number == pmadapter->pscan_table[bss_idx] .channel) && (user_scan_cfg->chan_list[i] .radio_type & pmadapter->pscan_table[bss_idx] .bss_band)) { chan_listed = MTRUE; break; } } if (chan_listed == MTRUE) continue; user_scan_cfg->chan_list[id].chan_number = pmadapter->pscan_table[bss_idx].channel; if (pmadapter->pscan_table[bss_idx].bss_band & (BAND_B | BAND_G)) user_scan_cfg->chan_list[id].radio_type = BAND_2GHZ; if (pmadapter->pscan_table[bss_idx].bss_band & BAND_A) user_scan_cfg->chan_list[id].radio_type = BAND_5GHZ; user_scan_cfg->chan_list[id].scan_type = MLAN_SCAN_TYPE_ACTIVE; id++; } } } if (id) { pmadapter->active_scan_triggered = MTRUE; if (pscan->sub_command == MLAN_OID_SCAN_USER_CONFIG) { memcpy_ext(pmpriv->adapter, user_scan_cfg->ssid_list, puser_scan_in->ssid_list, sizeof(user_scan_cfg->ssid_list), sizeof(user_scan_cfg->ssid_list)); } else { pscan_req = &pscan->param.scan_req; memcpy_ext(pmpriv->adapter, user_scan_cfg->ssid_list[0].ssid, pscan_req->scan_ssid.ssid, pscan_req->scan_ssid.ssid_len, MLAN_MAX_SSID_LENGTH); } user_scan_cfg->keep_previous_scan = MTRUE; if (MLAN_STATUS_SUCCESS != wlan_scan_networks(pmpriv, pioctl_buf, user_scan_cfg)) { goto done; } ret = MTRUE; } if (user_scan_cfg) pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)user_scan_cfg); done: LEAVE(); return ret; } /** * @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(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp, t_void *pioctl_buf) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_adapter *pmadapter = pmpriv->adapter; mlan_callbacks *pcb = MNULL; 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; MrvlIEtypes_ChannelStats_t *pchanstats_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_u16 band; t_u8 is_bgscan_resp; t_u32 age_ts_usec; t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = {0}; t_u32 status_code = 0; pmlan_ioctl_req pscan_ioctl_req = MNULL; 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); 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); /* Update the age_in_second */ pmadapter->callbacks.moal_get_system_time( pmadapter->pmoal_handle, &pmadapter->age_in_secs, &age_ts_usec); 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); if (is_bgscan_resp) tlv_buf_size -= sizeof( resp->params.bg_scan_query_resp.report_condition); 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); wlan_ret_802_11_scan_get_tlv_ptrs( pmadapter, ptlv, tlv_buf_size, TLV_TYPE_CHANNEL_STATS, (MrvlIEtypes_Data_t **)&pchanstats_tlv); if (pchanstats_tlv) wlan_update_chan_statistics(pmpriv, pchanstats_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"); 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, MFALSE) == MLAN_STATUS_SUCCESS) { PRINTM(MINFO, "SCAN_RESP: BSSID = " MACSTR "\n", MAC2STR(bss_new_entry->mac_address)); band = BAND_G; if (pchan_band_tlv) { pchan_band = &pchan_band_tlv->chan_band_param[idx]; band = radio_type_to_band( pchan_band->bandcfg.chanBand); if (!bss_new_entry->channel) bss_new_entry->channel = pchan_band->chan_number; } /* * Save the band designation for this entry * for use in join */ bss_new_entry->bss_band = band; bss_new_entry->age_in_secs = pmadapter->age_in_secs; cfp = wlan_find_cfp_by_band_and_channel( pmadapter, 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; /* Skip entry if on blacklisted channel */ if (cfp && cfp->dynamic.blacklist) { PRINTM(MINFO, "SCAN_RESP: dropping entry on blacklist channel.\n"); continue; } /* * 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 SSID is NULL for same BSSID * keep the bss_idx set to this entry * so we replace the old contents in * the table */ if (!memcmp(pmadapter, pmadapter ->pscan_table[bss_idx] .ssid.ssid, null_ssid, pmadapter ->pscan_table[bss_idx] .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++; } else { if ((bss_new_entry->channel != pmadapter->pscan_table[bss_idx].channel) && (bss_new_entry->rssi > pmadapter->pscan_table[bss_idx].rssi)) { PRINTM(MCMND, "skip update the duplicate entry with low rssi\n"); continue; } } /* * 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_ext( pmpriv->adapter, &tsf_val, &ptsf_tlv->tsf_data[idx * TSF_DATA_SIZE], sizeof(tsf_val), sizeof(tsf_val)); tsf_val = wlan_le64_to_cpu(tsf_val); memcpy_ext(pmpriv->adapter, &bss_new_entry->network_tsf, &tsf_val, sizeof(bss_new_entry->network_tsf), sizeof(bss_new_entry->network_tsf)); } /* Copy the locally created bss_new_entry to the scan * table */ memcpy_ext(pmadapter, &pmadapter->pscan_table[bss_idx], bss_new_entry, sizeof(pmadapter->pscan_table[bss_idx]), sizeof(pmadapter->pscan_table[bss_idx])); } else { /* Error parsing/interpreting the scan response, skipped */ PRINTM(MERROR, "SCAN_RESP: wlan_interpret_bss_desc_with_ie returned error\n"); } } PRINTM(MINFO, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", pscan_rsp->number_of_sets, num_in_table - pmadapter->num_in_scan_table, num_in_table); /* Update the total number of BSSIDs in the scan table */ pmadapter->num_in_scan_table = num_in_table; if (is_bgscan_resp) goto done; wlan_request_cmd_lock(pmadapter); if (!util_peek_list(pmadapter->pmoal_handle, &pmadapter->scan_pending_q, MNULL, MNULL)) { wlan_release_cmd_lock(pmadapter); if (pmadapter->pscan_ioctl_req) { if (((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) ->sub_command == MLAN_OID_SCAN_SPECIFIC_SSID || ((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) ->sub_command == MLAN_OID_SCAN_USER_CONFIG) { if (wlan_active_scan_req_for_passive_chan( pmpriv, pmadapter->pscan_ioctl_req)) { goto done; } } } /* * Process the resulting scan table: * - Remove any bad ssids * - Update our current BSS information from scan data */ wlan_scan_process_results(pmpriv); wlan_request_cmd_lock(pmadapter); pmadapter->scan_processing = MFALSE; pscan_ioctl_req = pmadapter->pscan_ioctl_req; pmadapter->pscan_ioctl_req = MNULL; /* Need to indicate IOCTL complete */ if (pscan_ioctl_req) { pscan_ioctl_req->status_code = MLAN_ERROR_NO_ERROR; /* Indicate ioctl complete */ pcb->moal_ioctl_complete( pmadapter->pmoal_handle, (pmlan_ioctl_req)pscan_ioctl_req, MLAN_STATUS_SUCCESS); } wlan_release_cmd_lock(pmadapter); pmadapter->bgscan_reported = MFALSE; 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) { wlan_release_cmd_lock(pmadapter); status_code = MLAN_ERROR_FW_NOT_READY; ret = MLAN_STATUS_FAILURE; goto done; } 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, MNULL, MNULL); wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); wlan_release_cmd_lock(pmadapter); } } done: if (bss_new_entry) pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)bss_new_entry); if (ret) { /* Flush all pending scan commands */ wlan_flush_scan_queue(pmadapter); wlan_request_cmd_lock(pmadapter); pmadapter->scan_processing = MFALSE; pscan_ioctl_req = pmadapter->pscan_ioctl_req; pmadapter->pscan_ioctl_req = MNULL; if (pscan_ioctl_req) { pscan_ioctl_req->status_code = status_code; /* Indicate ioctl complete */ pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pscan_ioctl_req, MLAN_STATUS_FAILURE); } wlan_release_cmd_lock(pmadapter); } 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(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd, 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(); pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); if (pmpriv->adapter->ext_scan_enh == MTRUE) { if (pdata_buf) { if (pmpriv->adapter->ext_scan_type == EXT_SCAN_ENHANCE) pext_scan_cmd->ext_scan_type = EXT_SCAN_ENHANCE; else pext_scan_cmd->ext_scan_type = EXT_SCAN_DEFAULT; } else { pcmd->size = wlan_cpu_to_le16((t_u16)( sizeof(pext_scan_cmd->ext_scan_type) + (t_u16)(sizeof(pext_scan_cmd->reserved)) + S_DS_GEN)); pext_scan_cmd->ext_scan_type = EXT_SCAN_CANCEL; LEAVE(); return MLAN_STATUS_SUCCESS; } } if (!pdata_buf) { PRINTM(MERROR, "wlan_cmd_802_11_scan_ext: pdata_buf is null\n"); LEAVE(); return MLAN_STATUS_FAILURE; } pscan_cfg = (wlan_scan_cmd_config *)pdata_buf; memcpy_ext(pmpriv->adapter, pext_scan_cmd->tlv_buffer, pscan_cfg->tlv_buf, pscan_cfg->tlv_buf_len, pscan_cfg->tlv_buf_len); /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ pcmd->size = wlan_cpu_to_le16( (t_u16)(sizeof(pext_scan_cmd->ext_scan_type) + (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(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp, t_void *pioctl_buf) { HostCmd_DS_802_11_SCAN_EXT *pext_scan_cmd = &(resp->params.ext_scan); MrvlIEtypesHeader_t *tlv = MNULL; MrvlIEtypes_ChannelStats_t *tlv_chanstats = MNULL; t_u16 tlv_buf_left = 0; t_u16 tlv_type = 0; t_u16 tlv_len = 0; t_u32 ext_scan_type; mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; pmlan_ioctl_req pioctl_req = (pmlan_ioctl_req)pioctl_buf; mlan_adapter *pmadapter = pmpriv->adapter; ENTER(); PRINTM(MINFO, "EXT scan returns successfully\n"); ext_scan_type = pext_scan_cmd->ext_scan_type; if (ext_scan_type == EXT_SCAN_CANCEL) { PRINTM(MCMND, "Cancel scan command completed!\n"); wlan_request_cmd_lock(pmadapter); pmadapter->scan_processing = MFALSE; pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; wlan_release_cmd_lock(pmadapter); /* Need to indicate IOCTL complete */ if (pioctl_req != MNULL) { pioctl_req->status_code = MLAN_STATUS_SUCCESS; /* Indicate ioctl complete */ pcb->moal_ioctl_complete(pmadapter->pmoal_handle, (pmlan_ioctl_req)pioctl_req, MLAN_STATUS_SUCCESS); } LEAVE(); return MLAN_STATUS_SUCCESS; } else if (ext_scan_type == EXT_SCAN_ENHANCE) { /* Setup the timer after scan command response */ pcb->moal_start_timer(pmpriv->adapter->pmoal_handle, pmpriv->adapter->pmlan_cmd_timer, MFALSE, MRVDRV_TIMER_10S * 2); pmpriv->adapter->cmd_timer_is_set = MTRUE; LEAVE(); return MLAN_STATUS_SUCCESS; } tlv = (MrvlIEtypesHeader_t *)pext_scan_cmd->tlv_buffer; tlv_buf_left = resp->size - (sizeof(HostCmd_DS_802_11_SCAN_EXT) - 1 + S_DS_GEN); while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { tlv_type = wlan_le16_to_cpu(tlv->type); tlv_len = wlan_le16_to_cpu(tlv->len); if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { PRINTM(MERROR, "Error processing scan gap TLV\n"); break; } switch (tlv_type) { case TLV_TYPE_CHANNEL_STATS: tlv_chanstats = (MrvlIEtypes_ChannelStats_t *)tlv; wlan_update_chan_statistics(pmpriv, tlv_chanstats); break; default: break; } tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + sizeof(MrvlIEtypesHeader_t)); } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function add a new entry to scan_table * * @param pmpriv A pointer to mlan_private structure * @param bss_new_entry A pointer to the bss_new_entry * @param num_in_tbl number of scan entry in scan_table * * @return N/A */ static t_void wlan_add_new_entry_to_scan_table(mlan_private *pmpriv, BSSDescriptor_t *bss_new_entry, t_u32 *num_in_tbl) { mlan_adapter *pmadapter = pmpriv->adapter; t_u32 bss_idx; t_u32 num_in_table = *num_in_tbl; t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = {0}; /* * 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 SSID is NULL for same BSSID * keep the bss_idx set to this entry * so we replace the old contents in * the table */ if (!memcmp(pmadapter, pmadapter->pscan_table[bss_idx].ssid.ssid, null_ssid, pmadapter->pscan_table[bss_idx] .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++; } else { if ((bss_new_entry->channel != pmadapter->pscan_table[bss_idx].channel) && (bss_new_entry->rssi > pmadapter->pscan_table[bss_idx].rssi)) { PRINTM(MCMND, "skip update the duplicate entry with low rssi\n"); return; } } /* * 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--; goto done; } else { /* Copy the locally created bss_new_entry to the scan table */ memcpy_ext(pmadapter, &pmadapter->pscan_table[bss_idx], bss_new_entry, sizeof(pmadapter->pscan_table[bss_idx]), sizeof(pmadapter->pscan_table[bss_idx])); } done: *num_in_tbl = num_in_table; return; } /** 8 bytes timestamp, 2 bytest interval, 2 bytes capability */ #define BEACON_FIX_SIZE 12 /** * @brief This function realloc the beacon buffer and update ssid for new entry * * @param pmadpater A pointer to mlan_adapter structure * @param pbss_entry A pointer to the bss_entry which has multi-bssid IE * @param pnew_entry A pinter to new entry * @param pssid A pointer to ssid IE * * @return MLAN_STATUS_FAILURE/MLAN_STATUS_SUCCESS */ static mlan_status wlan_update_ssid_in_beacon_buf( mlan_adapter *pmadapter, BSSDescriptor_t *pbss_entry, BSSDescriptor_t *pnew_entry, IEEEtypes_Ssid_t *pssid, IEEEtypes_ExtCap_t *pnew_extcap, IEEEtypes_Generic_t *pnew_rsnx) { mlan_callbacks *pcb = (pmlan_callbacks)&pmadapter->callbacks; t_u8 *pbeacon_buf = MNULL; t_u32 beacon_buf_size = 0; t_s8 offset = pnew_entry->ssid.ssid_len - pbss_entry->ssid.ssid_len; IEEEtypes_ExtCap_t *pextcap; mlan_status ret = MLAN_STATUS_FAILURE; t_u32 rsnx_offset = 0; if (pnew_entry->ssid.ssid_len >= pbss_entry->ssid.ssid_len) beacon_buf_size = pbss_entry->beacon_buf_size + (pnew_entry->ssid.ssid_len - pbss_entry->ssid.ssid_len); else beacon_buf_size = pbss_entry->beacon_buf_size - (pbss_entry->ssid.ssid_len - pnew_entry->ssid.ssid_len); rsnx_offset = beacon_buf_size; if (pnew_rsnx) beacon_buf_size += pnew_rsnx->ieee_hdr.len + sizeof(IEEEtypes_Header_t); ret = pcb->moal_malloc(pmadapter->pmoal_handle, beacon_buf_size, MLAN_MEM_DEF, (t_u8 **)&pbeacon_buf); if (ret != MLAN_STATUS_SUCCESS || !pbeacon_buf) { PRINTM(MERROR, "Memory allocation for beacon buf for bss_new_entry\n"); goto done; } pnew_entry->beacon_buf_size = beacon_buf_size; pnew_entry->pbeacon_buf = pbeacon_buf; /** copy fixed IE */ memcpy_ext(pmadapter, pbeacon_buf, pbss_entry->pbeacon_buf, BEACON_FIX_SIZE, BEACON_FIX_SIZE); /** copy new ssid ie */ memcpy_ext(pmadapter, pbeacon_buf + BEACON_FIX_SIZE, (t_u8 *)pssid, pssid->len + sizeof(IEEEtypes_Header_t), pssid->len + sizeof(IEEEtypes_Header_t)); /** copy left IE to new beacon buffer */ memcpy_ext(pmadapter, pbeacon_buf + BEACON_FIX_SIZE + pssid->len + sizeof(IEEEtypes_Header_t), pbss_entry->pbeacon_buf + BEACON_FIX_SIZE + pbss_entry->ssid.ssid_len + sizeof(IEEEtypes_Header_t), pbss_entry->beacon_buf_size - BEACON_FIX_SIZE - (pbss_entry->ssid.ssid_len + sizeof(IEEEtypes_Header_t)), pbss_entry->beacon_buf_size - BEACON_FIX_SIZE - (pbss_entry->ssid.ssid_len + sizeof(IEEEtypes_Header_t))); /* adjust the ie pointer */ if (pnew_entry->pwpa_ie) pnew_entry->wpa_offset += offset; if (pnew_entry->prsn_ie) pnew_entry->rsn_offset += offset; if (pnew_entry->pwapi_ie) pnew_entry->wapi_offset += offset; if (pnew_entry->posen_ie) pnew_entry->osen_offset += offset; if (pnew_entry->pmd_ie) pnew_entry->md_offset += offset; if (pnew_entry->pht_cap) pnew_entry->ht_cap_offset += offset; if (pnew_entry->pht_info) pnew_entry->ht_info_offset += offset; if (pnew_entry->pbss_co_2040) pnew_entry->bss_co_2040_offset += offset; if (pnew_entry->pext_cap) { pnew_entry->ext_cap_offset += offset; if (pnew_extcap) { pextcap = (IEEEtypes_ExtCap_t *)(pnew_entry->pbeacon_buf + pnew_entry->ext_cap_offset); memcpy_ext(pmadapter, pbeacon_buf + pnew_entry->ext_cap_offset, (t_u8 *)pnew_extcap, pnew_extcap->ieee_hdr.len + sizeof(IEEEtypes_Header_t), pextcap->ieee_hdr.len + sizeof(IEEEtypes_Header_t)); } } if (pnew_entry->poverlap_bss_scan_param) pnew_entry->overlap_bss_offset += offset; if (pnew_entry->pvht_cap) pnew_entry->vht_cap_offset += offset; if (pnew_entry->pvht_oprat) pnew_entry->vht_oprat_offset += offset; if (pnew_entry->pvht_txpower) pnew_entry->vht_txpower_offset += offset; if (pnew_entry->pext_pwer) pnew_entry->ext_pwer_offset += offset; if (pnew_entry->pext_bssload) pnew_entry->ext_bssload_offset += offset; if (pnew_entry->pquiet_chan) pnew_entry->quiet_chan_offset += offset; if (pnew_entry->poper_mode) pnew_entry->oper_mode_offset += offset; if (pnew_entry->phe_cap) pnew_entry->he_cap_offset += offset; if (pnew_entry->phe_oprat) pnew_entry->he_oprat_offset += offset; if (pnew_rsnx) memcpy_ext( pmadapter, pbeacon_buf + rsnx_offset, (t_u8 *)pnew_rsnx, pnew_rsnx->ieee_hdr.len + sizeof(IEEEtypes_Header_t), pnew_rsnx->ieee_hdr.len + sizeof(IEEEtypes_Header_t)); DBG_HEXDUMP(MCMD_D, "MBSSID beacon buf", pbeacon_buf, beacon_buf_size); ret = MLAN_STATUS_SUCCESS; done: return ret; } /** * @brief This function generate the bssid from bssid_idx * * @param pmadpater A pointer to mlan_adapter structure * @param pbss_entry A pointer to the bss_entry which has multi-bssid IE * @param pnew_entry A pinter to new entry * @param bssid_index bssid_index from BSSID_IDX IE * * @return N/A */ static void wlan_gen_multi_bssid_by_bssid_index(pmlan_adapter pmadapter, BSSDescriptor_t *pbss_entry, BSSDescriptor_t *pnew_entry, t_u8 bssid_index, t_u8 max_bssid_indicator) { t_u8 mask = 0xff; t_u8 new_bssid[6]; t_u8 bssid_a; t_u8 src_bssid[6]; memcpy_ext(pmadapter, (t_u8 *)src_bssid, pbss_entry->mac_address, sizeof(mlan_802_11_mac_addr), sizeof(src_bssid)); memcpy_ext(pmadapter, (t_u8 *)new_bssid, (t_u8 *)&pbss_entry->mac_address, sizeof(mlan_802_11_mac_addr), sizeof(new_bssid)); mask = (mask >> (8 - max_bssid_indicator)); bssid_a = src_bssid[5] & (~mask); src_bssid[5] = (src_bssid[5] + bssid_index) & mask; new_bssid[5] = bssid_a | src_bssid[5]; memcpy_ext(pmadapter, (t_u8 *)&pnew_entry->mac_address, new_bssid, sizeof(new_bssid), sizeof(mlan_802_11_mac_addr)); memcpy_ext(pmadapter, (t_u8 *)&pnew_entry->multi_bssid_ap_addr, (t_u8 *)&pbss_entry->mac_address, sizeof(mlan_802_11_mac_addr), sizeof(mlan_802_11_mac_addr)); } /** * @brief This function parse the non_trans_bssid_profile * * @param pmadapter A pointer to mlan_adapter structure * @param pbss_entry A pointer to BSSDescriptor_t which has multi-bssid * IE * @param pbss_profile A pointer to IEEEtypes_NonTransBSSIDprofile_t * @param num_in_table A pointer to buffer to save num of entry in scan * table. * @param max_bssid_indicator max bssid indicator * * @return N/A */ static t_void wlan_parse_non_trans_bssid_profile( mlan_private *pmpriv, BSSDescriptor_t *pbss_entry, IEEEtypes_NonTransBSSIDProfile_t *pbss_profile, t_u32 *num_in_table, t_u8 max_bssid_indicator) { mlan_adapter *pmadapter = pmpriv->adapter; IEEEtypes_Header_t *pheader = (IEEEtypes_Header_t *)pbss_profile->profile_data; IEEEtypes_MultiBSSIDIndex_t *pbssid_index = MNULL; IEEEtypes_Ssid_t *pssid = MNULL; IEEEtypes_NotxBssCap_t *pcap = (IEEEtypes_NotxBssCap_t *)pbss_profile->profile_data; t_u8 *pos = pbss_profile->profile_data; t_s8 left_len = pbss_profile->ieee_hdr.len; t_u8 ret = MFALSE; mlan_callbacks *pcb = (pmlan_callbacks)&pmadapter->callbacks; BSSDescriptor_t *bss_new_entry = MNULL; t_u8 *pbeacon_buf = MNULL; IEEEtypes_ExtCap_t *pextcap = MNULL; IEEEtypes_Generic_t *prsnx = MNULL; ENTER(); /* The first element within the Nontransmitted * BSSID Profile is not the Nontransmitted * BSSID Capability element. */ if (pcap->element_id != NONTX_BSSID_CAP || pcap->len != 2) { PRINTM(MERROR, "The first element within the Nontransmitted BSSID Profile is not the NontransmittedBSSID Capability element\n"); LEAVE(); return; } while (left_len >= 2) { pheader = (IEEEtypes_Header_t *)pos; if ((t_s8)(pheader->len + sizeof(IEEEtypes_Header_t)) > left_len) { PRINTM(MMSG, "invalid IE length = %d left len %d\n", pheader->len, left_len); break; } switch (pheader->element_id) { case MBSSID_INDEX: pbssid_index = (IEEEtypes_MultiBSSIDIndex_t *)pos; if (pbssid_index->bssid_index == 0 || pbssid_index->bssid_index > 46) { PRINTM(MERROR, " No valid Multiple BSSID-Index element\n"); goto done; } PRINTM(MCMND, "MBSSID: Find mbssid_index=%d\n", pbssid_index->bssid_index); ret = MTRUE; break; case EXT_CAPABILITY: pextcap = (IEEEtypes_ExtCap_t *)pos; DBG_HEXDUMP(MCMD_D, "MBSSID extcap", pos, pextcap->ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case RSNX_IE: prsnx = (IEEEtypes_Generic_t *)pos; DBG_HEXDUMP(MCMD_D, "MBSSID RSNX", pos, prsnx->ieee_hdr.len + sizeof(IEEEtypes_Header_t)); break; case SSID: pssid = (IEEEtypes_Ssid_t *)pos; PRINTM(MCMND, "MBSSID: Find mbssid ssid=%s\n", pssid->ssid); break; default: break; } left_len -= pheader->len + sizeof(IEEEtypes_Header_t); pos += pheader->len + sizeof(IEEEtypes_Header_t); } if (ret == MTRUE) { 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"); goto done; } memcpy_ext(pmadapter, bss_new_entry, pbss_entry, sizeof(BSSDescriptor_t), sizeof(BSSDescriptor_t)); wlan_gen_multi_bssid_by_bssid_index(pmadapter, pbss_entry, bss_new_entry, pbssid_index->bssid_index, max_bssid_indicator); if (pssid) { memset(pmadapter, (t_u8 *)&bss_new_entry->ssid, 0, sizeof(mlan_802_11_ssid)); bss_new_entry->ssid.ssid_len = pssid->len; memcpy_ext(pmadapter, bss_new_entry->ssid.ssid, pssid->ssid, pssid->len, MLAN_MAX_SSID_LENGTH); if (MLAN_STATUS_SUCCESS != wlan_update_ssid_in_beacon_buf( pmadapter, pbss_entry, bss_new_entry, pssid, pextcap, prsnx)) { PRINTM(MERROR, "Fail to update MBSSID beacon buf\n"); pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)bss_new_entry); goto done; } pbeacon_buf = bss_new_entry->pbeacon_buf; } memcpy_ext(pmadapter, &bss_new_entry->cap_info, &pcap->cap, sizeof(IEEEtypes_CapInfo_t), sizeof(IEEEtypes_CapInfo_t)); bss_new_entry->multi_bssid_ap = MULTI_BSSID_SUB_AP; wlan_add_new_entry_to_scan_table(pmpriv, bss_new_entry, num_in_table); if (pssid && pbeacon_buf) pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pbeacon_buf); pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)bss_new_entry); } done: LEAVE(); return; } /** * @brief This function parse the multi_bssid IE from pbss_entry * * @param pmpriv A pointer to mlan_private structure * @param pbss_entry A pointer to BSSDescriptor_t which has multi-bssid * IE * @param num_in_table A pointer to buffer to save num of entry in scan * table. * * @return number entry in scan table */ static t_void wlan_parse_multi_bssid_ie(mlan_private *pmpriv, BSSDescriptor_t *pbss_entry, IEEEtypes_MultiBSSID_t *pmulti_bssid, t_u32 *num_in_table) { t_u32 bytes_left = 0; t_u8 *pcurrent_ptr = MNULL; IEEEtypes_NonTransBSSIDProfile_t *pbssid_profile = MNULL; if (!pmulti_bssid) return; bytes_left = pmulti_bssid->ieee_hdr.len - 1; pcurrent_ptr = pmulti_bssid->sub_elem_data; while (bytes_left >= 2) { pbssid_profile = (IEEEtypes_NonTransBSSIDProfile_t *)pcurrent_ptr; if (pbssid_profile->ieee_hdr.element_id != NONTRANS_BSSID_PROFILE_SUBELEM_ID) { PRINTM(MERROR, "Invalid multi-bssid IE\n"); break; } if (bytes_left < (t_u32)(pbssid_profile->ieee_hdr.len + 2)) { PRINTM(MERROR, "Invalid multi-bssid IE\n"); break; } wlan_parse_non_trans_bssid_profile( pmpriv, pbss_entry, pbssid_profile, num_in_table, pmulti_bssid->max_bssid_indicator); pcurrent_ptr += pbssid_profile->ieee_hdr.len + 2; bytes_left -= pbssid_profile->ieee_hdr.len + 2; } return; } /** * @brief This function search all the mbssid IE in the beacon buffer * * @param pmpriv A pointer to mlan_private structure * @param pbss_entry A pointer to BSSDescriptor_t which has multi-bssid * IE * @param num_in_table A pointer to buffer to save num of entry in scan * table. * * @return N/A */ static void wlan_parse_multi_bssid_ap(mlan_private *pmpriv, BSSDescriptor_t *pbss_entry, t_u32 *num_in_table) { IEEEtypes_ElementId_e element_id; t_u8 element_len; t_u16 total_ie_len; t_u32 bytes_left = pbss_entry->beacon_buf_size - BEACON_FIX_SIZE; t_u8 *pcurrent_ptr = pbss_entry->pbeacon_buf + BEACON_FIX_SIZE; IEEEtypes_Ssid_t *pssid = (IEEEtypes_Ssid_t *)pcurrent_ptr; if (pssid->element_id != SSID) { PRINTM(MERROR, "Invalid beacon ie, ssid should be in the first element\n"); return; } /* Process variable IE */ while (bytes_left >= 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 < total_ie_len) { PRINTM(MERROR, "InterpretIE: Error in processing IE, " "bytes left < IE length\n"); bytes_left = 0; continue; } if (element_id == MULTI_BSSID) wlan_parse_multi_bssid_ie( pmpriv, pbss_entry, (IEEEtypes_MultiBSSID_t *)pcurrent_ptr, num_in_table); pcurrent_ptr += total_ie_len; bytes_left -= total_ie_len; } return; } /** * @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(mlan_private *pmpriv, t_u8 number_of_sets, t_u8 *pscan_resp, 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 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_u16 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); /* Update the age_in_second */ pmadapter->callbacks.moal_get_system_time( pmadapter->pmoal_handle, &pmadapter->age_in_secs, &age_ts_usec); 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, MTRUE) == MLAN_STATUS_SUCCESS) { PRINTM(MINFO, "EXT_SCAN: BSSID = " MACSTR "\n", MAC2STR(bss_new_entry->mac_address)); 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_ext(pmpriv->adapter, &tsf_val, &pscan_info_tlv->tsf, sizeof(tsf_val), sizeof(tsf_val)); tsf_val = wlan_le64_to_cpu(tsf_val); memcpy_ext(pmpriv->adapter, &bss_new_entry->network_tsf, &tsf_val, sizeof(bss_new_entry->network_tsf), sizeof(bss_new_entry->network_tsf)); band = radio_type_to_band( pscan_info_tlv->bandcfg.chanBand); if (!bss_new_entry->channel) bss_new_entry->channel = pscan_info_tlv->channel; } /* Save the band designation for this entry for use in * join */ bss_new_entry->bss_band = band; bss_new_entry->age_in_secs = pmadapter->age_in_secs; cfp = wlan_find_cfp_by_band_and_channel( pmadapter, 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; /* Skip entry if on blacklisted channel */ if (cfp && cfp->dynamic.blacklist) { PRINTM(MINFO, "EXT_SCAN: dropping entry on blacklist channel.\n"); continue; } if (IS_FW_SUPPORT_MULTIBSSID(pmadapter)) { if (bss_new_entry->multi_bssid_ap == MULTI_BSSID_AP) wlan_parse_multi_bssid_ap( pmpriv, bss_new_entry, &num_in_table); } wlan_add_new_entry_to_scan_table(pmpriv, bss_new_entry, &num_in_table); } else { /* Error parsing/interpreting the scan response, skipped */ PRINTM(MERROR, "EXT_SCAN: wlan_interpret_bss_desc_with_ie returned error\n"); } } PRINTM(MCMND, "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(mlan_private *pmpriv, 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_le16_to_cpu(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 && (pmadapter->ext_scan_type != EXT_SCAN_ENHANCE)) { wlan_request_cmd_lock(pmadapter); if (!util_peek_list(pmadapter->pmoal_handle, &pmadapter->scan_pending_q, MNULL, MNULL)) { wlan_release_cmd_lock(pmadapter); if (pmadapter->pscan_ioctl_req) { if (((mlan_ds_scan *) pmadapter->pscan_ioctl_req->pbuf) ->sub_command == MLAN_OID_SCAN_SPECIFIC_SSID || ((mlan_ds_scan *) pmadapter->pscan_ioctl_req->pbuf) ->sub_command == MLAN_OID_SCAN_USER_CONFIG) { if (wlan_active_scan_req_for_passive_chan( pmpriv, pmadapter->pscan_ioctl_req)) { LEAVE(); return ret; } } } /* * Process the resulting scan table: * - Remove any bad ssids * - Update our current BSS information from scan data */ wlan_scan_process_results(pmpriv); wlan_request_cmd_lock(pmadapter); pmadapter->scan_processing = MFALSE; pioctl_req = pmadapter->pscan_ioctl_req; pmadapter->pscan_ioctl_req = MNULL; /* 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_release_cmd_lock(pmadapter); pmadapter->bgscan_reported = MFALSE; 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) { wlan_release_cmd_lock(pmadapter); /* Flush all pending scan commands */ wlan_flush_scan_queue(pmadapter); wlan_request_cmd_lock(pmadapter); pmadapter->scan_processing = MFALSE; pioctl_req = pmadapter->pscan_ioctl_req; pmadapter->pscan_ioctl_req = MNULL; /* 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); } wlan_release_cmd_lock(pmadapter); } 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, MNULL, MNULL); wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); wlan_release_cmd_lock(pmadapter); } } } LEAVE(); return ret; } /** * @brief This function handles the event extended scan status * * @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_status(mlan_private *pmpriv, mlan_buffer *pmbuf) { mlan_adapter *pmadapter = pmpriv->adapter; mlan_status ret = MLAN_STATUS_SUCCESS; mlan_event_scan_status *scan_event; mlan_ioctl_req *pioctl_req; mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; t_u16 tlv_buf_left, tlv_len, tlv_type; MrvlIEtypesHeader_t *tlv; MrvlIEtypes_ChannelStats_t *tlv_chan_stats; t_u8 status; cmd_ctrl_node *pcmd_node = MNULL; ENTER(); if (pmbuf->data_len < sizeof(mlan_event_scan_status)) { PRINTM(MERROR, "Wrong ext scan status event data length\n"); ret = MLAN_STATUS_FAILURE; goto done; } scan_event = (pmlan_event_scan_status)(pmbuf->pbuf + pmbuf->data_offset); DBG_HEXDUMP(MCMD_D, "EVENT: Ext_Scan_Status", scan_event, pmbuf->data_len); status = scan_event->scan_status; PRINTM(MEVENT, "ext_scan_status: status %d (scan %s), buf_len %d\n", status, status ? "cancelled" : "success", scan_event->buf_len); tlv = (MrvlIEtypesHeader_t *)scan_event->event_buf; tlv_buf_left = pmbuf->data_len - sizeof(mlan_event_scan_status); while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { tlv_type = wlan_le16_to_cpu(tlv->type); tlv_len = wlan_le16_to_cpu(tlv->len); if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { PRINTM(MERROR, "Error process scan gap tlv: length %d type 0x%x\n", tlv_len, tlv_type); ret = MLAN_STATUS_FAILURE; goto done; } switch (tlv_type) { case TLV_TYPE_CHANNEL_STATS: tlv_chan_stats = (MrvlIEtypes_ChannelStats_t *)tlv; wlan_update_chan_statistics(pmpriv, tlv_chan_stats); break; default: break; } tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + sizeof(MrvlIEtypesHeader_t)); } done: wlan_request_cmd_lock(pmadapter); if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->scan_pending_q, MNULL, MNULL)) { /* If firmware not ready, do not issue any more scan * commands */ if (pmadapter->hw_status != WlanHardwareStatusReady) { wlan_release_cmd_lock(pmadapter); /* Flush all pending scan commands */ wlan_flush_scan_queue(pmadapter); wlan_request_cmd_lock(pmadapter); pmadapter->scan_processing = MFALSE; pioctl_req = pmadapter->pscan_ioctl_req; pmadapter->pscan_ioctl_req = MNULL; /* 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); } wlan_release_cmd_lock(pmadapter); } 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, MNULL, MNULL); wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); wlan_release_cmd_lock(pmadapter); } LEAVE(); return ret; } wlan_release_cmd_lock(pmadapter); /* Now we got response from FW, cancel the command timer */ if (!pmadapter->curr_cmd && pmadapter->cmd_timer_is_set) { /* Cancel command timeout timer */ pcb->moal_stop_timer(pmadapter->pmoal_handle, pmadapter->pmlan_cmd_timer); /* Cancel command timeout timer */ pmadapter->cmd_timer_is_set = MFALSE; } if (pmadapter->pscan_ioctl_req) { if (((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) ->sub_command == MLAN_OID_SCAN_SPECIFIC_SSID || ((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) ->sub_command == MLAN_OID_SCAN_USER_CONFIG) { if (wlan_active_scan_req_for_passive_chan( pmpriv, pmadapter->pscan_ioctl_req)) { LEAVE(); return ret; } } } /* * Process the resulting scan table: * - Remove any bad ssids * - Update our current BSS information from scan data */ wlan_scan_process_results(pmpriv); /** Complete scan ioctl */ wlan_request_cmd_lock(pmadapter); pmadapter->scan_processing = MFALSE; pioctl_req = pmadapter->pscan_ioctl_req; pmadapter->pscan_ioctl_req = MNULL; /* 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, pioctl_req, MLAN_STATUS_SUCCESS); } wlan_release_cmd_lock(pmadapter); wlan_move_cmd_to_cmd_pending_q(pmadapter); pmadapter->bgscan_reported = MFALSE; wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); 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(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd, 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(mlan_private *pmpriv, 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; t_u16 band; 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 ((pbg_scan_in && (pbg_scan_in->bss_type == MLAN_SCAN_MODE_IBSS)) || pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) band = pmadapter->adhoc_start_band; else band = pmpriv->config_bands; if (!wlan_is_band_compatible(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; switch (pscan_region->band) { case BAND_A: tlv_chan_list->chan_scan_param[chan_idx] .bandcfg.chanBand = BAND_5GHZ; /* Passive scan 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: if (wlan_bg_scan_type_is_passive( pmpriv, (t_u8)cfp->channel)) scan_type = MLAN_SCAN_TYPE_PASSIVE; tlv_chan_list->chan_scan_param[chan_idx] .bandcfg.chanBand = BAND_2GHZ; break; default: tlv_chan_list->chan_scan_param[chan_idx] .bandcfg.chanBand = BAND_2GHZ; 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; } } 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(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd, 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; MrvlIEtypes_StartLater_t *tlv_start_later = MNULL; MrvlIEtypes_RepeatCount_t *tlv_repeat = MNULL; MrvlIEtypes_EESParamSet_t *tlv_ees_cfg = MNULL; MrvlIEtype_EESNetworkCfg_t *tlv_ees_net_cfg = MNULL; MrvlIEtypes_Cipher_t *tlv_ees_cipher = MNULL; MrvlIEtypes_SsIdParamSet_t *tlv_ssid = MNULL; MrvlIETypes_HTCap_t *pht_cap = MNULL; MrvlIETypes_VHTCap_t *pvht_cap = MNULL; MrvlIEtypes_Extension_t *phe_cap = MNULL; t_u16 len = 0; t_u8 index; 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; t_u16 band; const t_u8 zero_mac[6] = {0, 0, 0, 0, 0, 0}; 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->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); } if (bg_scan_in->repeat_count) { tlv_repeat = (MrvlIEtypes_RepeatCount_t *)tlv; tlv_repeat->header.type = wlan_cpu_to_le16(TLV_TYPE_REPEAT_COUNT); tlv_repeat->header.len = wlan_cpu_to_le16(sizeof(MrvlIEtypes_RepeatCount_t) - sizeof(MrvlIEtypesHeader_t)); tlv_repeat->repeat_count = wlan_cpu_to_le16(bg_scan_in->repeat_count); tlv += sizeof(MrvlIEtypes_RepeatCount_t); cmd_size += sizeof(MrvlIEtypes_RepeatCount_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( (char *)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_ext(pmadapter, pwildcard_ssid_tlv->ssid, bg_scan_in->ssid_list[ssid_idx].ssid, ssid_len, MLAN_MAX_SSID_LENGTH); 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 (bg_scan_in->bss_type == MLAN_SCAN_MODE_IBSS || pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) band = pmadapter->adhoc_start_band; else band = pmpriv->config_bands; if (!wlan_is_band_compatible( 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 == BAND_5GHZ) { if (wlan_11h_radar_detect_required( pmpriv, bg_scan_in->chan_list[chan_idx] .chan_number)) { scan_type = MLAN_SCAN_TYPE_PASSIVE; } } if (radio_type == BAND_2GHZ) { if (wlan_bg_scan_type_is_passive( 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] .bandcfg.chanBand = 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; } if (bg_scan_in->chan_per_scan) { bg_scan->chan_per_scan = bg_scan_in->chan_per_scan; } else { if (bg_scan_in->report_condition & BG_SCAN_WAIT_ALL_CHAN_DONE) bg_scan->chan_per_scan = chan_num; else bg_scan->chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; } if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info) && (pmpriv->config_bands & BAND_GN || pmpriv->config_bands & BAND_AN)) { pht_cap = (MrvlIETypes_HTCap_t *)tlv; 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->config_bands, MTRUE); DBG_HEXDUMP(MCMD_D, "BGSCAN: HT_CAPABILITIES IE", (t_u8 *)pht_cap, sizeof(MrvlIETypes_HTCap_t)); tlv += sizeof(MrvlIETypes_HTCap_t); cmd_size += sizeof(MrvlIETypes_HTCap_t); pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len); } if (ISSUPP_11ACENABLED(pmpriv->adapter->fw_cap_info) && (pmpriv->config_bands & BAND_AAC)) { pvht_cap = (MrvlIETypes_VHTCap_t *)tlv; memset(pmadapter, pvht_cap, 0, sizeof(MrvlIETypes_VHTCap_t)); pvht_cap->header.type = wlan_cpu_to_le16(VHT_CAPABILITY); pvht_cap->header.len = sizeof(VHT_capa_t); wlan_fill_vht_cap_tlv(pmpriv, pvht_cap, pmpriv->config_bands, MFALSE, MFALSE); DBG_HEXDUMP(MCMD_D, "BGSCAN: VHT_CAPABILITIES IE", (t_u8 *)pvht_cap, sizeof(MrvlIETypes_VHTCap_t)); tlv += sizeof(MrvlIETypes_VHTCap_t); cmd_size += sizeof(MrvlIETypes_VHTCap_t); pvht_cap->header.len = wlan_cpu_to_le16(pvht_cap->header.len); } if (IS_FW_SUPPORT_11AX(pmadapter)) { phe_cap = (MrvlIEtypes_Extension_t *)tlv; len = wlan_fill_he_cap_tlv(pmpriv, pmpriv->config_bands, phe_cap, MFALSE); DBG_HEXDUMP(MCMD_D, "BGSCAN: HE_CAPABILITIES IE", (t_u8 *)phe_cap, len); tlv += len; cmd_size += len; } if (wlan_is_ext_capa_support(pmpriv)) { wlan_add_ext_capa_info_ie(pmpriv, MNULL, &tlv); cmd_size += sizeof(MrvlIETypes_ExtCap_t); } if (pmpriv->adapter->ecsa_enable) { t_u8 bandwidth = BW_20MHZ; t_u8 oper_class = 1; t_u32 usr_dot_11n_dev_cap; if (pmpriv->media_connected) { if (pmpriv->config_bands & BAND_A) usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_a; else usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_bg; if (usr_dot_11n_dev_cap & MBIT(17)) { bandwidth = BW_40MHZ; if (ISSUPP_11ACENABLED( pmadapter->fw_cap_info) && (pmpriv->config_bands & BAND_AAC)) bandwidth = BW_80MHZ; } wlan_get_curr_oper_class( pmpriv, pmpriv->curr_bss_params.bss_descriptor.channel, bandwidth, &oper_class); } len = wlan_add_supported_oper_class_ie(pmpriv, &tlv, oper_class); cmd_size += len; } tlv_start_later = (MrvlIEtypes_StartLater_t *)tlv; tlv_start_later->header.type = wlan_cpu_to_le16(TLV_TYPE_STARTBGSCANLATER); tlv_start_later->header.len = wlan_cpu_to_le16( sizeof(MrvlIEtypes_StartLater_t) - sizeof(MrvlIEtypesHeader_t)); tlv_start_later->value = wlan_cpu_to_le16(bg_scan_in->start_later); tlv += sizeof(MrvlIEtypes_StartLater_t); cmd_size += sizeof(MrvlIEtypes_StartLater_t); if (bg_scan_in->config_ees) { /* Fill EES configuration */ tlv_ees_cfg = (MrvlIEtypes_EESParamSet_t *)tlv; tlv_ees_cfg->header.type = wlan_cpu_to_le16(TLV_TYPE_EES_CFG); tlv_ees_cfg->header.len = wlan_cpu_to_le16(sizeof(MrvlIEtypes_EESParamSet_t) - sizeof(MrvlIEtypesHeader_t)); tlv_ees_cfg->ees_mode = wlan_cpu_to_le16(bg_scan_in->ees_mode); tlv_ees_cfg->report_cond = wlan_cpu_to_le16(bg_scan_in->report_cond); tlv_ees_cfg->high_period = wlan_cpu_to_le16(bg_scan_in->high_period); tlv_ees_cfg->high_period_count = wlan_cpu_to_le16(bg_scan_in->high_period_count); tlv_ees_cfg->mid_period = wlan_cpu_to_le16(bg_scan_in->mid_period); tlv_ees_cfg->mid_period_count = wlan_cpu_to_le16(bg_scan_in->mid_period_count); tlv_ees_cfg->low_period = wlan_cpu_to_le16(bg_scan_in->low_period); tlv_ees_cfg->low_period_count = wlan_cpu_to_le16(bg_scan_in->low_period_count); tlv += sizeof(MrvlIEtypes_EESParamSet_t); cmd_size += sizeof(MrvlIEtypes_EESParamSet_t); if (bg_scan_in->network_count) { /* Fill EES network configuration */ tlv_ees_net_cfg = (MrvlIEtype_EESNetworkCfg_t *)tlv; tlv_ees_net_cfg->header.type = wlan_cpu_to_le16(TLV_TYPE_EES_NET_CFG); tlv_ees_net_cfg->header.len = wlan_cpu_to_le16( sizeof(MrvlIEtype_EESNetworkCfg_t) - sizeof(MrvlIEtypesHeader_t)); tlv_ees_net_cfg->network_count = bg_scan_in->network_count; tlv_ees_net_cfg->max_conn_count = bg_scan_in->max_conn_count; tlv_ees_net_cfg->black_list_exp = bg_scan_in->black_list_exp; tlv += sizeof(MrvlIEtype_EESNetworkCfg_t); cmd_size += sizeof(MrvlIEtype_EESNetworkCfg_t); for (index = 0; index < bg_scan_in->network_count; index++) { if (wlan_strlen((char *)bg_scan_in ->ees_ssid_cfg[index] .ssid)) { /* Fill SSID settings */ tlv_ssid = (MrvlIEtypes_SsIdParamSet_t *) tlv; tlv_ssid->header.type = wlan_cpu_to_le16(TLV_TYPE_SSID); tlv_ssid->header.len = wlan_cpu_to_le16( (t_u16)bg_scan_in ->ees_ssid_cfg[index] .max_len); memcpy_ext( pmadapter, tlv_ssid->ssid, bg_scan_in->ees_ssid_cfg[index] .ssid, bg_scan_in->ees_ssid_cfg[index] .max_len, MLAN_MAX_SSID_LENGTH); tlv += sizeof(MrvlIEtypesHeader_t) + tlv_ssid->header.len; cmd_size += sizeof(MrvlIEtypesHeader_t) + tlv_ssid->header.len; } else { /* Fill Wildcard SSID settings */ pwildcard_ssid_tlv = (MrvlIEtypes_WildCardSsIdParamSet_t *)tlv; pwildcard_ssid_tlv->header.type = wlan_cpu_to_le16( TLV_TYPE_WILDCARDSSID); pwildcard_ssid_tlv->header .len = wlan_cpu_to_le16( sizeof(MrvlIEtypes_WildCardSsIdParamSet_t) - sizeof(MrvlIEtypesHeader_t)); pwildcard_ssid_tlv->max_ssid_length = MLAN_MAX_SSID_LENGTH; tlv += sizeof(MrvlIEtypesHeader_t) + sizeof(pwildcard_ssid_tlv ->max_ssid_length); cmd_size += sizeof(MrvlIEtypesHeader_t) + sizeof(pwildcard_ssid_tlv ->max_ssid_length); } /* Fill Cipher settings */ tlv_ees_cipher = (MrvlIEtypes_Cipher_t *)tlv; tlv_ees_cipher->header.type = wlan_cpu_to_le16(TLV_TYPE_CIPHER); tlv_ees_cipher->header.len = wlan_cpu_to_le16( sizeof(MrvlIEtypes_Cipher_t) - sizeof(MrvlIEtypesHeader_t)); tlv_ees_cipher->pair_cipher = bg_scan_in->ees_ssid_cfg[index] .pair_cipher; tlv_ees_cipher->group_cipher = bg_scan_in->ees_ssid_cfg[index] .group_cipher; tlv += sizeof(MrvlIEtypes_Cipher_t); cmd_size += sizeof(MrvlIEtypes_Cipher_t); } } } if (memcmp(pmadapter, bg_scan_in->random_mac, zero_mac, MLAN_MAC_ADDR_LENGTH)) { MrvlIEtypes_MacAddr_t *randomMacParam = (MrvlIEtypes_MacAddr_t *)tlv; memset(pmadapter, randomMacParam, 0, sizeof(MrvlIEtypes_MacAddr_t)); randomMacParam->header.type = wlan_cpu_to_le16(TLV_TYPE_RANDOM_MAC); randomMacParam->header.len = wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); memcpy_ext(pmadapter, randomMacParam->mac, bg_scan_in->random_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); tlv += sizeof(MrvlIEtypes_MacAddr_t); cmd_size += sizeof(MrvlIEtypes_MacAddr_t); } 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(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp, 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 handles the command response of bgscan_query * @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_bgscan_query(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp, mlan_ioctl_req *pioctl_buf) { mlan_ds_scan *pscan = MNULL; mlan_adapter *pmadapter = pmpriv->adapter; t_u8 i; ENTER(); for (i = 0; i < pmadapter->num_in_chan_stats; i++) pmadapter->pchan_stats[i].cca_scan_duration = 0; pmadapter->idx_chan_stats = 0; wlan_ret_802_11_scan(pmpriv, resp, MNULL); if (pioctl_buf) { pscan = (mlan_ds_scan *)pioctl_buf->pbuf; pscan->param.scan_resp.pscan_table = (t_u8 *)pmadapter->pscan_table; pscan->param.scan_resp.num_in_scan_table = pmadapter->num_in_scan_table; pscan->param.scan_resp.age_in_secs = pmadapter->age_in_secs; pscan->param.scan_resp.pchan_stats = (t_u8 *)pmadapter->pchan_stats; pscan->param.scan_resp.num_in_chan_stats = pmadapter->num_in_chan_stats; pioctl_buf->data_read_written = sizeof(mlan_scan_resp) + 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(mlan_private *pmpriv, mlan_802_11_ssid *ssid, t_u8 *bssid, 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( pmpriv->config_bands, pmadapter->pscan_table[i].bss_band)) 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(mlan_private *pmpriv, t_u8 *bssid, 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( pmpriv->config_bands, pmadapter->pscan_table[i].bss_band)) 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(pmlan_adapter pmadapter, mlan_802_11_ssid *ssid1, 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 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(mlan_private *pmpriv, 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_ext(pmadapter, &preq_ssid_bssid->ssid, &preq_bss->ssid, sizeof(mlan_802_11_ssid), sizeof(preq_ssid_bssid->ssid)); memcpy_ext(pmadapter, (t_u8 *)&preq_ssid_bssid->bssid, (t_u8 *)&preq_bss->mac_address, MLAN_MAC_ADDR_LENGTH, 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; preq_ssid_bssid->channel = (t_u16)preq_bss->channel; if (preq_bss->pmd_ie && wlan_ft_akm_is_used(pmpriv, (t_u8 *)preq_bss->prsn_ie)) { preq_ssid_bssid->ft_md = preq_bss->pmd_ie->mdid; preq_ssid_bssid->ft_cap = preq_bss->pmd_ie->ft_cap; } preq_ssid_bssid->bss_band = preq_bss->bss_band; preq_ssid_bssid->idx = i + 1; } if (!preq_ssid_bssid->ssid.ssid_len) { ret = MLAN_STATUS_FAILURE; goto done; } PRINTM(MINFO, "Best network found = [%s], " "[" MACSTR "]\n", preq_ssid_bssid->ssid.ssid, MAC2STR(preq_ssid_bssid->bssid)); 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(mlan_private *pmpriv, t_void *pioctl_buf, 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_ext(pmpriv->adapter, pscan_cfg->ssid_list[0].ssid, preq_ssid->ssid, preq_ssid->ssid_len, MLAN_MAX_SSID_LENGTH); pscan_cfg->keep_previous_scan = MFALSE; 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(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(); pcb->moal_spin_lock(pmadapter->pmoal_handle, pmpriv->curr_bcn_buf_lock); /* 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_ext(pmpriv->adapter, pmpriv->pcurr_bcn_buf, pcurr_bss->pbeacon_buf, pcurr_bss->beacon_buf_size, pcurr_bss->beacon_buf_size); PRINTM(MINFO, "current beacon saved %d\n", pmpriv->curr_bcn_size); } else { PRINTM(MERROR, "Fail to allocate curr_bcn_buf\n"); } } } wlan_update_curr_bcn(pmpriv); pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmpriv->curr_bcn_buf_lock); 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(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(); }