| /** @file mlan_scan.c |
| * |
| * @brief Functions implementing wlan scan IOCTL and firmware command APIs |
| * |
| * IOCTL handlers as well as command preparation and response routines |
| * for sending scan commands to the firmware. |
| * |
| * Copyright (C) 2008-2014, Marvell International Ltd. |
| * |
| * This software file (the "File") is distributed by Marvell International |
| * Ltd. under the terms of the GNU General Public License Version 2, June 1991 |
| * (the "License"). You may use, redistribute and/or modify this File in |
| * accordance with the terms and conditions of the License, a copy of which |
| * is available by writing to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the |
| * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE |
| * ARE EXPRESSLY DISCLAIMED. The License provides additional details about |
| * this warranty disclaimer. |
| */ |
| |
| /****************************************************** |
| Change log: |
| 10/28/2008: initial version |
| ******************************************************/ |
| |
| #include "mlan.h" |
| #include "mlan_join.h" |
| #include "mlan_util.h" |
| #include "mlan_fw.h" |
| #include "mlan_main.h" |
| #include "mlan_11n.h" |
| #include "mlan_11h.h" |
| /******************************************************** |
| Local Constants |
| ********************************************************/ |
| |
| /** The maximum number of channels the firmware can scan per command */ |
| #define MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 |
| |
| /** |
| * Number of channels to scan per firmware scan command issuance. |
| * |
| * Number restricted to prevent hitting the limit on the amount of scan data |
| * returned in a single firmware scan command. |
| */ |
| #define MRVDRV_CHANNELS_PER_SCAN_CMD 4 |
| |
| /** Memory needed to store a max sized Channel List TLV for a firmware scan */ |
| #define CHAN_TLV_MAX_SIZE (sizeof(MrvlIEtypesHeader_t) \ |
| + (MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN \ |
| * sizeof(ChanScanParamSet_t))) |
| |
| /** Memory needed to store supported rate */ |
| #define RATE_TLV_MAX_SIZE (sizeof(MrvlIEtypes_RatesParamSet_t) + HOSTCMD_SUPPORTED_RATES) |
| |
| /** Memory needed to store a max number/size WildCard |
| * SSID TLV for a firmware scan */ |
| #define WILDCARD_SSID_TLV_MAX_SIZE \ |
| (MRVDRV_MAX_SSID_LIST_LENGTH * \ |
| (sizeof(MrvlIEtypes_WildCardSsIdParamSet_t) + \ |
| MRVDRV_MAX_SSID_LENGTH)) |
| |
| /** WPS TLV MAX size is MAX IE size plus 2 bytes for |
| * t_u16 MRVL TLV extension */ |
| #define WPS_TLV_MAX_SIZE (sizeof(IEEEtypes_VendorSpecific_t) + 2) |
| /** Maximum memory needed for a wlan_scan_cmd_config |
| * with all TLVs at max */ |
| #define MAX_SCAN_CFG_ALLOC (sizeof(wlan_scan_cmd_config) \ |
| + sizeof(MrvlIEtypes_NumProbes_t) \ |
| + sizeof(MrvlIETypes_HTCap_t) \ |
| + CHAN_TLV_MAX_SIZE \ |
| + RATE_TLV_MAX_SIZE \ |
| + WILDCARD_SSID_TLV_MAX_SIZE \ |
| + WPS_TLV_MAX_SIZE) |
| |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| /** |
| * Interally used to send a configured scan cmd between |
| * driver routines |
| */ |
| typedef union { |
| /** Scan configuration (variable length) */ |
| wlan_scan_cmd_config config; |
| /** Max allocated block */ |
| t_u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; |
| } wlan_scan_cmd_config_tlv; |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| /** Cipher suite definition */ |
| enum cipher_suite { |
| CIPHER_SUITE_WEP40, |
| CIPHER_SUITE_TKIP, |
| CIPHER_SUITE_CCMP, |
| CIPHER_SUITE_WEP104, |
| CIPHER_SUITE_MAX |
| }; |
| |
| static t_u8 wpa_oui[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 */ |
| }; |
| |
| /** |
| * @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 |
| */ |
| void |
| wlan_update_chan_statistics(mlan_private * pmpriv, |
| MrvlIEtypes_ChannelStats_t * pchanstats_tlv) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| t_u8 i, j; |
| ChanStatistics_t *pchan_stats = |
| (ChanStatistics_t *) ((t_u8 *) pchanstats_tlv + |
| sizeof(MrvlIEtypesHeader_t)); |
| t_u8 num_chan = |
| wlan_le16_to_cpu(pchanstats_tlv->header.len) / |
| sizeof(ChanStatistics_t); |
| |
| ENTER(); |
| |
| for (j = 0; j < num_chan; j++) { |
| for (i = 0; i < pmadapter->num_in_chan_stats; i++) { |
| if (pmadapter->pchan_stats[i].chan_num == |
| pchan_stats->chan_num) { |
| 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(pmadapter, &pmadapter->pchan_stats[i], |
| pchan_stats, sizeof(ChanStatistics_t)); |
| break; |
| } |
| } |
| 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))) { |
| 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 pbss_desc for appropriate IE and then check if WPA IE has AES |
| * OUI in it. If WPA IE does not have AES in PTK then return 0; |
| * |
| * @param pbss_desc A pointer to current BSS descriptor |
| * @return 0 on failure to find AES OUI, 1 on success. |
| */ |
| static t_u8 |
| is_wpa_oui_present(mlan_adapter * pmadapter, BSSDescriptor_t * pbss_desc, |
| t_u32 cipher_suite) |
| { |
| t_u8 *oui = MNULL; |
| IEBody *ie_body = MNULL; |
| t_u8 ret = MLAN_OUI_NOT_PRESENT; |
| |
| ENTER(); |
| if (((pbss_desc->pwpa_ie) && |
| ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE))) { |
| ie_body = (IEBody *) pbss_desc->pwpa_ie->data; |
| oui = &wpa_oui[cipher_suite][0]; |
| 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_u8 band; |
| switch (scan_band) { |
| case BAND_A: |
| band = BAND_A | BAND_AN; |
| break; |
| case BAND_G: |
| default: |
| band = BAND_B | BAND_G | BAND_GN; |
| } |
| return cfg_band & band; |
| } |
| |
| /** |
| * @brief Convert radio type scan parameter to a band config used in join cmd |
| * |
| * @param radio_type Scan parameter indicating the radio used for a channel |
| * in a scan command. |
| * |
| * @return Band type conversion of scanBand used in join/assoc cmds |
| * |
| */ |
| static t_u8 |
| radio_type_to_band(t_u8 radio_type) |
| { |
| t_u8 ret_band; |
| |
| switch (radio_type) { |
| case HostCmd_SCAN_RADIO_TYPE_A: |
| ret_band = BAND_A; |
| break; |
| case HostCmd_SCAN_RADIO_TYPE_BG: |
| default: |
| ret_band = BAND_G; |
| break; |
| } |
| |
| return ret_band; |
| } |
| |
| /** |
| * @brief This function finds the best SSID in the Scan List |
| * |
| * Search the scan table for the best SSID that also matches the current |
| * adapter network preference (infrastructure or adhoc) |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @return index in BSSID list |
| */ |
| static t_s32 |
| wlan_find_best_network_in_list(IN mlan_private * pmpriv) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| t_u32 mode = pmpriv->bss_mode; |
| t_s32 best_net = -1; |
| t_s32 best_rssi = 0; |
| t_u32 i; |
| |
| ENTER(); |
| |
| PRINTM(MINFO, "Num of BSSIDs = %d\n", pmadapter->num_in_scan_table); |
| |
| for (i = 0; i < pmadapter->num_in_scan_table; i++) { |
| switch (mode) { |
| case MLAN_BSS_MODE_INFRA: |
| case MLAN_BSS_MODE_IBSS: |
| if (wlan_is_network_compatible(pmpriv, i, mode) >= 0) { |
| if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > |
| best_rssi) { |
| best_rssi = |
| SCAN_RSSI(pmadapter-> |
| pscan_table[i].rssi); |
| best_net = i; |
| } |
| } |
| break; |
| case MLAN_BSS_MODE_AUTO: |
| default: |
| if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > |
| best_rssi) { |
| best_rssi = |
| SCAN_RSSI(pmadapter->pscan_table[i]. |
| rssi); |
| best_net = i; |
| } |
| break; |
| } |
| } |
| |
| LEAVE(); |
| return best_net; |
| } |
| |
| /** |
| * @brief Create a channel list for the driver to scan based on region info |
| * |
| * Use the driver region/band information to construct a comprehensive list |
| * of channels to scan. This routine is used for any scan that is not |
| * provided a specific channel list to scan. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param puser_scan_in MNULL or pointer to scan configuration parameters |
| * @param pscan_chan_list Output parameter: Resulting channel list to scan |
| * @param filtered_scan Flag indicating whether or not a BSSID or SSID filter |
| * is being sent in the command to firmware. Used to |
| * increase the number of channels sent in a scan |
| * command and to disable the firmware channel scan |
| * filter. |
| * |
| * @return N/A |
| */ |
| static t_void |
| wlan_scan_create_channel_list(IN mlan_private * pmpriv, |
| IN const wlan_user_scan_cfg * puser_scan_in, |
| OUT ChanScanParamSet_t * pscan_chan_list, |
| IN t_u8 filtered_scan) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| region_chan_t *pscan_region; |
| chan_freq_power_t *cfp; |
| t_u32 region_idx; |
| t_u32 chan_idx = 0; |
| t_u32 next_chan; |
| t_u8 scan_type; |
| t_u8 radio_type; |
| |
| ENTER(); |
| |
| for (region_idx = 0; |
| region_idx < NELEMENTS(pmadapter->region_channel); region_idx++) { |
| |
| if (wlan_11d_is_enabled(pmpriv) && |
| pmpriv->media_connected != MTRUE) { |
| /* Scan all the supported chan for the first scan */ |
| if (!pmadapter->universal_channel[region_idx].valid) |
| continue; |
| pscan_region = |
| &pmadapter->universal_channel[region_idx]; |
| } else { |
| if (!pmadapter->region_channel[region_idx].valid) |
| continue; |
| pscan_region = &pmadapter->region_channel[region_idx]; |
| } |
| |
| if (puser_scan_in && !puser_scan_in->chan_list[0].chan_number && |
| puser_scan_in->chan_list[0].radio_type & BAND_SPECIFIED) { |
| radio_type = |
| puser_scan_in->chan_list[0]. |
| radio_type & ~BAND_SPECIFIED; |
| if (!radio_type && (pscan_region->band != BAND_B) && |
| (pscan_region->band != BAND_G)) |
| continue; |
| if (radio_type && (pscan_region->band != BAND_A)) |
| continue; |
| } |
| if (!wlan_is_band_compatible |
| (pmpriv->config_bands | pmadapter->adhoc_start_band, |
| pscan_region->band)) |
| continue; |
| for (next_chan = 0; |
| next_chan < pscan_region->num_cfp; |
| next_chan++, chan_idx++) { |
| /* Set the default scan type to the user specified |
| type, will later be changed to passive on a per |
| channel basis if restricted by regulatory |
| requirements (11d or 11h) */ |
| scan_type = pmadapter->scan_type; |
| cfp = pscan_region->pcfp + next_chan; |
| |
| if (scan_type == MLAN_SCAN_TYPE_ACTIVE |
| && wlan_11d_is_enabled(pmpriv)) { |
| scan_type = wlan_11d_get_scan_type(pmadapter, |
| pscan_region-> |
| band, |
| (t_u8) cfp-> |
| channel, |
| &pmadapter-> |
| parsed_region_chan); |
| } |
| |
| switch (pscan_region->band) { |
| case BAND_A: |
| pscan_chan_list[chan_idx].radio_type = |
| HostCmd_SCAN_RADIO_TYPE_A; |
| if (!wlan_11d_is_enabled(pmpriv)) { |
| /* 11D not available... play it safe on |
| DFS channels */ |
| if (wlan_11h_radar_detect_required |
| (pmpriv, (t_u8) cfp->channel)) |
| scan_type = |
| MLAN_SCAN_TYPE_PASSIVE; |
| } |
| break; |
| case BAND_B: |
| case BAND_G: |
| if (!wlan_11d_is_enabled(pmpriv)) |
| if (wlan_bg_scan_type_is_passive |
| (pmpriv, (t_u8) cfp->channel)) { |
| scan_type = |
| MLAN_SCAN_TYPE_PASSIVE; |
| } |
| pscan_chan_list[chan_idx].radio_type = |
| HostCmd_SCAN_RADIO_TYPE_BG; |
| break; |
| default: |
| pscan_chan_list[chan_idx].radio_type = |
| HostCmd_SCAN_RADIO_TYPE_BG; |
| break; |
| } |
| |
| if (puser_scan_in && |
| puser_scan_in->chan_list[0].scan_time) { |
| pscan_chan_list[chan_idx].max_scan_time = |
| wlan_cpu_to_le16((t_u16) puser_scan_in-> |
| chan_list[0]. |
| scan_time); |
| } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| pscan_chan_list[chan_idx].max_scan_time = |
| wlan_cpu_to_le16(pmadapter-> |
| passive_scan_time); |
| } else if (filtered_scan) { |
| pscan_chan_list[chan_idx].max_scan_time = |
| wlan_cpu_to_le16(pmadapter-> |
| specific_scan_time); |
| } else { |
| pscan_chan_list[chan_idx].max_scan_time = |
| wlan_cpu_to_le16(pmadapter-> |
| active_scan_time); |
| } |
| |
| if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| pscan_chan_list[chan_idx].chan_scan_mode. |
| passive_scan = MTRUE; |
| } else { |
| pscan_chan_list[chan_idx].chan_scan_mode. |
| passive_scan = MFALSE; |
| } |
| |
| pscan_chan_list[chan_idx].chan_number = |
| (t_u8) cfp->channel; |
| |
| if (filtered_scan) { |
| pscan_chan_list[chan_idx].chan_scan_mode. |
| disable_chan_filt = MTRUE; |
| } |
| } |
| } |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Add WPS IE to probe request frame |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pptlv_out A pointer to TLV to fill in |
| * |
| * @return N/A |
| */ |
| static void |
| wlan_add_wps_probe_request_ie(IN mlan_private * pmpriv, OUT t_u8 ** pptlv_out) |
| { |
| MrvlIEtypesHeader_t *tlv; |
| |
| ENTER(); |
| |
| if (pmpriv->wps.wps_ie.vend_hdr.len) { |
| tlv = (MrvlIEtypesHeader_t *) * pptlv_out; |
| tlv->type = wlan_cpu_to_le16(VENDOR_SPECIFIC_221); |
| tlv->len = wlan_cpu_to_le16(pmpriv->wps.wps_ie.vend_hdr.len); |
| *pptlv_out += sizeof(MrvlIEtypesHeader_t); |
| memcpy(pmpriv->adapter, *pptlv_out, |
| pmpriv->wps.wps_ie.vend_hdr.oui, |
| pmpriv->wps.wps_ie.vend_hdr.len); |
| *pptlv_out += (pmpriv->wps.wps_ie.vend_hdr.len |
| + sizeof(MrvlIEtypesHeader_t)); |
| } |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Construct and send multiple scan config commands to the firmware |
| * |
| * Previous routines have created a wlan_scan_cmd_config with any requested |
| * TLVs. This function splits the channel TLV into max_chan_per_scan lists |
| * and sends the portion of the channel TLV along with the other TLVs |
| * to the wlan_cmd routines for execution in the firmware. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pioctl_buf A pointer to MLAN IOCTL Request buffer |
| * @param max_chan_per_scan Maximum number channels to be included in each |
| * scan command sent to firmware |
| * @param filtered_scan Flag indicating whether or not a BSSID or SSID |
| * filter is being used for the firmware command |
| * scan command sent to firmware |
| * @param pscan_cfg_out Scan configuration used for this scan. |
| * @param pchan_tlv_out Pointer in the pscan_cfg_out where the channel TLV |
| * should start. This is past any other TLVs that |
| * must be sent down in each firmware command. |
| * @param pscan_chan_list List of channels to scan in max_chan_per_scan segments |
| * |
| * @return MLAN_STATUS_SUCCESS or error return otherwise |
| */ |
| static mlan_status |
| wlan_scan_channel_list(IN mlan_private * pmpriv, |
| IN t_void * pioctl_buf, |
| IN t_u32 max_chan_per_scan, |
| IN t_u8 filtered_scan, |
| OUT wlan_scan_cmd_config * pscan_cfg_out, |
| OUT MrvlIEtypes_ChanListParamSet_t * pchan_tlv_out, |
| IN ChanScanParamSet_t * pscan_chan_list) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| ChanScanParamSet_t *ptmp_chan_list; |
| ChanScanParamSet_t *pstart_chan; |
| pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; |
| t_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; |
| 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-> |
| radio_type), |
| ptmp_chan_list-> |
| chan_number)) { |
| ptmp_chan_list++; |
| continue; |
| } |
| |
| if (first_chan) { |
| ptmp_chan_list->chan_scan_mode.first_chan = |
| MTRUE; |
| first_chan = 0; |
| } |
| |
| PRINTM(MINFO, |
| "Scan: Chan(%3d), Radio(%d), Mode(%d,%d), Dur(%d)\n", |
| ptmp_chan_list->chan_number, |
| ptmp_chan_list->radio_type, |
| ptmp_chan_list->chan_scan_mode.passive_scan, |
| ptmp_chan_list->chan_scan_mode.disable_chan_filt, |
| wlan_le16_to_cpu(ptmp_chan_list->max_scan_time)); |
| |
| 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(pmadapter, |
| pscan_cfg_out->tlv_buf, |
| ptlv_temp, 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)) { |
| |
| 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(pmadapter, ptlv_temp, ptlv_pos, |
| tlv_buf_len); |
| |
| /* 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(pmadapter, |
| pchan_tlv_out->chan_scan_param + tlv_idx, |
| ptmp_chan_list, |
| sizeof(pchan_tlv_out->chan_scan_param)); |
| |
| /* Increment the TLV header length by the size appended |
| */ |
| pchan_tlv_out->header.len += |
| sizeof(pchan_tlv_out->chan_scan_param); |
| |
| /* |
| * The tlv buffer length is set to the number of |
| * bytes of the between the channel tlv pointer |
| * and the start of the tlv buffer. This |
| * compensates for any TLVs that were appended |
| * before the channel list. |
| */ |
| pscan_cfg_out->tlv_buf_len = |
| (t_u32) ((t_u8 *) pchan_tlv_out - |
| pscan_cfg_out->tlv_buf); |
| |
| /* Add the size of the channel tlv header and the data |
| length */ |
| pscan_cfg_out->tlv_buf_len += |
| (sizeof(pchan_tlv_out->header) |
| + pchan_tlv_out->header.len); |
| |
| /* Increment the index to the channel tlv we are |
| constructing */ |
| tlv_idx++; |
| |
| /* Count the total scan time per command */ |
| total_scan_time += |
| wlan_le16_to_cpu(ptmp_chan_list->max_scan_time); |
| |
| done_early = MFALSE; |
| |
| /* Stop the loop if the *current* channel is in the |
| 1,6,11 set * and we are not filtering on a BSSID or |
| SSID. */ |
| if (!filtered_scan && |
| (ptmp_chan_list->chan_number == 1 || |
| ptmp_chan_list->chan_number == 6 || |
| ptmp_chan_list->chan_number == 11)) { |
| done_early = MTRUE; |
| } |
| |
| /* 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; |
| } |
| } |
| |
| /* The total scan time should be less than scan command timeout |
| value */ |
| if (total_scan_time > MRVDRV_MAX_TOTAL_SCAN_TIME) { |
| PRINTM(MMSG, |
| "Total scan time %d ms is over limit (%d ms), scan skipped\n", |
| total_scan_time, MRVDRV_MAX_TOTAL_SCAN_TIME); |
| if (pioctl_req) |
| pioctl_req->status_code = |
| MLAN_ERROR_CMD_SCAN_FAIL; |
| ret = MLAN_STATUS_FAILURE; |
| break; |
| } |
| |
| pchan_tlv_out->header.len = |
| wlan_cpu_to_le16(pchan_tlv_out->header.len); |
| |
| pmadapter->pscan_channels = pstart_chan; |
| |
| /* Send the scan command to the firmware with the specified cfg |
| */ |
| if (pmadapter->ext_scan) |
| cmd_no = HostCmd_CMD_802_11_SCAN_EXT; |
| else |
| cmd_no = HostCmd_CMD_802_11_SCAN; |
| ret = wlan_prepare_cmd(pmpriv, |
| cmd_no, |
| HostCmd_ACT_GEN_SET, |
| 0, 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(IN mlan_private * pmpriv, |
| IN const wlan_user_scan_cfg * puser_scan_in, |
| OUT wlan_scan_cmd_config * pscan_cfg_out, |
| OUT MrvlIEtypes_ChanListParamSet_t ** |
| ppchan_list_out, |
| OUT ChanScanParamSet_t * pscan_chan_list, |
| OUT t_u8 * pmax_chan_per_scan, |
| OUT t_u8 * pfiltered_scan, |
| OUT t_u8 * pscan_current_only) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| MrvlIEtypes_NumProbes_t *pnum_probes_tlv; |
| MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv; |
| MrvlIEtypes_RatesParamSet_t *prates_tlv; |
| 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 scan_type; |
| t_u16 scan_dur; |
| t_u8 channel; |
| t_u8 radio_type; |
| t_u32 ssid_idx; |
| t_u8 ssid_filter; |
| WLAN_802_11_RATES rates; |
| t_u32 rates_size; |
| MrvlIETypes_HTCap_t *pht_cap; |
| |
| MrvlIEtypes_ScanChanGap_t *pscan_gap_tlv; |
| ENTER(); |
| |
| /* The tlv_buf_len is calculated for each scan command. The TLVs added |
| in this routine will be preserved since the routine that sends the |
| command will append channelTLVs at *ppchan_list_out. The difference |
| between the *ppchan_list_out and the tlv_buf start will be used to |
| calculate the size of anything we add in this routine. */ |
| pscan_cfg_out->tlv_buf_len = 0; |
| |
| /* Running tlv pointer. Assigned to ppchan_list_out at end of function |
| so later routines know where channels can be added to the command |
| buf */ |
| ptlv_pos = pscan_cfg_out->tlv_buf; |
| |
| /* Initialize the scan as un-filtered; the flag is later set to TRUE |
| below if a SSID or BSSID filter is sent in the command */ |
| *pfiltered_scan = MFALSE; |
| |
| /* Initialize the scan as not being only on the current channel. If |
| the channel list is customized, only contains one channel, and is |
| the active channel, this is set true and data flow is not halted. */ |
| *pscan_current_only = MFALSE; |
| |
| if (puser_scan_in) { |
| |
| ssid_filter = MFALSE; |
| |
| /* Set the bss type scan filter, use Adapter setting if unset */ |
| pscan_cfg_out->bss_mode = (puser_scan_in->bss_mode |
| ? (t_u8) puser_scan_in->bss_mode : |
| (t_u8) pmadapter->scan_mode); |
| |
| /* Set the number of probes to send, use Adapter setting if |
| unset */ |
| num_probes = |
| (puser_scan_in->num_probes ? puser_scan_in-> |
| num_probes : pmadapter->scan_probes); |
| /* |
| * Set the BSSID filter to the incoming configuration, |
| * if non-zero. If not set, it will remain disabled |
| * (all zeros). |
| */ |
| memcpy(pmadapter, pscan_cfg_out->specific_bssid, |
| puser_scan_in->specific_bssid, |
| sizeof(pscan_cfg_out->specific_bssid)); |
| |
| if (pmadapter->ext_scan |
| && 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 = MLAN_MAC_ADDR_LENGTH; |
| memcpy(pmadapter, pbssid_tlv->bssid, |
| puser_scan_in->specific_bssid, |
| 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(pmadapter, pwildcard_ssid_tlv->ssid, |
| puser_scan_in->ssid_list[ssid_idx].ssid, |
| MIN(MLAN_MAX_SSID_LENGTH, ssid_len)); |
| |
| ptlv_pos += (sizeof(pwildcard_ssid_tlv->header) |
| + pwildcard_ssid_tlv->header.len); |
| |
| pwildcard_ssid_tlv->header.len |
| = |
| wlan_cpu_to_le16(pwildcard_ssid_tlv->header. |
| len); |
| |
| PRINTM(MINFO, "Scan: ssid_list[%d]: %s, %d\n", |
| ssid_idx, |
| pwildcard_ssid_tlv->ssid, |
| pwildcard_ssid_tlv->max_ssid_length); |
| |
| if (ssid_len) |
| ssid_filter = MTRUE; |
| } |
| |
| /* |
| * The default number of channels sent in the command is low to |
| * ensure the response buffer from the firmware does not |
| * truncate scan results. That is not an issue with an SSID or |
| * BSSID filter applied to the scan results in the firmware. |
| */ |
| if ((ssid_idx && ssid_filter) || |
| memcmp(pmadapter, pscan_cfg_out->specific_bssid, &zero_mac, |
| sizeof(zero_mac))) { |
| *pfiltered_scan = MTRUE; |
| } |
| |
| } else { |
| pscan_cfg_out->bss_mode = (t_u8) pmadapter->scan_mode; |
| num_probes = pmadapter->scan_probes; |
| } |
| |
| /* |
| * If a specific BSSID or SSID is used, the number of channels in |
| * the scan command will be increased to the absolute maximum. |
| */ |
| if (*pfiltered_scan) |
| *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; |
| else |
| *pmax_chan_per_scan = MRVDRV_CHANNELS_PER_SCAN_CMD; |
| |
| if (puser_scan_in && puser_scan_in->scan_chan_gap) { |
| *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; |
| PRINTM(MINFO, "Scan: channel gap = %d\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); |
| } |
| /* 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(pmadapter, prates_tlv->rates, rates, rates_size); |
| ptlv_pos += sizeof(prates_tlv->header) + rates_size; |
| |
| PRINTM(MINFO, "SCAN_CMD: Rates size = %d\n", rates_size); |
| |
| if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info) |
| && (pmpriv->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); |
| 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 (wlan_is_ext_capa_support(pmpriv)) |
| wlan_add_ext_capa_info_ie(pmpriv, &ptlv_pos); |
| wlan_add_wps_probe_request_ie(pmpriv, &ptlv_pos); |
| |
| /* |
| * Set the output for the channel TLV to the address in the tlv buffer |
| * past any TLVs that were added in this function (SSID, num_probes). |
| * Channel TLVs will be added past this for each scan command, |
| * preserving the TLVs that were previously added. |
| */ |
| *ppchan_list_out = (MrvlIEtypes_ChanListParamSet_t *) ptlv_pos; |
| |
| if (puser_scan_in && puser_scan_in->chan_list[0].chan_number) { |
| |
| PRINTM(MINFO, "Scan: Using supplied channel list\n"); |
| |
| for (chan_idx = 0; |
| chan_idx < WLAN_USER_SCAN_CHAN_MAX |
| && puser_scan_in->chan_list[chan_idx].chan_number; |
| chan_idx++) { |
| |
| channel = |
| puser_scan_in->chan_list[chan_idx].chan_number; |
| (pscan_chan_list + chan_idx)->chan_number = channel; |
| |
| radio_type = |
| puser_scan_in->chan_list[chan_idx].radio_type; |
| (pscan_chan_list + chan_idx)->radio_type = radio_type; |
| |
| scan_type = |
| puser_scan_in->chan_list[chan_idx].scan_type; |
| if (scan_type == MLAN_SCAN_TYPE_UNCHANGED) |
| scan_type = pmadapter->scan_type; |
| |
| if (radio_type == HostCmd_SCAN_RADIO_TYPE_A) { |
| if (pmadapter->fw_bands & BAND_A) |
| PRINTM(MINFO, |
| "UserScan request for A Band channel %d!!\n", |
| channel); |
| else { |
| PRINTM(MERROR, |
| "Scan in A band is not allowed!!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| LEAVE(); |
| return ret; |
| |
| } |
| } |
| |
| /* Prevent active scanning on a radar controlled |
| channel */ |
| if (radio_type == HostCmd_SCAN_RADIO_TYPE_A) { |
| if (wlan_11h_radar_detect_required |
| (pmpriv, channel)) { |
| scan_type = MLAN_SCAN_TYPE_PASSIVE; |
| } |
| } |
| if (radio_type == HostCmd_SCAN_RADIO_TYPE_BG) { |
| if (wlan_bg_scan_type_is_passive |
| (pmpriv, channel)) { |
| scan_type = MLAN_SCAN_TYPE_PASSIVE; |
| } |
| } |
| if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| (pscan_chan_list + |
| chan_idx)->chan_scan_mode.passive_scan = MTRUE; |
| } else { |
| (pscan_chan_list + |
| chan_idx)->chan_scan_mode.passive_scan = |
| MFALSE; |
| } |
| |
| if (puser_scan_in->chan_list[chan_idx].scan_time) { |
| scan_dur = |
| (t_u16) puser_scan_in-> |
| chan_list[chan_idx].scan_time; |
| } else { |
| if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| scan_dur = pmadapter->passive_scan_time; |
| } else if (*pfiltered_scan) { |
| scan_dur = |
| pmadapter->specific_scan_time; |
| } else { |
| scan_dur = pmadapter->active_scan_time; |
| } |
| } |
| |
| (pscan_chan_list + chan_idx)->min_scan_time = |
| wlan_cpu_to_le16(scan_dur); |
| (pscan_chan_list + chan_idx)->max_scan_time = |
| wlan_cpu_to_le16(scan_dur); |
| if (*pfiltered_scan) { |
| (pscan_chan_list + |
| chan_idx)->chan_scan_mode.disable_chan_filt = |
| MTRUE; |
| } |
| } |
| |
| /* Check if we are only scanning the current channel */ |
| if ((chan_idx == 1) |
| && (puser_scan_in->chan_list[0].chan_number |
| == pmpriv->curr_bss_params.bss_descriptor.channel)) { |
| *pscan_current_only = MTRUE; |
| PRINTM(MINFO, "Scan: Scanning current channel only\n"); |
| } |
| |
| } else { |
| PRINTM(MINFO, "Scan: Creating full region channel list\n"); |
| wlan_scan_create_channel_list(pmpriv, puser_scan_in, |
| pscan_chan_list, *pfiltered_scan); |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Inspect the scan response buffer for pointers to expected TLVs |
| * |
| * TLVs can be included at the end of the scan response BSS information. |
| * Parse the data in the buffer for pointers to TLVs that can potentially |
| * be passed back in the response |
| * |
| * @param pmadapter Pointer to the mlan_adapter structure |
| * @param ptlv Pointer to the start of the TLV buffer to parse |
| * @param tlv_buf_size Size of the TLV buffer |
| * @param req_tlv_type Request TLV's type |
| * @param pptlv Output parameter: Pointer to the request TLV if found |
| * |
| * @return N/A |
| */ |
| static t_void |
| wlan_ret_802_11_scan_get_tlv_ptrs(IN pmlan_adapter pmadapter, |
| IN MrvlIEtypes_Data_t * ptlv, |
| IN t_u32 tlv_buf_size, |
| IN t_u32 req_tlv_type, |
| OUT MrvlIEtypes_Data_t ** pptlv) |
| { |
| MrvlIEtypes_Data_t *pcurrent_tlv; |
| t_u32 tlv_buf_left; |
| t_u32 tlv_type; |
| t_u32 tlv_len; |
| |
| ENTER(); |
| |
| pcurrent_tlv = ptlv; |
| tlv_buf_left = tlv_buf_size; |
| *pptlv = MNULL; |
| |
| PRINTM(MINFO, "SCAN_RESP: tlv_buf_size = %d\n", tlv_buf_size); |
| |
| while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { |
| |
| tlv_type = wlan_le16_to_cpu(pcurrent_tlv->header.type); |
| tlv_len = wlan_le16_to_cpu(pcurrent_tlv->header.len); |
| |
| if (sizeof(ptlv->header) + tlv_len > tlv_buf_left) { |
| PRINTM(MERROR, "SCAN_RESP: TLV buffer corrupt\n"); |
| break; |
| } |
| |
| if (req_tlv_type == tlv_type) { |
| switch (tlv_type) { |
| case TLV_TYPE_TSFTIMESTAMP: |
| PRINTM(MINFO, |
| "SCAN_RESP: TSF Timestamp TLV, len = %d\n", |
| tlv_len); |
| *pptlv = (MrvlIEtypes_Data_t *) pcurrent_tlv; |
| break; |
| case TLV_TYPE_CHANNELBANDLIST: |
| PRINTM(MINFO, |
| "SCAN_RESP: CHANNEL BAND LIST TLV, len = %d\n", |
| tlv_len); |
| *pptlv = (MrvlIEtypes_Data_t *) pcurrent_tlv; |
| break; |
| 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(IN pmlan_adapter pmadapter, |
| OUT BSSDescriptor_t * pbss_entry, |
| IN t_u8 ** pbeacon_info, |
| IN t_u32 * bytes_left, IN 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; |
| |
| ENTER(); |
| |
| found_data_rate_ie = MFALSE; |
| rate_size = 0; |
| beacon_size = 0; |
| |
| if (*bytes_left >= sizeof(beacon_size)) { |
| /* Extract & convert beacon size from the command buffer */ |
| memcpy(pmadapter, &beacon_size, *pbeacon_info, |
| sizeof(beacon_size)); |
| beacon_size = wlan_le16_to_cpu(beacon_size); |
| *bytes_left -= sizeof(beacon_size); |
| *pbeacon_info += sizeof(beacon_size); |
| } |
| |
| if (!beacon_size || beacon_size > *bytes_left) { |
| |
| *pbeacon_info += *bytes_left; |
| *bytes_left = 0; |
| |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /* Initialize the current working beacon pointer for this BSS iteration |
| */ |
| pcurrent_ptr = *pbeacon_info; |
| |
| /* Advance the return beacon pointer past the current beacon */ |
| *pbeacon_info += beacon_size; |
| *bytes_left -= beacon_size; |
| |
| bytes_left_for_current_beacon = beacon_size; |
| |
| if (bytes_left_for_current_beacon < |
| (MLAN_MAC_ADDR_LENGTH + sizeof(t_u8) + |
| sizeof(WLAN_802_11_FIXED_IEs))) { |
| PRINTM(MERROR, "InterpretIE: Not enough bytes left\n"); |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| memcpy(pmadapter, pbss_entry->mac_address, pcurrent_ptr, |
| MLAN_MAC_ADDR_LENGTH); |
| PRINTM(MINFO, "InterpretIE: AP MAC Addr-" 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(pmadapter, fixed_ie.time_stamp, pcurrent_ptr, 8); |
| memcpy(pmadapter, pbss_entry->time_stamp, pcurrent_ptr, 8); |
| pcurrent_ptr += 8; |
| bytes_left_for_current_beacon -= 8; |
| |
| /* Beacon interval is 2 bytes long */ |
| memcpy(pmadapter, &fixed_ie.beacon_interval, pcurrent_ptr, 2); |
| pbss_entry->beacon_period = wlan_le16_to_cpu(fixed_ie.beacon_interval); |
| pcurrent_ptr += 2; |
| bytes_left_for_current_beacon -= 2; |
| |
| /* Capability information is 2 bytes long */ |
| memcpy(pmadapter, &fixed_ie.capabilities, pcurrent_ptr, 2); |
| PRINTM(MINFO, "InterpretIE: fixed_ie.capabilities=0x%X\n", |
| fixed_ie.capabilities); |
| fixed_ie.capabilities = wlan_le16_to_cpu(fixed_ie.capabilities); |
| pcap_info = (IEEEtypes_CapInfo_t *) & fixed_ie.capabilities; |
| memcpy(pmadapter, &pbss_entry->cap_info, pcap_info, |
| sizeof(IEEEtypes_CapInfo_t)); |
| pcurrent_ptr += 2; |
| bytes_left_for_current_beacon -= 2; |
| |
| /* Rest of the current buffer are IE's */ |
| PRINTM(MINFO, "InterpretIE: IELength for this AP = %d\n", |
| bytes_left_for_current_beacon); |
| |
| HEXDUMP("InterpretIE: IE info", (t_u8 *) pcurrent_ptr, |
| bytes_left_for_current_beacon); |
| |
| if (pcap_info->privacy) { |
| PRINTM(MINFO, "InterpretIE: AP WEP enabled\n"); |
| pbss_entry->privacy = Wlan802_11PrivFilter8021xWEP; |
| } else { |
| pbss_entry->privacy = Wlan802_11PrivFilterAcceptAll; |
| } |
| |
| if (pcap_info->ibss == 1) |
| pbss_entry->bss_mode = MLAN_BSS_MODE_IBSS; |
| else |
| pbss_entry->bss_mode = MLAN_BSS_MODE_INFRA; |
| |
| if (pcap_info->spectrum_mgmt == 1) { |
| PRINTM(MINFO, "InterpretIE: 11h- Spectrum Management " |
| "capability bit found\n"); |
| pbss_entry->wlan_11h_bss_info.sensed_11h = 1; |
| } |
| |
| /* Process variable IE */ |
| while (bytes_left_for_current_beacon >= 2) { |
| element_id = (IEEEtypes_ElementId_e) (*((t_u8 *) pcurrent_ptr)); |
| element_len = *((t_u8 *) pcurrent_ptr + 1); |
| total_ie_len = element_len + sizeof(IEEEtypes_Header_t); |
| |
| if (bytes_left_for_current_beacon < total_ie_len) { |
| PRINTM(MERROR, "InterpretIE: Error in processing IE, " |
| "bytes left < IE length\n"); |
| bytes_left_for_current_beacon = 0; |
| continue; |
| } |
| |
| switch (element_id) { |
| |
| case SSID: |
| if (element_len > MRVDRV_MAX_SSID_LENGTH) { |
| bytes_left_for_current_beacon = 0; |
| continue; |
| } |
| if (!pbss_entry->ssid.ssid_len) { |
| pbss_entry->ssid.ssid_len = element_len; |
| memcpy(pmadapter, pbss_entry->ssid.ssid, |
| (pcurrent_ptr + 2), element_len); |
| } |
| PRINTM(MINFO, "InterpretIE: ssid: %-32s\n", |
| pbss_entry->ssid.ssid); |
| break; |
| |
| case SUPPORTED_RATES: |
| if (element_len > WLAN_SUPPORTED_RATES) { |
| bytes_left_for_current_beacon = 0; |
| continue; |
| } |
| memcpy(pmadapter, pbss_entry->data_rates, |
| pcurrent_ptr + 2, element_len); |
| memcpy(pmadapter, pbss_entry->supported_rates, |
| pcurrent_ptr + 2, element_len); |
| HEXDUMP("InterpretIE: SupportedRates:", |
| pbss_entry->supported_rates, element_len); |
| rate_size = element_len; |
| found_data_rate_ie = MTRUE; |
| break; |
| |
| case FH_PARAM_SET: |
| pfh_param_set = (IEEEtypes_FhParamSet_t *) pcurrent_ptr; |
| pbss_entry->network_type_use = Wlan802_11FH; |
| memcpy(pmadapter, |
| &pbss_entry->phy_param_set.fh_param_set, |
| pfh_param_set, MIN(total_ie_len, |
| sizeof |
| (IEEEtypes_FhParamSet_t))); |
| pbss_entry->phy_param_set.fh_param_set.len = |
| MIN(element_len, (sizeof(IEEEtypes_FhParamSet_t) |
| - |
| sizeof(IEEEtypes_Header_t))); |
| pbss_entry->phy_param_set.fh_param_set.dwell_time = |
| wlan_le16_to_cpu(pbss_entry->phy_param_set. |
| fh_param_set.dwell_time); |
| break; |
| |
| case DS_PARAM_SET: |
| pds_param_set = (IEEEtypes_DsParamSet_t *) pcurrent_ptr; |
| |
| pbss_entry->network_type_use = Wlan802_11DS; |
| pbss_entry->channel = pds_param_set->current_chan; |
| |
| memcpy(pmadapter, |
| &pbss_entry->phy_param_set.ds_param_set, |
| pds_param_set, MIN(total_ie_len, |
| sizeof |
| (IEEEtypes_DsParamSet_t))); |
| pbss_entry->phy_param_set.ds_param_set.len = |
| MIN(element_len, (sizeof(IEEEtypes_DsParamSet_t) |
| - |
| sizeof(IEEEtypes_Header_t))); |
| break; |
| |
| case CF_PARAM_SET: |
| pcf_param_set = (IEEEtypes_CfParamSet_t *) pcurrent_ptr; |
| memcpy(pmadapter, |
| &pbss_entry->ss_param_set.cf_param_set, |
| pcf_param_set, MIN(total_ie_len, |
| sizeof |
| (IEEEtypes_CfParamSet_t))); |
| pbss_entry->ss_param_set.cf_param_set.len = |
| MIN(element_len, (sizeof(IEEEtypes_CfParamSet_t) |
| - |
| sizeof(IEEEtypes_Header_t))); |
| break; |
| |
| case IBSS_PARAM_SET: |
| pibss_param_set = |
| (IEEEtypes_IbssParamSet_t *) pcurrent_ptr; |
| pbss_entry->atim_window = |
| wlan_le16_to_cpu(pibss_param_set->atim_window); |
| memcpy(pmadapter, |
| &pbss_entry->ss_param_set.ibss_param_set, |
| pibss_param_set, MIN(total_ie_len, |
| sizeof |
| (IEEEtypes_IbssParamSet_t))); |
| pbss_entry->ss_param_set.ibss_param_set.len = |
| MIN(element_len, |
| (sizeof(IEEEtypes_IbssParamSet_t) |
| - sizeof(IEEEtypes_Header_t))); |
| break; |
| |
| /* Handle Country Info IE */ |
| case COUNTRY_INFO: |
| pcountry_info = |
| (IEEEtypes_CountryInfoSet_t *) pcurrent_ptr; |
| |
| if (pcountry_info->len < |
| sizeof(pcountry_info->country_code) || |
| (unsigned)(pcountry_info->len + 2) > |
| sizeof(IEEEtypes_CountryInfoFullSet_t)) { |
| PRINTM(MERROR, |
| "InterpretIE: 11D- Err " |
| "country_info len =%d min=%d max=%d\n", |
| pcountry_info->len, |
| sizeof(pcountry_info->country_code), |
| sizeof(IEEEtypes_CountryInfoFullSet_t)); |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| memcpy(pmadapter, &pbss_entry->country_info, |
| pcountry_info, pcountry_info->len + 2); |
| HEXDUMP("InterpretIE: 11D- country_info:", |
| (t_u8 *) pcountry_info, |
| (t_u32) (pcountry_info->len + 2)); |
| break; |
| |
| case ERP_INFO: |
| perp_info = (IEEEtypes_ERPInfo_t *) pcurrent_ptr; |
| pbss_entry->erp_flags = perp_info->erp_flags; |
| break; |
| |
| case POWER_CONSTRAINT: |
| case POWER_CAPABILITY: |
| case TPC_REPORT: |
| case CHANNEL_SWITCH_ANN: |
| case QUIET: |
| case IBSS_DFS: |
| case SUPPORTED_CHANNELS: |
| case TPC_REQUEST: |
| wlan_11h_process_bss_elem(pmadapter, |
| &pbss_entry-> |
| wlan_11h_bss_info, |
| pcurrent_ptr); |
| break; |
| case EXTENDED_SUPPORTED_RATES: |
| /* |
| * Only process extended supported rate |
| * if data rate is already found. |
| * Data rate IE should come before |
| * extended supported rate IE |
| */ |
| if (found_data_rate_ie) { |
| if ((element_len + rate_size) > |
| WLAN_SUPPORTED_RATES) { |
| bytes_to_copy = |
| (WLAN_SUPPORTED_RATES - |
| rate_size); |
| } else { |
| bytes_to_copy = element_len; |
| } |
| |
| prate = (t_u8 *) pbss_entry->data_rates; |
| prate += rate_size; |
| memcpy(pmadapter, prate, pcurrent_ptr + 2, |
| bytes_to_copy); |
| |
| prate = (t_u8 *) pbss_entry->supported_rates; |
| prate += rate_size; |
| memcpy(pmadapter, prate, pcurrent_ptr + 2, |
| bytes_to_copy); |
| } |
| HEXDUMP("InterpretIE: ExtSupportedRates:", |
| pbss_entry->supported_rates, |
| element_len + rate_size); |
| break; |
| |
| case VENDOR_SPECIFIC_221: |
| pvendor_ie = |
| (IEEEtypes_VendorSpecific_t *) pcurrent_ptr; |
| |
| if (!memcmp |
| (pmadapter, pvendor_ie->vend_hdr.oui, wpa_oui, |
| sizeof(wpa_oui))) { |
| pbss_entry->pwpa_ie = |
| (IEEEtypes_VendorSpecific_t *) |
| pcurrent_ptr; |
| pbss_entry->wpa_offset = |
| (t_u16) (pcurrent_ptr - |
| pbss_entry->pbeacon_buf); |
| HEXDUMP("InterpretIE: Resp WPA_IE", |
| (t_u8 *) pbss_entry->pwpa_ie, |
| ((*(pbss_entry->pwpa_ie)).vend_hdr.len + |
| sizeof(IEEEtypes_Header_t))); |
| } else if (!memcmp |
| (pmadapter, pvendor_ie->vend_hdr.oui, |
| wmm_oui, sizeof(wmm_oui))) { |
| if (total_ie_len == |
| sizeof(IEEEtypes_WmmParameter_t) |
| || total_ie_len == |
| sizeof(IEEEtypes_WmmInfo_t)) { |
| |
| /* |
| * Only accept and copy the WMM IE if |
| * it matches the size expected for the |
| * WMM Info IE or the WMM Parameter IE. |
| */ |
| memcpy(pmadapter, |
| (t_u8 *) & pbss_entry->wmm_ie, |
| pcurrent_ptr, total_ie_len); |
| HEXDUMP("InterpretIE: Resp WMM_IE", |
| (t_u8 *) & pbss_entry->wmm_ie, |
| total_ie_len); |
| } |
| } 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 WAPI_IE: |
| pbss_entry->pwapi_ie = |
| (IEEEtypes_Generic_t *) pcurrent_ptr; |
| pbss_entry->wapi_offset = |
| (t_u16) (pcurrent_ptr - |
| pbss_entry->pbeacon_buf); |
| HEXDUMP("InterpretIE: Resp WAPI_IE", |
| (t_u8 *) pbss_entry->pwapi_ie, |
| (*(pbss_entry->pwapi_ie)).ieee_hdr.len + |
| sizeof(IEEEtypes_Header_t)); |
| break; |
| case HT_CAPABILITY: |
| pbss_entry->pht_cap = |
| (IEEEtypes_HTCap_t *) pcurrent_ptr; |
| pbss_entry->ht_cap_offset = |
| (t_u16) (pcurrent_ptr - |
| pbss_entry->pbeacon_buf); |
| HEXDUMP("InterpretIE: Resp HTCAP_IE", |
| (t_u8 *) pbss_entry->pht_cap, |
| (*(pbss_entry->pht_cap)).ieee_hdr.len + |
| sizeof(IEEEtypes_Header_t)); |
| break; |
| case HT_OPERATION: |
| pbss_entry->pht_info = |
| (IEEEtypes_HTInfo_t *) pcurrent_ptr; |
| pbss_entry->ht_info_offset = |
| (t_u16) (pcurrent_ptr - |
| pbss_entry->pbeacon_buf); |
| HEXDUMP("InterpretIE: Resp HTINFO_IE", |
| (t_u8 *) pbss_entry->pht_info, |
| (*(pbss_entry->pht_info)).ieee_hdr.len + |
| sizeof(IEEEtypes_Header_t)); |
| break; |
| case BSSCO_2040: |
| pbss_entry->pbss_co_2040 = |
| (IEEEtypes_2040BSSCo_t *) pcurrent_ptr; |
| pbss_entry->bss_co_2040_offset = |
| (t_u16) (pcurrent_ptr - |
| pbss_entry->pbeacon_buf); |
| HEXDUMP("InterpretIE: Resp 2040BSSCOEXISTANCE_IE", |
| (t_u8 *) pbss_entry->pbss_co_2040, |
| (*(pbss_entry->pbss_co_2040)).ieee_hdr.len + |
| sizeof(IEEEtypes_Header_t)); |
| break; |
| case EXT_CAPABILITY: |
| pbss_entry->pext_cap = |
| (IEEEtypes_ExtCap_t *) pcurrent_ptr; |
| pbss_entry->ext_cap_offset = |
| (t_u16) (pcurrent_ptr - |
| pbss_entry->pbeacon_buf); |
| HEXDUMP("InterpretIE: Resp EXTCAP_IE", |
| (t_u8 *) pbss_entry->pext_cap, |
| (*(pbss_entry->pext_cap)).ieee_hdr.len + |
| sizeof(IEEEtypes_Header_t)); |
| break; |
| case OVERLAPBSSSCANPARAM: |
| pbss_entry->poverlap_bss_scan_param = |
| (IEEEtypes_OverlapBSSScanParam_t *) |
| pcurrent_ptr; |
| pbss_entry->overlap_bss_offset = |
| (t_u16) (pcurrent_ptr - |
| pbss_entry->pbeacon_buf); |
| HEXDUMP("InterpretIE: Resp OBSS_IE", |
| (t_u8 *) pbss_entry->poverlap_bss_scan_param, |
| (*(pbss_entry->poverlap_bss_scan_param)). |
| 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(IN mlan_private * pmpriv, |
| IN BSSDescriptor_t * pbss_entry) |
| { |
| ENTER(); |
| if (pbss_entry->pbeacon_buf) { |
| if (pbss_entry->pwpa_ie) { |
| pbss_entry->pwpa_ie = (IEEEtypes_VendorSpecific_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->wpa_offset); |
| } |
| if (pbss_entry->prsn_ie) { |
| pbss_entry->prsn_ie = (IEEEtypes_Generic_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->rsn_offset); |
| } |
| if (pbss_entry->pwapi_ie) { |
| pbss_entry->pwapi_ie = (IEEEtypes_Generic_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->wapi_offset); |
| } |
| if (pbss_entry->posen_ie) { |
| pbss_entry->posen_ie = (IEEEtypes_Generic_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->osen_offset); |
| } |
| if (pbss_entry->pht_cap) { |
| pbss_entry->pht_cap = (IEEEtypes_HTCap_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->ht_cap_offset); |
| } |
| if (pbss_entry->pht_info) { |
| pbss_entry->pht_info = (IEEEtypes_HTInfo_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->ht_info_offset); |
| } |
| if (pbss_entry->pbss_co_2040) { |
| pbss_entry->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->bss_co_2040_offset); |
| } |
| if (pbss_entry->pext_cap) { |
| pbss_entry->pext_cap = (IEEEtypes_ExtCap_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->ext_cap_offset); |
| } |
| if (pbss_entry->poverlap_bss_scan_param) { |
| pbss_entry->poverlap_bss_scan_param = |
| (IEEEtypes_OverlapBSSScanParam_t *) |
| (pbss_entry->pbeacon_buf + |
| pbss_entry->overlap_bss_offset); |
| } |
| } else { |
| pbss_entry->pwpa_ie = MNULL; |
| pbss_entry->wpa_offset = 0; |
| pbss_entry->prsn_ie = MNULL; |
| pbss_entry->rsn_offset = 0; |
| pbss_entry->pwapi_ie = MNULL; |
| pbss_entry->wapi_offset = 0; |
| pbss_entry->posen_ie = MNULL; |
| pbss_entry->osen_offset = 0; |
| pbss_entry->pht_cap = MNULL; |
| pbss_entry->ht_cap_offset = 0; |
| pbss_entry->pht_info = MNULL; |
| pbss_entry->ht_info_offset = 0; |
| pbss_entry->pbss_co_2040 = MNULL; |
| pbss_entry->bss_co_2040_offset = 0; |
| pbss_entry->pext_cap = MNULL; |
| pbss_entry->ext_cap_offset = 0; |
| pbss_entry->poverlap_bss_scan_param = MNULL; |
| pbss_entry->overlap_bss_offset = 0; |
| } |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief Store a beacon or probe response for a BSS returned in the scan |
| * |
| * Store a new scan response or an update for a previous scan response. New |
| * entries need to verify that they do not exceed the total amount of |
| * memory allocated for the table. |
| |
| * Replacement entries need to take into consideration the amount of space |
| * currently allocated for the beacon/probe response and adjust the entry |
| * as needed. |
| * |
| * A small amount of extra pad (SCAN_BEACON_ENTRY_PAD) is generally reserved |
| * for an entry in case it is a beacon since a probe response for the |
| * network will by larger per the standard. This helps to reduce the |
| * amount of memory copying to fit a new probe response into an entry |
| * already occupied by a network's previously stored beacon. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param beacon_idx Index in the scan table to store this entry; may be |
| * replacing an older duplicate entry for this BSS |
| * @param num_of_ent Number of entries currently in the table |
| * @param pnew_beacon Pointer to the new beacon/probe response to save |
| * |
| * @return N/A |
| */ |
| static t_void |
| wlan_ret_802_11_scan_store_beacon(IN mlan_private * pmpriv, |
| IN t_u32 beacon_idx, |
| IN t_u32 num_of_ent, |
| IN BSSDescriptor_t * pnew_beacon) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| t_u8 *pbcn_store; |
| t_u32 new_bcn_size; |
| t_u32 old_bcn_size; |
| t_u32 bcn_space; |
| t_u32 adj_idx; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u8 *tmp_buf; |
| t_u16 bcn_size = 0; |
| t_u32 bcn_offset = 0; |
| |
| ENTER(); |
| |
| if (pmadapter->pscan_table[beacon_idx].pbeacon_buf) { |
| |
| new_bcn_size = pnew_beacon->beacon_buf_size; |
| old_bcn_size = |
| pmadapter->pscan_table[beacon_idx].beacon_buf_size; |
| bcn_space = |
| pmadapter->pscan_table[beacon_idx].beacon_buf_size_max; |
| pbcn_store = pmadapter->pscan_table[beacon_idx].pbeacon_buf; |
| |
| /* Set the max to be the same as current entry unless changed |
| below */ |
| pnew_beacon->beacon_buf_size_max = bcn_space; |
| |
| if (new_bcn_size == old_bcn_size) { |
| /* |
| * Beacon is the same size as the previous entry. |
| * Replace the previous contents with the scan result |
| */ |
| memcpy(pmadapter, pbcn_store, |
| pnew_beacon->pbeacon_buf, |
| pnew_beacon->beacon_buf_size); |
| |
| } else if (new_bcn_size <= bcn_space) { |
| /* |
| * New beacon size will fit in the amount of space |
| * we have previously allocated for it |
| */ |
| |
| /* Copy the new beacon buffer entry over the old one */ |
| memcpy(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, |
| new_bcn_size); |
| |
| /* |
| * If the old beacon size was less than the |
| * maximum we had allotted for the entry, and |
| * the new entry is even smaller, reset the |
| * max size to the old beacon entry and compress |
| * the storage space (leaving a new pad space of |
| * (old_bcn_size - new_bcn_size). |
| */ |
| if (old_bcn_size < bcn_space && |
| new_bcn_size <= old_bcn_size) { |
| /* |
| * Old Beacon size is smaller than the |
| * allotted storage size. Shrink the |
| * allotted storage space. |
| */ |
| PRINTM(MINFO, |
| "AppControl: Smaller Duplicate Beacon (%d), " |
| "old = %d, new = %d, space = %d, left = %d\n", |
| beacon_idx, old_bcn_size, new_bcn_size, |
| bcn_space, |
| (pmadapter->bcn_buf_size - |
| (pmadapter->pbcn_buf_end - |
| pmadapter->bcn_buf))); |
| |
| /* |
| * memmove (since the memory overlaps) the data |
| * after the beacon we just stored to the end |
| * of the current beacon. This cleans up any |
| * unused space the old larger beacon was using |
| * in the buffer |
| */ |
| memmove(pmadapter, |
| (void *)((t_ptr) pbcn_store + |
| (t_ptr) old_bcn_size), |
| (void *)((t_ptr) pbcn_store + |
| (t_ptr) bcn_space), |
| (t_u32) ((t_ptr) pmadapter-> |
| pbcn_buf_end - |
| ((t_ptr) pbcn_store + |
| (t_ptr) bcn_space))); |
| |
| /* |
| * Decrement the end pointer by the difference |
| * between the old larger size and the new |
| * smaller size since we are using less space |
| * due to the new beacon being smaller |
| */ |
| pmadapter->pbcn_buf_end -= |
| (bcn_space - old_bcn_size); |
| |
| /* Set the maximum storage size to the old * |
| beacon size */ |
| pnew_beacon->beacon_buf_size_max = old_bcn_size; |
| |
| /* Adjust beacon buffer pointers that are past |
| the current */ |
| for (adj_idx = 0; adj_idx < num_of_ent; |
| adj_idx++) { |
| if (pmadapter->pscan_table[adj_idx]. |
| pbeacon_buf > pbcn_store) { |
| pmadapter->pscan_table[adj_idx]. |
| pbeacon_buf -= |
| (bcn_space - |
| old_bcn_size); |
| wlan_adjust_ie_in_bss_entry |
| (pmpriv, |
| &pmadapter-> |
| pscan_table[adj_idx]); |
| } |
| } |
| } |
| } else if (pmadapter->pbcn_buf_end + (new_bcn_size - bcn_space) |
| < (pmadapter->bcn_buf + pmadapter->bcn_buf_size)) { |
| /* |
| * Beacon is larger than space previously allocated |
| * (bcn_space) and there is enough space left in the |
| * beaconBuffer to store the additional data |
| */ |
| PRINTM(MINFO, |
| "AppControl: Larger Duplicate Beacon (%d), " |
| "old = %d, new = %d, space = %d, left = %d\n", |
| beacon_idx, old_bcn_size, new_bcn_size, |
| bcn_space, |
| (pmadapter->bcn_buf_size - |
| (pmadapter->pbcn_buf_end - |
| pmadapter->bcn_buf))); |
| |
| /* |
| * memmove (since the memory overlaps) the data |
| * after the beacon we just stored to the end of |
| * the current beacon. This moves the data for |
| * the beacons after this further in memory to |
| * make space for the new larger beacon we are |
| * about to copy in. |
| */ |
| memmove(pmadapter, |
| (void *)((t_ptr) pbcn_store + |
| (t_ptr) new_bcn_size), |
| (void *)((t_ptr) pbcn_store + |
| (t_ptr) bcn_space), |
| (t_u32) ((t_ptr) pmadapter->pbcn_buf_end - |
| ((t_ptr) pbcn_store + |
| (t_ptr) bcn_space))); |
| |
| /* Copy the new beacon buffer entry over the old one */ |
| memcpy(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, |
| new_bcn_size); |
| |
| /* Move the beacon end pointer by the amount of new * |
| beacon data we are adding */ |
| pmadapter->pbcn_buf_end += (new_bcn_size - bcn_space); |
| |
| /* |
| * This entry is bigger than the allotted max space |
| * previously reserved. Increase the max space to |
| * be equal to the new beacon size |
| */ |
| pnew_beacon->beacon_buf_size_max = new_bcn_size; |
| |
| /* Adjust beacon buffer pointers that are past the |
| current */ |
| for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) { |
| if (pmadapter->pscan_table[adj_idx]. |
| pbeacon_buf > pbcn_store) { |
| pmadapter->pscan_table[adj_idx]. |
| pbeacon_buf += |
| (new_bcn_size - bcn_space); |
| wlan_adjust_ie_in_bss_entry(pmpriv, |
| &pmadapter-> |
| pscan_table |
| [adj_idx]); |
| } |
| } |
| } else { |
| /* |
| * Beacon is larger than the previously allocated |
| * space, but there is not enough free space to |
| * store the additional data |
| */ |
| PRINTM(MERROR, |
| "AppControl: Failed: Larger Duplicate Beacon (%d)," |
| " old = %d, new = %d, space = %d, left = %d\n", |
| beacon_idx, old_bcn_size, new_bcn_size, |
| bcn_space, |
| (pmadapter->bcn_buf_size - |
| (pmadapter->pbcn_buf_end - |
| pmadapter->bcn_buf))); |
| |
| /* Storage failure, keep old beacon intact */ |
| pnew_beacon->beacon_buf_size = old_bcn_size; |
| if (pnew_beacon->pwpa_ie) |
| pnew_beacon->wpa_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| wpa_offset; |
| if (pnew_beacon->prsn_ie) |
| pnew_beacon->rsn_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| rsn_offset; |
| if (pnew_beacon->pwapi_ie) |
| pnew_beacon->wapi_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| wapi_offset; |
| if (pnew_beacon->posen_ie) |
| pnew_beacon->osen_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| osen_offset; |
| if (pnew_beacon->pht_cap) |
| pnew_beacon->ht_cap_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| ht_cap_offset; |
| if (pnew_beacon->pht_info) |
| pnew_beacon->ht_info_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| ht_info_offset; |
| if (pnew_beacon->pbss_co_2040) |
| pnew_beacon->bss_co_2040_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| bss_co_2040_offset; |
| if (pnew_beacon->pext_cap) |
| pnew_beacon->ext_cap_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| ext_cap_offset; |
| if (pnew_beacon->poverlap_bss_scan_param) |
| pnew_beacon->overlap_bss_offset = |
| pmadapter->pscan_table[beacon_idx]. |
| overlap_bss_offset; |
| } |
| /* Point the new entry to its permanent storage space */ |
| pnew_beacon->pbeacon_buf = pbcn_store; |
| wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); |
| } else { |
| if ((pmadapter->pbcn_buf_end + pnew_beacon->beacon_buf_size + |
| SCAN_BEACON_ENTRY_PAD > (pmadapter->bcn_buf + |
| pmadapter->bcn_buf_size)) && |
| (pmadapter->bcn_buf_size < MAX_SCAN_BEACON_BUFFER)) { |
| /* no space for this entry, realloc bcn buffer */ |
| 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(pmadapter, tmp_buf, pmadapter->bcn_buf, |
| bcn_size); |
| /* Adjust beacon buffer pointers that are past |
| the current */ |
| for (adj_idx = 0; adj_idx < num_of_ent; |
| adj_idx++) { |
| bcn_offset = |
| pmadapter->pscan_table[adj_idx]. |
| pbeacon_buf - |
| pmadapter->bcn_buf; |
| pmadapter->pscan_table[adj_idx]. |
| pbeacon_buf = |
| tmp_buf + bcn_offset; |
| wlan_adjust_ie_in_bss_entry(pmpriv, |
| &pmadapter-> |
| pscan_table |
| [adj_idx]); |
| } |
| pmadapter->pbcn_buf_end = tmp_buf + bcn_size; |
| 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(pmadapter, pmadapter->pbcn_buf_end, |
| pnew_beacon->pbeacon_buf, |
| pnew_beacon->beacon_buf_size); |
| |
| /* Update the beacon ptr to point to the table * save |
| area */ |
| pnew_beacon->pbeacon_buf = pmadapter->pbcn_buf_end; |
| pnew_beacon->beacon_buf_size_max = |
| (pnew_beacon->beacon_buf_size + |
| SCAN_BEACON_ENTRY_PAD); |
| wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); |
| |
| /* Increment the end pointer by the size reserved */ |
| pmadapter->pbcn_buf_end += |
| pnew_beacon->beacon_buf_size_max; |
| |
| PRINTM(MINFO, "AppControl: Beacon[%02d] sz=%03d," |
| " used = %04d, left = %04d\n", |
| beacon_idx, |
| pnew_beacon->beacon_buf_size, |
| (pmadapter->pbcn_buf_end - pmadapter->bcn_buf), |
| (pmadapter->bcn_buf_size - |
| (pmadapter->pbcn_buf_end - |
| pmadapter->bcn_buf))); |
| } else { |
| /* |
| * No space for new beacon |
| */ |
| PRINTM(MCMND, "AppControl: No space beacon (%d): " |
| 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 Restore a 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_restore_curr_bcn(IN mlan_private * pmpriv) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = (pmlan_callbacks) & pmadapter->callbacks; |
| BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (pmpriv->pcurr_bcn_buf && |
| ((pmadapter->pbcn_buf_end + pmpriv->curr_bcn_size) < |
| (pmadapter->bcn_buf + pmadapter->bcn_buf_size))) { |
| |
| pcb->moal_spin_lock(pmadapter->pmoal_handle, |
| pmpriv->curr_bcn_buf_lock); |
| |
| /* restore the current beacon buffer */ |
| memcpy(pmadapter, pmadapter->pbcn_buf_end, |
| pmpriv->pcurr_bcn_buf, pmpriv->curr_bcn_size); |
| pcurr_bss->pbeacon_buf = pmadapter->pbcn_buf_end; |
| pcurr_bss->beacon_buf_size = pmpriv->curr_bcn_size; |
| pcurr_bss->beacon_buf_size_max = |
| pmpriv->curr_bcn_size + SCAN_BEACON_ENTRY_PAD; |
| pmadapter->pbcn_buf_end += pcurr_bss->beacon_buf_size_max; |
| |
| /* adjust the pointers in the current bss descriptor */ |
| if (pcurr_bss->pwpa_ie) { |
| pcurr_bss->pwpa_ie = (IEEEtypes_VendorSpecific_t *) |
| (pcurr_bss->pbeacon_buf + |
| pcurr_bss->wpa_offset); |
| } |
| if (pcurr_bss->prsn_ie) { |
| pcurr_bss->prsn_ie = (IEEEtypes_Generic_t *) |
| (pcurr_bss->pbeacon_buf + |
| pcurr_bss->rsn_offset); |
| } |
| if (pcurr_bss->pht_cap) { |
| pcurr_bss->pht_cap = (IEEEtypes_HTCap_t *) |
| (pcurr_bss->pbeacon_buf + |
| pcurr_bss->ht_cap_offset); |
| } |
| |
| if (pcurr_bss->pht_info) { |
| pcurr_bss->pht_info = (IEEEtypes_HTInfo_t *) |
| (pcurr_bss->pbeacon_buf + |
| pcurr_bss->ht_info_offset); |
| } |
| |
| if (pcurr_bss->pbss_co_2040) { |
| pcurr_bss->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *) |
| (pcurr_bss->pbeacon_buf + |
| pcurr_bss->bss_co_2040_offset); |
| } |
| |
| if (pcurr_bss->pext_cap) { |
| pcurr_bss->pext_cap = (IEEEtypes_ExtCap_t *) |
| (pcurr_bss->pbeacon_buf + |
| pcurr_bss->ext_cap_offset); |
| } |
| |
| if (pcurr_bss->poverlap_bss_scan_param) { |
| pcurr_bss->poverlap_bss_scan_param = |
| (IEEEtypes_OverlapBSSScanParam_t *) |
| (pcurr_bss->pbeacon_buf + |
| pcurr_bss->overlap_bss_offset); |
| } |
| |
| pcb->moal_spin_unlock(pmadapter->pmoal_handle, |
| pmpriv->curr_bcn_buf_lock); |
| |
| PRINTM(MINFO, "current beacon restored %d\n", |
| pmpriv->curr_bcn_size); |
| } else { |
| PRINTM(MWARN, |
| "curr_bcn_buf not saved or bcn_buf has no space\n"); |
| ret = MLAN_STATUS_FAILURE; |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Post process the scan table after a new scan command has completed |
| * |
| * Inspect each entry of the scan table and try to find an entry that |
| * matches our current associated/joined network from the scan. If |
| * one is found, update the stored copy of the BSSDescriptor for our |
| * current network. |
| * |
| * Debug dump the current scan table contents if compiled accordingly. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * |
| * @return N/A |
| */ |
| static t_void |
| wlan_scan_process_results(IN mlan_private * pmpriv) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| t_s32 j; |
| t_u32 i; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (pmpriv->media_connected == MTRUE) { |
| |
| j = wlan_find_ssid_in_list(pmpriv, |
| &pmpriv->curr_bss_params. |
| bss_descriptor.ssid, |
| pmpriv->curr_bss_params. |
| bss_descriptor.mac_address, |
| pmpriv->bss_mode); |
| |
| if (j >= 0) { |
| pmadapter->callbacks.moal_spin_lock(pmadapter-> |
| pmoal_handle, |
| pmpriv-> |
| curr_bcn_buf_lock); |
| pmpriv->curr_bss_params.bss_descriptor.pwpa_ie = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.wpa_offset = 0; |
| pmpriv->curr_bss_params.bss_descriptor.prsn_ie = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.rsn_offset = 0; |
| pmpriv->curr_bss_params.bss_descriptor.pwapi_ie = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.wapi_offset = 0; |
| pmpriv->curr_bss_params.bss_descriptor.posen_ie = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.osen_offset = 0; |
| pmpriv->curr_bss_params.bss_descriptor.pht_cap = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.ht_cap_offset = |
| 0; |
| pmpriv->curr_bss_params.bss_descriptor.pht_info = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.ht_info_offset = |
| 0; |
| pmpriv->curr_bss_params.bss_descriptor.pbss_co_2040 = |
| MNULL; |
| pmpriv->curr_bss_params.bss_descriptor. |
| bss_co_2040_offset = 0; |
| pmpriv->curr_bss_params.bss_descriptor.pext_cap = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.ext_cap_offset = |
| 0; |
| pmpriv->curr_bss_params.bss_descriptor. |
| poverlap_bss_scan_param = MNULL; |
| pmpriv->curr_bss_params.bss_descriptor. |
| overlap_bss_offset = 0; |
| pmpriv->curr_bss_params.bss_descriptor.pbeacon_buf = |
| MNULL; |
| pmpriv->curr_bss_params.bss_descriptor.beacon_buf_size = |
| 0; |
| pmpriv->curr_bss_params.bss_descriptor. |
| beacon_buf_size_max = 0; |
| |
| PRINTM(MINFO, |
| "Found current ssid/bssid in list @ index #%d\n", |
| j); |
| /* Make a copy of current BSSID descriptor */ |
| memcpy(pmadapter, |
| &pmpriv->curr_bss_params.bss_descriptor, |
| &pmadapter->pscan_table[j], |
| sizeof(pmpriv->curr_bss_params.bss_descriptor)); |
| |
| wlan_save_curr_bcn(pmpriv); |
| pmadapter->callbacks.moal_spin_unlock(pmadapter-> |
| pmoal_handle, |
| pmpriv-> |
| curr_bcn_buf_lock); |
| } else { |
| ret = wlan_restore_curr_bcn(pmpriv); |
| /** append current AP to the end of scan table when restore curr_bcn success */ |
| if (ret == MLAN_STATUS_SUCCESS) { |
| if (pmadapter->num_in_scan_table < |
| MRVDRV_MAX_BSSID_LIST) |
| pmadapter->num_in_scan_table++; |
| memcpy(pmadapter, |
| &pmadapter->pscan_table[pmadapter-> |
| num_in_scan_table |
| - 1], |
| &pmpriv->curr_bss_params.bss_descriptor, |
| sizeof(pmpriv->curr_bss_params. |
| bss_descriptor)); |
| } |
| } |
| |
| } |
| |
| 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); |
| |
| /* |
| * Prepares domain info from scan table and downloads the |
| * domain info command to the FW. |
| */ |
| wlan_11d_prepare_dnld_domain_info_cmd(pmpriv); |
| PRINTM(MINFO, "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(IN mlan_private * pmpriv, IN t_s32 table_idx) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| t_u32 del_idx; |
| t_u32 beacon_buf_adj; |
| t_u8 *pbeacon_buf; |
| |
| ENTER(); |
| |
| /* |
| * Shift the saved beacon buffer data for the scan table back over the |
| * entry being removed. Update the end of buffer pointer. Save the |
| * deleted buffer allocation size for pointer adjustments for entries |
| * compacted after the deleted index. |
| */ |
| beacon_buf_adj = pmadapter->pscan_table[table_idx].beacon_buf_size_max; |
| |
| PRINTM(MINFO, |
| "Scan: Delete Entry %d, beacon buffer removal = %d bytes\n", |
| table_idx, beacon_buf_adj); |
| |
| /* Check if the table entry had storage allocated for its beacon */ |
| if (beacon_buf_adj) { |
| pbeacon_buf = pmadapter->pscan_table[table_idx].pbeacon_buf; |
| |
| /* |
| * Remove the entry's buffer space, decrement the table |
| * end pointer by the amount we are removing |
| */ |
| pmadapter->pbcn_buf_end -= beacon_buf_adj; |
| |
| PRINTM(MINFO, |
| "Scan: Delete Entry %d, compact data: %p <- %p (sz = %d)\n", |
| table_idx, |
| pbeacon_buf, |
| pbeacon_buf + beacon_buf_adj, |
| pmadapter->pbcn_buf_end - pbeacon_buf); |
| |
| /* |
| * Compact data storage. Copy all data after the deleted entry's |
| * end address (pbeacon_buf + beacon_buf_adj) back to the original |
| * start address (pbeacon_buf). |
| * |
| * Scan table entries affected by the move will have their entry |
| * pointer adjusted below. |
| * |
| * Use memmove since the dest/src memory regions overlap. |
| */ |
| memmove(pmadapter, pbeacon_buf, |
| (void *)((t_ptr) pbeacon_buf + (t_ptr) beacon_buf_adj), |
| (t_u32) ((t_ptr) pmadapter->pbcn_buf_end - |
| (t_ptr) pbeacon_buf)); |
| } |
| |
| PRINTM(MINFO, "Scan: Delete Entry %d, num_in_scan_table = %d\n", |
| table_idx, pmadapter->num_in_scan_table); |
| |
| /* Shift all of the entries after the table_idx back by one, compacting |
| * the table and removing the requested entry */ |
| for (del_idx = table_idx; (del_idx + 1) < pmadapter->num_in_scan_table; |
| del_idx++) { |
| /* Copy the next entry over this one */ |
| memcpy(pmadapter, pmadapter->pscan_table + del_idx, |
| pmadapter->pscan_table + del_idx + 1, |
| sizeof(BSSDescriptor_t)); |
| |
| /* |
| * Adjust this entry's pointer to its beacon buffer based on the |
| * removed/compacted entry from the deleted index. Don't decrement |
| * if the buffer pointer is MNULL (no data stored for this entry). |
| */ |
| if (pmadapter->pscan_table[del_idx].pbeacon_buf) { |
| pmadapter->pscan_table[del_idx].pbeacon_buf -= |
| beacon_buf_adj; |
| if (pmadapter->pscan_table[del_idx].pwpa_ie) { |
| pmadapter->pscan_table[del_idx].pwpa_ie = |
| (IEEEtypes_VendorSpecific_t *) |
| (pmadapter->pscan_table[del_idx]. |
| pbeacon_buf + |
| pmadapter->pscan_table[del_idx]. |
| wpa_offset); |
| } |
| if (pmadapter->pscan_table[del_idx].prsn_ie) { |
| pmadapter->pscan_table[del_idx].prsn_ie = |
| (IEEEtypes_Generic_t *) |
| (pmadapter->pscan_table[del_idx]. |
| pbeacon_buf + |
| pmadapter->pscan_table[del_idx]. |
| rsn_offset); |
| } |
| if (pmadapter->pscan_table[del_idx].pwapi_ie) { |
| pmadapter->pscan_table[del_idx].pwapi_ie = |
| (IEEEtypes_Generic_t *) |
| (pmadapter->pscan_table[del_idx]. |
| pbeacon_buf + |
| pmadapter->pscan_table[del_idx]. |
| wapi_offset); |
| } |
| if (pmadapter->pscan_table[del_idx].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].pht_cap) { |
| pmadapter->pscan_table[del_idx].pht_cap = |
| (IEEEtypes_HTCap_t *) (pmadapter-> |
| pscan_table |
| [del_idx]. |
| pbeacon_buf + |
| pmadapter-> |
| pscan_table |
| [del_idx]. |
| ht_cap_offset); |
| } |
| |
| if (pmadapter->pscan_table[del_idx].pht_info) { |
| pmadapter->pscan_table[del_idx].pht_info = |
| (IEEEtypes_HTInfo_t *) (pmadapter-> |
| pscan_table |
| [del_idx]. |
| pbeacon_buf + |
| pmadapter-> |
| pscan_table |
| [del_idx]. |
| ht_info_offset); |
| } |
| if (pmadapter->pscan_table[del_idx].pbss_co_2040) { |
| pmadapter->pscan_table[del_idx].pbss_co_2040 = |
| (IEEEtypes_2040BSSCo_t *) (pmadapter-> |
| pscan_table |
| [del_idx]. |
| pbeacon_buf + |
| pmadapter-> |
| pscan_table |
| [del_idx]. |
| bss_co_2040_offset); |
| } |
| if (pmadapter->pscan_table[del_idx].pext_cap) { |
| pmadapter->pscan_table[del_idx].pext_cap = |
| (IEEEtypes_ExtCap_t *) (pmadapter-> |
| pscan_table |
| [del_idx]. |
| pbeacon_buf + |
| pmadapter-> |
| pscan_table |
| [del_idx]. |
| ext_cap_offset); |
| } |
| if (pmadapter->pscan_table[del_idx]. |
| poverlap_bss_scan_param) { |
| pmadapter->pscan_table[del_idx]. |
| poverlap_bss_scan_param = |
| (IEEEtypes_OverlapBSSScanParam_t |
| *) (pmadapter->pscan_table[del_idx]. |
| pbeacon_buf + |
| pmadapter->pscan_table[del_idx]. |
| overlap_bss_offset); |
| } |
| |
| } |
| } |
| |
| /* The last entry is invalid now that it has been deleted or moved back |
| */ |
| memset(pmadapter, |
| pmadapter->pscan_table + pmadapter->num_in_scan_table - 1, 0x00, |
| sizeof(BSSDescriptor_t)); |
| |
| pmadapter->num_in_scan_table--; |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Delete all occurrences of a given SSID from the scan table |
| * |
| * Iterate through the scan table and delete all entries that match a given |
| * SSID. Compact the remaining scan table entries. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pdel_ssid Pointer to an SSID to be used in deleting all |
| * matching SSIDs from the scan table |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_scan_delete_ssid_table_entry(IN mlan_private * pmpriv, |
| IN mlan_802_11_ssid * pdel_ssid) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_s32 table_idx; |
| |
| ENTER(); |
| |
| PRINTM(MINFO, "Scan: Delete Ssid Entry: %-32s\n", pdel_ssid->ssid); |
| |
| /* If the requested SSID is found in the table, delete it. Then keep * |
| searching the table for multiple entries for the SSID until no * |
| more are found */ |
| while ((table_idx = wlan_find_ssid_in_list(pmpriv, |
| pdel_ssid, |
| MNULL, |
| MLAN_BSS_MODE_AUTO)) >= 0) { |
| PRINTM(MINFO, "Scan: Delete SSID Entry: Found Idx = %d\n", |
| table_idx); |
| ret = MLAN_STATUS_SUCCESS; |
| wlan_scan_delete_table_entry(pmpriv, table_idx); |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| |
| /** |
| * @brief Check if a scanned network compatible with the driver settings |
| * |
| * WEP WPA WPA2 ad-hoc encrypt Network |
| * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible |
| * 0 0 0 0 NONE 0 0 0 yes No security |
| * 0 1 0 0 x 1x 1 x yes WPA (disable HT if no AES) |
| * 0 0 1 0 x 1x x 1 yes WPA2 (disable HT if no AES) |
| * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES |
| * 1 0 0 0 NONE 1 0 0 yes Static WEP (disable HT) |
| * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP |
| * |
| * @param pmpriv A pointer to mlan_private |
| * @param index Index in scan table to check against current driver settings |
| * @param mode Network mode: Infrastructure or IBSS |
| * |
| * @return Index in ScanTable, or negative value if error |
| */ |
| t_s32 |
| wlan_is_network_compatible(IN mlan_private * pmpriv, |
| IN t_u32 index, IN t_u32 mode) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| BSSDescriptor_t *pbss_desc; |
| |
| ENTER(); |
| |
| pbss_desc = &pmadapter->pscan_table[index]; |
| pbss_desc->disable_11n = MFALSE; |
| |
| /* Don't check for compatibility if roaming */ |
| if ((pmpriv->media_connected == MTRUE) |
| && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) |
| && (pbss_desc->bss_mode == MLAN_BSS_MODE_INFRA)) { |
| LEAVE(); |
| return index; |
| } |
| |
| if (pbss_desc->wlan_11h_bss_info.chan_switch_ann.element_id == |
| CHANNEL_SWITCH_ANN) { |
| PRINTM(MINFO, |
| "Don't connect to AP with CHANNEL_SWITCH_ANN IE.\n"); |
| LEAVE(); |
| return -1; |
| } |
| |
| if (pmpriv->wps.session_enable == MTRUE) { |
| PRINTM(MINFO, "Return success directly in WPS period\n"); |
| LEAVE(); |
| return index; |
| } |
| |
| if (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)) { |
| if (((pbss_desc->pwpa_ie) && |
| ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE)) || |
| ((pbss_desc->prsn_ie) && |
| ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE))) { |
| if (((pmpriv->adapter->config_bands & BAND_GN || |
| pmpriv->adapter->config_bands & BAND_AN) && |
| pbss_desc->pht_cap) |
| && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) |
| && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, |
| CIPHER_SUITE_CCMP) |
| && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, |
| CIPHER_SUITE_CCMP)) { |
| |
| if (is_wpa_oui_present |
| (pmpriv->adapter, pbss_desc, |
| CIPHER_SUITE_TKIP) |
| || is_rsn_oui_present(pmpriv->adapter, |
| pbss_desc, |
| CIPHER_SUITE_TKIP)) { |
| PRINTM(MINFO, |
| "Disable 11n if AES is not supported by AP\n"); |
| pbss_desc->disable_11n = MTRUE; |
| } else { |
| LEAVE(); |
| return -1; |
| } |
| } |
| LEAVE(); |
| return index; |
| } else { |
| PRINTM(MINFO, |
| "ewpa_enabled: Ignore none WPA/WPA2 AP\n"); |
| LEAVE(); |
| return -1; |
| } |
| } |
| |
| if (pmpriv->sec_info.wapi_enabled && |
| (pbss_desc->pwapi_ie && |
| ((*(pbss_desc->pwapi_ie)).ieee_hdr.element_id == WAPI_IE))) { |
| PRINTM(MINFO, "Return success for WAPI AP\n"); |
| LEAVE(); |
| return index; |
| } |
| |
| if (pbss_desc->bss_mode == mode) { |
| if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled |
| && !pmpriv->sec_info.wpa_enabled |
| && !pmpriv->sec_info.wpa2_enabled |
| && ((!pbss_desc->pwpa_ie) || |
| ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != WPA_IE)) |
| && ((!pbss_desc->prsn_ie) || |
| ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != RSN_IE)) |
| && !pmpriv->adhoc_aes_enabled && |
| pmpriv->sec_info.encryption_mode == |
| MLAN_ENCRYPTION_MODE_NONE && !pbss_desc->privacy) { |
| /* No security */ |
| LEAVE(); |
| return index; |
| } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled |
| && !pmpriv->sec_info.wpa_enabled |
| && !pmpriv->sec_info.wpa2_enabled |
| && !pmpriv->adhoc_aes_enabled |
| && pbss_desc->privacy) { |
| /* Static WEP enabled */ |
| PRINTM(MINFO, "Disable 11n in WEP mode\n"); |
| pbss_desc->disable_11n = MTRUE; |
| /* 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)) |
| && !pmpriv->adhoc_aes_enabled |
| /* |
| * Privacy bit may NOT be set in some APs like |
| * LinkSys WRT54G && pbss_desc->privacy |
| */ |
| ) { |
| /* WPA enabled */ |
| PRINTM(MINFO, |
| "wlan_is_network_compatible() WPA: index=%d wpa_ie=%#x " |
| "rsn_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x " |
| "privacy=%#x\n", index, |
| (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)). |
| vend_hdr.element_id : 0, |
| (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)). |
| ieee_hdr.element_id : 0, |
| (pmpriv->sec_info.wep_status == |
| Wlan802_11WEPEnabled) ? "e" : "d", |
| (pmpriv->sec_info.wpa_enabled) ? "e" : "d", |
| (pmpriv->sec_info.wpa2_enabled) ? "e" : "d", |
| pmpriv->sec_info.encryption_mode, |
| pbss_desc->privacy); |
| if (((pmpriv->adapter->config_bands & BAND_GN || |
| pmpriv->adapter->config_bands & BAND_AN) && |
| pbss_desc->pht_cap) |
| && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) |
| && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, |
| CIPHER_SUITE_CCMP)) { |
| if (is_wpa_oui_present |
| (pmpriv->adapter, pbss_desc, |
| CIPHER_SUITE_TKIP)) { |
| PRINTM(MINFO, |
| "Disable 11n if AES is not supported by AP\n"); |
| pbss_desc->disable_11n = MTRUE; |
| } else { |
| LEAVE(); |
| return -1; |
| } |
| } |
| LEAVE(); |
| return index; |
| } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled |
| && !pmpriv->sec_info.wpa_enabled |
| && pmpriv->sec_info.wpa2_enabled |
| && ((pbss_desc->prsn_ie) && |
| ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == |
| RSN_IE)) |
| && !pmpriv->adhoc_aes_enabled |
| /* |
| * Privacy bit may NOT be set in some APs like |
| * LinkSys WRT54G && pbss_desc->privacy |
| */ |
| ) { |
| /* WPA2 enabled */ |
| PRINTM(MINFO, |
| "wlan_is_network_compatible() WPA2: index=%d wpa_ie=%#x " |
| "rsn_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x " |
| "privacy=%#x\n", index, |
| (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)). |
| vend_hdr.element_id : 0, |
| (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)). |
| ieee_hdr.element_id : 0, |
| (pmpriv->sec_info.wep_status == |
| Wlan802_11WEPEnabled) ? "e" : "d", |
| (pmpriv->sec_info.wpa_enabled) ? "e" : "d", |
| (pmpriv->sec_info.wpa2_enabled) ? "e" : "d", |
| pmpriv->sec_info.encryption_mode, |
| pbss_desc->privacy); |
| if (((pmpriv->adapter->config_bands & BAND_GN || |
| pmpriv->adapter->config_bands & BAND_AN) && |
| pbss_desc->pht_cap) |
| && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) |
| && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, |
| CIPHER_SUITE_CCMP)) { |
| if (is_rsn_oui_present |
| (pmpriv->adapter, pbss_desc, |
| CIPHER_SUITE_TKIP)) { |
| PRINTM(MINFO, |
| "Disable 11n if AES is not supported by AP\n"); |
| pbss_desc->disable_11n = MTRUE; |
| } else { |
| LEAVE(); |
| return -1; |
| } |
| } |
| LEAVE(); |
| return index; |
| } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled |
| && !pmpriv->sec_info.wpa_enabled |
| && !pmpriv->sec_info.wpa2_enabled |
| && ((!pbss_desc->pwpa_ie) || |
| ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != |
| WPA_IE)) |
| && ((!pbss_desc->prsn_ie) || |
| ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != |
| RSN_IE)) |
| && pmpriv->adhoc_aes_enabled && |
| pmpriv->sec_info.encryption_mode == |
| MLAN_ENCRYPTION_MODE_NONE && pbss_desc->privacy) { |
| /* Ad-hoc AES enabled */ |
| LEAVE(); |
| return index; |
| } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled |
| && !pmpriv->sec_info.wpa_enabled |
| && !pmpriv->sec_info.wpa2_enabled |
| && ((!pbss_desc->pwpa_ie) || |
| ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != |
| WPA_IE)) |
| && ((!pbss_desc->prsn_ie) || |
| ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != |
| RSN_IE)) |
| && !pmpriv->adhoc_aes_enabled && |
| pmpriv->sec_info.encryption_mode != |
| MLAN_ENCRYPTION_MODE_NONE && pbss_desc->privacy) { |
| /* Dynamic WEP enabled */ |
| PRINTM(MINFO, |
| "wlan_is_network_compatible() dynamic WEP: index=%d " |
| "wpa_ie=%#x rsn_ie=%#x EncMode=%#x privacy=%#x\n", |
| index, |
| (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)). |
| vend_hdr.element_id : 0, |
| (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)). |
| ieee_hdr.element_id : 0, |
| pmpriv->sec_info.encryption_mode, |
| pbss_desc->privacy); |
| LEAVE(); |
| return index; |
| } |
| |
| /* Security doesn't match */ |
| PRINTM(MINFO, |
| "wlan_is_network_compatible() FAILED: index=%d wpa_ie=%#x " |
| "rsn_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", |
| index, |
| (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)).vend_hdr. |
| element_id : 0, |
| (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)).ieee_hdr. |
| element_id : 0, |
| (pmpriv->sec_info.wep_status == |
| Wlan802_11WEPEnabled) ? "e" : "d", |
| (pmpriv->sec_info.wpa_enabled) ? "e" : "d", |
| (pmpriv->sec_info.wpa2_enabled) ? "e" : "d", |
| pmpriv->sec_info.encryption_mode, pbss_desc->privacy); |
| LEAVE(); |
| return -1; |
| } |
| |
| /* Mode doesn't match */ |
| LEAVE(); |
| return -1; |
| } |
| |
| /** |
| * @brief Internal function used to flush the scan list |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_flush_scan_table(IN pmlan_adapter pmadapter) |
| { |
| 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; |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Internal function used to start a scan based on an input config |
| * |
| * Use the input user scan configuration information when provided in |
| * order to send the appropriate scan commands to firmware to populate or |
| * update the internal driver scan table |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pioctl_buf A pointer to MLAN IOCTL Request buffer |
| * @param puser_scan_in Pointer to the input configuration for the requested |
| * scan. |
| * |
| * @return MLAN_STATUS_SUCCESS or < 0 if error |
| */ |
| mlan_status |
| wlan_scan_networks(IN mlan_private * pmpriv, |
| IN t_void * pioctl_buf, |
| IN const wlan_user_scan_cfg * puser_scan_in) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| cmd_ctrl_node *pcmd_node = MNULL; |
| pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; |
| |
| wlan_scan_cmd_config_tlv *pscan_cfg_out = MNULL; |
| MrvlIEtypes_ChanListParamSet_t *pchan_list_out; |
| t_u32 buf_size; |
| ChanScanParamSet_t *pscan_chan_list; |
| |
| t_u8 keep_previous_scan; |
| t_u8 filtered_scan; |
| t_u8 scan_current_chan_only; |
| t_u8 max_chan_per_scan; |
| 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; |
| for (i = 0; i < pmadapter->num_in_chan_stats; i++) |
| pmadapter->pchan_stats[i].cca_scan_duration = 0; |
| /** appened current AP at the beginning of scantable to avoid no beacon buffer */ |
| if (pmpriv->media_connected == MTRUE) { |
| ret = wlan_restore_curr_bcn(pmpriv); |
| /** append current AP to scan table */ |
| if (ret == MLAN_STATUS_SUCCESS) { |
| pmadapter->num_in_scan_table++; |
| memcpy(pmadapter, |
| &pmadapter->pscan_table[pmadapter-> |
| num_in_scan_table |
| - 1], |
| &pmpriv->curr_bss_params.bss_descriptor, |
| sizeof(pmpriv->curr_bss_params. |
| bss_descriptor)); |
| } |
| } |
| } |
| |
| ret = wlan_scan_channel_list(pmpriv, |
| pioctl_buf, |
| max_chan_per_scan, |
| filtered_scan, |
| &pscan_cfg_out->config, |
| pchan_list_out, pscan_chan_list); |
| |
| /* Get scan command from scan_pending_q and put to cmd_pending_q */ |
| if (ret == MLAN_STATUS_SUCCESS) { |
| if (util_peek_list |
| (pmadapter->pmoal_handle, &pmadapter->scan_pending_q, |
| pcb->moal_spin_lock, pcb->moal_spin_unlock)) { |
| pcmd_node = |
| (cmd_ctrl_node *) util_dequeue_list(pmadapter-> |
| pmoal_handle, |
| &pmadapter-> |
| scan_pending_q, |
| pcb-> |
| moal_spin_lock, |
| pcb-> |
| moal_spin_unlock); |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->pscan_ioctl_req = pioctl_req; |
| pmadapter->scan_processing = MTRUE; |
| wlan_release_cmd_lock(pmadapter); |
| wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, |
| MTRUE); |
| } |
| } |
| if (pscan_cfg_out) |
| pcb->moal_mfree(pmadapter->pmoal_handle, |
| (t_u8 *) pscan_cfg_out); |
| |
| if (pscan_chan_list) |
| pcb->moal_mfree(pmadapter->pmoal_handle, |
| (t_u8 *) pscan_chan_list); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Prepare a scan command to be sent to the firmware |
| * |
| * Use the wlan_scan_cmd_config sent to the command processing module in |
| * the wlan_prepare_cmd to configure a HostCmd_DS_802_11_SCAN command |
| * struct to send to firmware. |
| * |
| * The fixed fields specifying the BSS type and BSSID filters as well as a |
| * variable number/length of TLVs are sent in the command to firmware. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pcmd A pointer to HostCmd_DS_COMMAND structure to be sent to |
| * firmware with the HostCmd_DS_801_11_SCAN structure |
| * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used |
| * to set the fields/TLVs for the command sent to firmware |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_cmd_802_11_scan(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * pcmd, IN t_void * pdata_buf) |
| { |
| HostCmd_DS_802_11_SCAN *pscan_cmd = &pcmd->params.scan; |
| wlan_scan_cmd_config *pscan_cfg; |
| |
| ENTER(); |
| |
| pscan_cfg = (wlan_scan_cmd_config *) pdata_buf; |
| |
| /* Set fixed field variables in scan command */ |
| pscan_cmd->bss_mode = pscan_cfg->bss_mode; |
| memcpy(pmpriv->adapter, pscan_cmd->bssid, pscan_cfg->specific_bssid, |
| sizeof(pscan_cmd->bssid)); |
| memcpy(pmpriv->adapter, pscan_cmd->tlv_buffer, pscan_cfg->tlv_buf, |
| pscan_cfg->tlv_buf_len); |
| |
| pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN); |
| |
| /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ |
| pcmd->size = |
| (t_u16) wlan_cpu_to_le16((t_u16) (sizeof(pscan_cmd->bss_mode) |
| + sizeof(pscan_cmd->bssid) |
| + pscan_cfg->tlv_buf_len |
| + S_DS_GEN)); |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handles the command response of scan |
| * |
| * The response buffer for the scan command has the following |
| * memory layout: |
| * |
| * .-------------------------------------------------------------. |
| * | Header (4 * sizeof(t_u16)): Standard command response hdr | |
| * .-------------------------------------------------------------. |
| * | BufSize (t_u16) : sizeof the BSS Description data | |
| * .-------------------------------------------------------------. |
| * | NumOfSet (t_u8) : Number of BSS Descs returned | |
| * .-------------------------------------------------------------. |
| * | BSSDescription data (variable, size given in BufSize) | |
| * .-------------------------------------------------------------. |
| * | TLV data (variable, size calculated using Header->Size, | |
| * | BufSize and sizeof the fixed fields above) | |
| * .-------------------------------------------------------------. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param resp A pointer to HostCmd_DS_COMMAND |
| * @param pioctl_buf A pointer to mlan_ioctl_req structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_ret_802_11_scan(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * resp, IN t_void * pioctl_buf) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = MNULL; |
| 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_u8 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); |
| |
| 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-> |
| radio_type & (MBIT(0) |
| | |
| MBIT |
| (1))); |
| 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; |
| |
| cfp = wlan_find_cfp_by_band_and_channel(pmadapter, |
| (t_u8) |
| bss_new_entry-> |
| bss_band, |
| (t_u16) |
| bss_new_entry-> |
| channel); |
| if (cfp) |
| bss_new_entry->freq = cfp->freq; |
| else |
| bss_new_entry->freq = 0; |
| |
| /* 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++; |
| } |
| |
| /* |
| * Save the beacon/probe response returned for later |
| * application retrieval. Duplicate beacon/probe |
| * responses are updated if possible |
| */ |
| wlan_ret_802_11_scan_store_beacon(pmpriv, |
| bss_idx, |
| num_in_table, |
| bss_new_entry); |
| if (bss_new_entry->pbeacon_buf == MNULL) { |
| PRINTM(MCMND, |
| "No space for beacon, drop this entry\n"); |
| num_in_table--; |
| continue; |
| } |
| /* |
| * If the TSF TLV was appended to the scan results, save |
| * this entry's TSF value in the networkTSF field. The |
| * networkTSF is the firmware's TSF value at the time |
| * the beacon or probe response was received. |
| */ |
| if (ptsf_tlv) { |
| memcpy(pmpriv->adapter, &tsf_val, |
| &ptsf_tlv->tsf_data[idx * TSF_DATA_SIZE], |
| sizeof(tsf_val)); |
| tsf_val = wlan_le64_to_cpu(tsf_val); |
| memcpy(pmpriv->adapter, |
| &bss_new_entry->network_tsf, &tsf_val, |
| sizeof(bss_new_entry->network_tsf)); |
| } |
| |
| /* Copy the locally created bss_new_entry to the scan |
| table */ |
| memcpy(pmadapter, &pmadapter->pscan_table[bss_idx], |
| bss_new_entry, |
| sizeof(pmadapter->pscan_table[bss_idx])); |
| |
| } else { |
| /* Error parsing/interpreting the scan response, |
| skipped */ |
| PRINTM(MERROR, |
| "SCAN_RESP: wlan_interpret_bss_desc_with_ie returned error\n"); |
| } |
| } |
| |
| PRINTM(MINFO, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", |
| pscan_rsp->number_of_sets, |
| num_in_table - pmadapter->num_in_scan_table, num_in_table); |
| |
| /* Update the total number of BSSIDs in the scan table */ |
| pmadapter->num_in_scan_table = num_in_table; |
| /* Update the age_in_second */ |
| pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, |
| &pmadapter->age_in_secs, |
| &age_ts_usec); |
| if (is_bgscan_resp) |
| goto done; |
| if (!util_peek_list |
| (pmadapter->pmoal_handle, &pmadapter->scan_pending_q, |
| pcb->moal_spin_lock, pcb->moal_spin_unlock)) { |
| /* |
| * 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) { |
| 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, |
| pcb-> |
| moal_spin_lock, |
| pcb-> |
| moal_spin_unlock); |
| |
| wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, |
| MTRUE); |
| } |
| } |
| |
| done: |
| if (bss_new_entry) |
| pcb->moal_mfree(pmadapter->pmoal_handle, |
| (t_u8 *) bss_new_entry); |
| 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(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * pcmd, IN t_void * pdata_buf) |
| { |
| HostCmd_DS_802_11_SCAN_EXT *pext_scan_cmd = &pcmd->params.ext_scan; |
| wlan_scan_cmd_config *pscan_cfg = MNULL; |
| |
| ENTER(); |
| |
| pscan_cfg = (wlan_scan_cmd_config *) pdata_buf; |
| |
| memcpy(pmpriv->adapter, pext_scan_cmd->tlv_buffer, |
| pscan_cfg->tlv_buf, pscan_cfg->tlv_buf_len); |
| |
| pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); |
| |
| /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ |
| pcmd->size = wlan_cpu_to_le16((t_u16) (sizeof(pext_scan_cmd->reserved) |
| + pscan_cfg->tlv_buf_len |
| + S_DS_GEN)); |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handles the command response of extended scan |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param resp A pointer to HostCmd_DS_COMMAND |
| * @param pioctl_buf A pointer to mlan_ioctl_req structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_ret_802_11_scan_ext(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * resp, IN t_void * pioctl_buf) |
| { |
| 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; |
| ENTER(); |
| |
| PRINTM(MINFO, "EXT scan returns successfully\n"); |
| 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 uAP sys config TLVs, bytes left < TLV length\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 parse and store the extended scan results |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param number_of_sets Number of BSS |
| * @param pscan_resp A pointer to scan response buffer |
| * @param scan_resp_size Size of scan response buffer |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_parse_ext_scan_result(IN mlan_private * pmpriv, |
| IN t_u8 number_of_sets, |
| IN t_u8 * pscan_resp, IN t_u16 scan_resp_size) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = MNULL; |
| BSSDescriptor_t *bss_new_entry = MNULL; |
| t_u8 *pbss_info; |
| t_u32 bytes_left; |
| t_u32 bytes_left_for_tlv; |
| t_u32 num_in_table; |
| t_u32 bss_idx; |
| t_u32 idx; |
| t_u64 tsf_val; |
| chan_freq_power_t *cfp; |
| t_u16 tlv_type, tlv_len; |
| MrvlIEtypes_Data_t *ptlv = MNULL; |
| MrvlIEtypes_Bss_Scan_Rsp_t *pscan_rsp_tlv = MNULL; |
| MrvlIEtypes_Bss_Scan_Info_t *pscan_info_tlv = MNULL; |
| t_u8 band; |
| t_u32 age_ts_usec; |
| t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = { 0 }; |
| |
| ENTER(); |
| pcb = (pmlan_callbacks) & pmadapter->callbacks; |
| |
| if (number_of_sets > MRVDRV_MAX_BSSID_LIST) { |
| PRINTM(MERROR, |
| "EXT_SCAN: Invalid number of AP returned (%d)!!\n", |
| number_of_sets); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| bytes_left = scan_resp_size; |
| PRINTM(MINFO, "EXT_SCAN: bss_descript_size %d\n", scan_resp_size); |
| PRINTM(MINFO, "EXT_SCAN: returned %d APs before parsing\n", |
| number_of_sets); |
| |
| num_in_table = pmadapter->num_in_scan_table; |
| ptlv = (MrvlIEtypes_Data_t *) pscan_resp; |
| |
| /* |
| * Process each scan response returned number_of_sets. Save |
| * the information in the bss_new_entry and then insert into the |
| * driver scan table either as an update to an existing entry |
| * or as an addition at the end of the table |
| */ |
| ret = pcb->moal_malloc(pmadapter->pmoal_handle, sizeof(BSSDescriptor_t), |
| MLAN_MEM_DEF, (t_u8 **) & bss_new_entry); |
| |
| if (ret != MLAN_STATUS_SUCCESS || !bss_new_entry) { |
| PRINTM(MERROR, "Memory allocation for bss_new_entry failed!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| for (idx = 0; idx < number_of_sets && bytes_left > |
| sizeof(MrvlIEtypesHeader_t); idx++) { |
| tlv_type = wlan_le16_to_cpu(ptlv->header.type); |
| tlv_len = wlan_le16_to_cpu(ptlv->header.len); |
| if (bytes_left < sizeof(MrvlIEtypesHeader_t) + tlv_len) { |
| PRINTM(MERROR, |
| "EXT_SCAN: Error bytes left < TLV length\n"); |
| break; |
| } |
| pscan_rsp_tlv = MNULL; |
| pscan_info_tlv = MNULL; |
| bytes_left_for_tlv = bytes_left; |
| /* BSS response TLV with beacon or probe response buffer * at |
| the initial position of each descriptor */ |
| if (tlv_type == TLV_TYPE_BSS_SCAN_RSP) { |
| pbss_info = (t_u8 *) ptlv; |
| pscan_rsp_tlv = (MrvlIEtypes_Bss_Scan_Rsp_t *) ptlv; |
| ptlv = (MrvlIEtypes_Data_t *) (ptlv->data + tlv_len); |
| bytes_left_for_tlv -= |
| (tlv_len + sizeof(MrvlIEtypesHeader_t)); |
| } else |
| break; |
| |
| /* Process variable TLV */ |
| while (bytes_left_for_tlv >= sizeof(MrvlIEtypesHeader_t) && |
| wlan_le16_to_cpu(ptlv->header.type) != |
| TLV_TYPE_BSS_SCAN_RSP) { |
| tlv_type = wlan_le16_to_cpu(ptlv->header.type); |
| tlv_len = wlan_le16_to_cpu(ptlv->header.len); |
| if (bytes_left_for_tlv < |
| sizeof(MrvlIEtypesHeader_t) + tlv_len) { |
| PRINTM(MERROR, |
| "EXT_SCAN: Error in processing TLV, " |
| "bytes left < TLV length\n"); |
| pscan_rsp_tlv = MNULL; |
| bytes_left_for_tlv = 0; |
| continue; |
| } |
| switch (tlv_type) { |
| case TLV_TYPE_BSS_SCAN_INFO: |
| pscan_info_tlv = |
| (MrvlIEtypes_Bss_Scan_Info_t *) ptlv; |
| if (tlv_len != |
| sizeof(MrvlIEtypes_Bss_Scan_Info_t) - |
| sizeof(MrvlIEtypesHeader_t)) { |
| bytes_left_for_tlv = 0; |
| continue; |
| } |
| break; |
| default: |
| break; |
| } |
| ptlv = (MrvlIEtypes_Data_t *) (ptlv->data + tlv_len); |
| bytes_left -= (tlv_len + sizeof(MrvlIEtypesHeader_t)); |
| bytes_left_for_tlv -= |
| (tlv_len + sizeof(MrvlIEtypesHeader_t)); |
| } |
| /* No BSS response TLV */ |
| if (pscan_rsp_tlv == MNULL) |
| break; |
| |
| /* Advance pointer to the beacon buffer length and * update the |
| bytes count so that the function * |
| wlan_interpret_bss_desc_with_ie() can handle the * scan |
| buffer withut any change */ |
| pbss_info += sizeof(t_u16); |
| bytes_left -= sizeof(t_u16); |
| |
| /* Zero out the bss_new_entry we are about to store info in */ |
| memset(pmadapter, bss_new_entry, 0x00, sizeof(BSSDescriptor_t)); |
| |
| /* Process the data fields and IEs returned for this BSS */ |
| if (wlan_interpret_bss_desc_with_ie(pmadapter, |
| bss_new_entry, |
| &pbss_info, |
| &bytes_left, |
| 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(pmpriv->adapter, &tsf_val, |
| &pscan_info_tlv->tsf, sizeof(tsf_val)); |
| tsf_val = wlan_le64_to_cpu(tsf_val); |
| memcpy(pmpriv->adapter, |
| &bss_new_entry->network_tsf, &tsf_val, |
| sizeof(bss_new_entry->network_tsf)); |
| band = radio_type_to_band(pscan_info_tlv->band); |
| } |
| /* Save the band designation for this entry for use in |
| join */ |
| bss_new_entry->bss_band = band; |
| |
| cfp = wlan_find_cfp_by_band_and_channel(pmadapter, |
| (t_u8) |
| bss_new_entry-> |
| bss_band, |
| (t_u16) |
| bss_new_entry-> |
| channel); |
| if (cfp) |
| bss_new_entry->freq = cfp->freq; |
| else |
| bss_new_entry->freq = 0; |
| |
| /* Skip entry if on blacklisted channel */ |
| if (cfp && cfp->dynamic.blacklist) { |
| PRINTM(MINFO, |
| "EXT_SCAN: 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, |
| "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++; |
| } |
| |
| /* |
| * 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; |
| } |
| |
| /* Copy the locally created bss_new_entry to the scan |
| table */ |
| memcpy(pmadapter, &pmadapter->pscan_table[bss_idx], |
| bss_new_entry, |
| sizeof(pmadapter->pscan_table[bss_idx])); |
| } else { |
| /* Error parsing/interpreting the scan response, |
| skipped */ |
| PRINTM(MERROR, |
| "EXT_SCAN: wlan_interpret_bss_desc_with_ie returned error\n"); |
| } |
| } |
| |
| PRINTM(MINFO, "EXT_SCAN: Scanned %2d APs, %d valid, %d total\n", |
| number_of_sets, num_in_table - pmadapter->num_in_scan_table, |
| num_in_table); |
| |
| /* Update the total number of BSSIDs in the scan table */ |
| pmadapter->num_in_scan_table = num_in_table; |
| /* Update the age_in_second */ |
| pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, |
| &pmadapter->age_in_secs, |
| &age_ts_usec); |
| |
| done: |
| if (bss_new_entry) |
| pcb->moal_mfree(pmadapter->pmoal_handle, |
| (t_u8 *) bss_new_entry); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function handles the event extended scan report |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pmbuf A pointer to mlan_buffer |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_handle_event_ext_scan_report(IN mlan_private * pmpriv, |
| IN mlan_buffer * pmbuf) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = &pmadapter->callbacks; |
| mlan_ioctl_req *pioctl_req = MNULL; |
| cmd_ctrl_node *pcmd_node = MNULL; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| mlan_event_scan_result *pevent_scan = (pmlan_event_scan_result) |
| (pmbuf->pbuf + pmbuf->data_offset); |
| t_u8 *ptlv = (pmbuf->pbuf + pmbuf->data_offset |
| + sizeof(mlan_event_scan_result)); |
| t_u16 tlv_buf_left = wlan_cpu_to_le16(pevent_scan->buf_size); |
| |
| DBG_HEXDUMP(MCMD_D, "EVENT EXT_SCAN", pmbuf->pbuf + |
| pmbuf->data_offset, pmbuf->data_len); |
| wlan_parse_ext_scan_result(pmpriv, pevent_scan->num_of_set, |
| ptlv, tlv_buf_left); |
| if (!pevent_scan->more_event) { |
| |
| if (!util_peek_list(pmadapter->pmoal_handle, |
| &pmadapter->scan_pending_q, |
| pcb->moal_spin_lock, |
| pcb->moal_spin_unlock)) { |
| /* |
| * 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) { |
| /* 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, |
| pcb->moal_spin_lock, |
| pcb-> |
| moal_spin_unlock); |
| wlan_insert_cmd_to_pending_q(pmadapter, |
| pcmd_node, MTRUE); |
| } |
| } |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function prepares command of bg_scan_query. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pcmd A pointer to HostCmd_DS_COMMAND structure |
| * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used |
| * to set the fields/TLVs for the command sent to firmware |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_cmd_802_11_bg_scan_query(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * pcmd, |
| IN t_void * pdata_buf) |
| { |
| HostCmd_DS_802_11_BG_SCAN_QUERY *bg_query = &pcmd->params.bg_scan_query; |
| |
| ENTER(); |
| |
| pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); |
| pcmd->size = |
| wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_BG_SCAN_QUERY) + |
| S_DS_GEN); |
| |
| bg_query->flush = MTRUE; |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Create a channel list for the driver to scan based on region info |
| * |
| * Use the driver region/band information to construct a comprehensive list |
| * of channels to scan. This routine is used for any scan that is not |
| * provided a specific channel list to scan. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pbg_scan_in pointer to scan configuration parameters |
| * @param tlv_chan_list A pointer to structure MrvlIEtypes_ChanListParamSet_t |
| * |
| * @return channel number |
| */ |
| static t_u8 |
| wlan_bgscan_create_channel_list(IN mlan_private * pmpriv, |
| IN const wlan_bgscan_cfg * pbg_scan_in, |
| MrvlIEtypes_ChanListParamSet_t * tlv_chan_list) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| region_chan_t *pscan_region; |
| chan_freq_power_t *cfp; |
| t_u32 region_idx; |
| t_u32 chan_idx = 0; |
| t_u32 next_chan; |
| t_u8 scan_type; |
| t_u8 radio_type; |
| |
| ENTER(); |
| |
| for (region_idx = 0; |
| region_idx < NELEMENTS(pmadapter->region_channel); region_idx++) { |
| |
| if (wlan_11d_is_enabled(pmpriv) && |
| pmpriv->media_connected != MTRUE) { |
| /* Scan all the supported chan for the first scan */ |
| if (!pmadapter->universal_channel[region_idx].valid) |
| continue; |
| pscan_region = |
| &pmadapter->universal_channel[region_idx]; |
| } else { |
| if (!pmadapter->region_channel[region_idx].valid) |
| continue; |
| pscan_region = &pmadapter->region_channel[region_idx]; |
| } |
| |
| if (pbg_scan_in && !pbg_scan_in->chan_list[0].chan_number && |
| pbg_scan_in->chan_list[0].radio_type & BAND_SPECIFIED) { |
| radio_type = |
| pbg_scan_in->chan_list[0]. |
| radio_type & ~BAND_SPECIFIED; |
| if (!radio_type && (pscan_region->band != BAND_B) && |
| (pscan_region->band != BAND_G)) |
| continue; |
| if (radio_type && (pscan_region->band != BAND_A)) |
| continue; |
| } |
| if (!wlan_is_band_compatible |
| (pmpriv->config_bands | pmadapter->adhoc_start_band, |
| pscan_region->band)) |
| continue; |
| for (next_chan = 0; |
| next_chan < pscan_region->num_cfp; |
| next_chan++, chan_idx++) { |
| if (chan_idx >= WLAN_BG_SCAN_CHAN_MAX) |
| break; |
| /* Set the default scan type to ACTIVE SCAN type, will |
| * later be changed to passive on a per channel basis |
| * if restricted by regulatory requirements (11d or |
| 11h) */ |
| scan_type = MLAN_SCAN_TYPE_ACTIVE; |
| cfp = pscan_region->pcfp + next_chan; |
| if (scan_type == MLAN_SCAN_TYPE_ACTIVE |
| && wlan_11d_is_enabled(pmpriv)) { |
| scan_type = wlan_11d_get_scan_type(pmadapter, |
| pscan_region-> |
| band, |
| (t_u8) cfp-> |
| channel, |
| &pmadapter-> |
| parsed_region_chan); |
| } |
| switch (pscan_region->band) { |
| case BAND_A: |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| radio_type = HostCmd_SCAN_RADIO_TYPE_A; |
| if (!wlan_11d_is_enabled(pmpriv)) { |
| /* 11D not available... play it safe on |
| DFS channels */ |
| if (wlan_11h_radar_detect_required |
| (pmpriv, (t_u8) cfp->channel)) |
| scan_type = |
| MLAN_SCAN_TYPE_PASSIVE; |
| } |
| break; |
| case BAND_B: |
| case BAND_G: |
| if (!wlan_11d_is_enabled(pmpriv)) |
| 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]. |
| radio_type = HostCmd_SCAN_RADIO_TYPE_BG; |
| break; |
| default: |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| radio_type = HostCmd_SCAN_RADIO_TYPE_BG; |
| break; |
| } |
| |
| if (pbg_scan_in && pbg_scan_in->chan_list[0].scan_time) { |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| max_scan_time = |
| wlan_cpu_to_le16((t_u16) pbg_scan_in-> |
| chan_list[0]. |
| scan_time); |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| min_scan_time = |
| wlan_cpu_to_le16((t_u16) pbg_scan_in-> |
| chan_list[0]. |
| scan_time); |
| } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| max_scan_time = |
| wlan_cpu_to_le16(pmadapter-> |
| passive_scan_time); |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| min_scan_time = |
| wlan_cpu_to_le16(pmadapter-> |
| passive_scan_time); |
| } else { |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| max_scan_time = |
| wlan_cpu_to_le16(pmadapter-> |
| specific_scan_time); |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| min_scan_time = |
| wlan_cpu_to_le16(pmadapter-> |
| specific_scan_time); |
| } |
| |
| if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| chan_scan_mode.passive_scan = MTRUE; |
| } else { |
| tlv_chan_list->chan_scan_param[chan_idx]. |
| chan_scan_mode.passive_scan = MFALSE; |
| } |
| |
| tlv_chan_list->chan_scan_param[chan_idx].chan_number = |
| (t_u8) cfp->channel; |
| tlv_chan_list->chan_scan_param[chan_idx].chan_scan_mode. |
| disable_chan_filt = MTRUE; |
| } |
| } |
| |
| LEAVE(); |
| return chan_idx; |
| } |
| |
| /** |
| * @brief This function prepares command of bg_scan_config |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pcmd A pointer to HostCmd_DS_COMMAND structure |
| * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used |
| * to set the fields/TLVs for the command sent to firmware |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_cmd_bgscan_config(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * pcmd, IN t_void * pdata_buf) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = |
| &pcmd->params.bg_scan_config; |
| wlan_bgscan_cfg *bg_scan_in = (wlan_bgscan_cfg *) pdata_buf; |
| t_u16 cmd_size = 0; |
| MrvlIEtypes_NumProbes_t *pnum_probes_tlv = MNULL; |
| MrvlIEtypes_BeaconLowRssiThreshold_t *rssi_tlv = MNULL; |
| MrvlIEtypes_BeaconLowSnrThreshold_t *snr_tlv = MNULL; |
| MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv = MNULL; |
| MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; |
| MrvlIEtypes_StartLater_t *tlv_start_later = MNULL; |
| MrvlIEtypes_RepeatCount_t *tlv_repeat = MNULL; |
| t_u8 *tlv = MNULL; |
| t_u16 num_probes = 0; |
| t_u32 ssid_idx; |
| t_u32 ssid_len = 0; |
| t_u32 chan_idx; |
| t_u32 chan_num; |
| t_u8 radio_type; |
| t_u16 scan_dur; |
| t_u8 scan_type; |
| |
| ENTER(); |
| |
| pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG); |
| bg_scan->action = wlan_cpu_to_le16(bg_scan_in->action); |
| bg_scan->enable = bg_scan_in->enable; |
| bg_scan->bss_type = bg_scan_in->bss_type; |
| cmd_size = sizeof(HostCmd_DS_802_11_BG_SCAN_CONFIG) + S_DS_GEN; |
| if (bg_scan_in->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(pmadapter, pwildcard_ssid_tlv->ssid, |
| bg_scan_in->ssid_list[ssid_idx].ssid, |
| MIN(MLAN_MAX_SSID_LENGTH, ssid_len)); |
| tlv += sizeof(pwildcard_ssid_tlv->header) + |
| pwildcard_ssid_tlv->header.len; |
| cmd_size += |
| sizeof(pwildcard_ssid_tlv->header) + |
| pwildcard_ssid_tlv->header.len; |
| pwildcard_ssid_tlv->header.len = |
| wlan_cpu_to_le16(pwildcard_ssid_tlv->header.len); |
| PRINTM(MINFO, "Scan: ssid_list[%d]: %s, %d\n", ssid_idx, |
| pwildcard_ssid_tlv->ssid, |
| pwildcard_ssid_tlv->max_ssid_length); |
| } |
| if (bg_scan_in->chan_list[0].chan_number) { |
| tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *) tlv; |
| PRINTM(MINFO, "Scan: Using supplied channel list\n"); |
| chan_num = 0; |
| for (chan_idx = 0; chan_idx < WLAN_BG_SCAN_CHAN_MAX |
| && bg_scan_in->chan_list[chan_idx].chan_number; |
| chan_idx++) { |
| radio_type = bg_scan_in->chan_list[chan_idx].radio_type; |
| if (!wlan_is_band_compatible |
| (pmpriv->config_bands | pmadapter->adhoc_start_band, |
| radio_type_to_band(radio_type))) |
| continue; |
| scan_type = bg_scan_in->chan_list[chan_idx].scan_type; |
| /* Prevent active scanning on a radar controlled |
| channel */ |
| if (radio_type == HostCmd_SCAN_RADIO_TYPE_A) { |
| if (wlan_11h_radar_detect_required |
| (pmpriv, |
| bg_scan_in->chan_list[chan_idx]. |
| chan_number)) { |
| scan_type = MLAN_SCAN_TYPE_PASSIVE; |
| } |
| } |
| if (radio_type == HostCmd_SCAN_RADIO_TYPE_BG) { |
| 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].radio_type = |
| bg_scan_in->chan_list[chan_idx].radio_type; |
| |
| if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| tlv_chan_list->chan_scan_param[chan_num]. |
| chan_scan_mode.passive_scan = MTRUE; |
| } else { |
| tlv_chan_list->chan_scan_param[chan_num]. |
| chan_scan_mode.passive_scan = MFALSE; |
| } |
| if (bg_scan_in->chan_list[chan_idx].scan_time) { |
| scan_dur = |
| (t_u16) bg_scan_in->chan_list[chan_idx]. |
| scan_time; |
| } else { |
| if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { |
| scan_dur = pmadapter->passive_scan_time; |
| } else { |
| scan_dur = |
| pmadapter->specific_scan_time; |
| } |
| } |
| tlv_chan_list->chan_scan_param[chan_num].min_scan_time = |
| wlan_cpu_to_le16(scan_dur); |
| tlv_chan_list->chan_scan_param[chan_num].max_scan_time = |
| wlan_cpu_to_le16(scan_dur); |
| chan_num++; |
| } |
| tlv_chan_list->header.type = |
| wlan_cpu_to_le16(TLV_TYPE_CHANLIST); |
| tlv_chan_list->header.len = |
| wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); |
| tlv += sizeof(MrvlIEtypesHeader_t) + |
| sizeof(ChanScanParamSet_t) * chan_num; |
| cmd_size += |
| sizeof(MrvlIEtypesHeader_t) + |
| sizeof(ChanScanParamSet_t) * chan_num; |
| } else { |
| tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *) tlv; |
| chan_num = |
| wlan_bgscan_create_channel_list(pmpriv, bg_scan_in, |
| tlv_chan_list); |
| tlv_chan_list->header.type = |
| wlan_cpu_to_le16(TLV_TYPE_CHANLIST); |
| tlv_chan_list->header.len = |
| wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); |
| tlv += sizeof(MrvlIEtypesHeader_t) + |
| sizeof(ChanScanParamSet_t) * chan_num; |
| cmd_size += |
| sizeof(MrvlIEtypesHeader_t) + |
| sizeof(ChanScanParamSet_t) * chan_num; |
| } |
| 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; |
| } |
| |
| 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); |
| done: |
| pcmd->size = wlan_cpu_to_le16(cmd_size); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handles the command response of extended scan |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param resp A pointer to HostCmd_DS_COMMAND |
| * @param pioctl_buf A pointer to mlan_ioctl_req structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_ret_bgscan_config(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * resp, |
| IN mlan_ioctl_req * pioctl_buf) |
| { |
| mlan_ds_scan *pscan = MNULL; |
| HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = |
| &resp->params.bg_scan_config; |
| wlan_bgscan_cfg *bg_scan_out = MNULL; |
| |
| ENTER(); |
| if (pioctl_buf) { |
| pscan = (mlan_ds_scan *) pioctl_buf->pbuf; |
| bg_scan_out = |
| (wlan_bgscan_cfg *) pscan->param.user_scan.scan_cfg_buf; |
| bg_scan_out->action = wlan_le16_to_cpu(bg_scan->action); |
| if ((bg_scan_out->action == BG_SCAN_ACT_GET) && |
| (bg_scan_out->action == BG_SCAN_ACT_GET_PPS_UAPSD)) { |
| bg_scan_out->enable = bg_scan->enable; |
| bg_scan_out->bss_type = bg_scan->bss_type; |
| bg_scan_out->chan_per_scan = bg_scan->chan_per_scan; |
| bg_scan_out->scan_interval = |
| wlan_le32_to_cpu(bg_scan->scan_interval); |
| bg_scan_out->report_condition = |
| wlan_le32_to_cpu(bg_scan->report_condition); |
| pioctl_buf->data_read_written = |
| sizeof(mlan_ds_scan) + MLAN_SUB_COMMAND_SIZE; |
| } |
| } |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function 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(IN mlan_private * pmpriv, |
| IN HostCmd_DS_COMMAND * resp, |
| IN mlan_ioctl_req * pioctl_buf) |
| { |
| mlan_ds_scan *pscan = MNULL; |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| ENTER(); |
| 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(IN mlan_private * pmpriv, |
| IN mlan_802_11_ssid * ssid, |
| IN t_u8 * bssid, IN t_u32 mode) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| t_s32 net = -1, j; |
| t_u8 best_rssi = 0; |
| t_u32 i; |
| |
| ENTER(); |
| PRINTM(MINFO, "Num of entries in scan table = %d\n", |
| pmadapter->num_in_scan_table); |
| |
| /* |
| * Loop through the table until the maximum is reached or until a match |
| * is found based on the bssid field comparison |
| */ |
| for (i = 0; |
| i < pmadapter->num_in_scan_table && (!bssid || (bssid && net < 0)); |
| i++) { |
| if (!wlan_ssid_cmp |
| (pmadapter, &pmadapter->pscan_table[i].ssid, ssid) && |
| (!bssid || |
| !memcmp(pmadapter, pmadapter->pscan_table[i].mac_address, |
| bssid, MLAN_MAC_ADDR_LENGTH))) { |
| |
| if ((mode == MLAN_BSS_MODE_INFRA) && |
| !wlan_is_band_compatible(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(IN mlan_private * pmpriv, |
| IN t_u8 * bssid, IN t_u32 mode) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| t_s32 net = -1; |
| t_u32 i; |
| |
| ENTER(); |
| |
| if (!bssid) { |
| LEAVE(); |
| return -1; |
| } |
| |
| PRINTM(MINFO, "FindBSSID: Num of BSSIDs = %d\n", |
| pmadapter->num_in_scan_table); |
| |
| /* |
| * Look through the scan table for a compatible match. The ret return |
| * variable will be equal to the index in the scan table (greater |
| * than zero) if the network is compatible. The loop will continue |
| * past a matched bssid that is not compatible in case there is an |
| * AP with multiple SSIDs assigned to the same BSSID |
| */ |
| for (i = 0; net < 0 && i < pmadapter->num_in_scan_table; i++) { |
| if (!memcmp |
| (pmadapter, pmadapter->pscan_table[i].mac_address, bssid, |
| MLAN_MAC_ADDR_LENGTH)) { |
| if ((mode == MLAN_BSS_MODE_INFRA) && |
| !wlan_is_band_compatible(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(IN pmlan_adapter pmadapter, |
| IN mlan_802_11_ssid * ssid1, IN mlan_802_11_ssid * ssid2) |
| { |
| ENTER(); |
| |
| if (!ssid1 || !ssid2) { |
| LEAVE(); |
| return -1; |
| } |
| |
| if (ssid1->ssid_len != ssid2->ssid_len) { |
| LEAVE(); |
| return -1; |
| } |
| |
| LEAVE(); |
| return memcmp(pmadapter, ssid1->ssid, ssid2->ssid, ssid1->ssid_len); |
| } |
| |
| /** |
| * @brief This function inserts scan command node to scan_pending_q. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pcmd_node A pointer to cmd_ctrl_node structure |
| * @return N/A |
| */ |
| t_void |
| wlan_queue_scan_cmd(IN mlan_private * pmpriv, IN cmd_ctrl_node * pcmd_node) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| |
| ENTER(); |
| |
| if (pcmd_node == MNULL) |
| goto done; |
| util_enqueue_list_tail(pmadapter->pmoal_handle, |
| &pmadapter->scan_pending_q, |
| (pmlan_linked_list) pcmd_node, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| |
| done: |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Find the AP with specific ssid in the scan list |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param preq_ssid_bssid A pointer to AP's ssid returned |
| * |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| mlan_status |
| wlan_find_best_network(IN mlan_private * pmpriv, |
| OUT mlan_ssid_bssid * preq_ssid_bssid) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| BSSDescriptor_t *preq_bss; |
| t_s32 i; |
| |
| ENTER(); |
| |
| memset(pmadapter, preq_ssid_bssid, 0, sizeof(mlan_ssid_bssid)); |
| |
| i = wlan_find_best_network_in_list(pmpriv); |
| |
| if (i >= 0) { |
| preq_bss = &pmadapter->pscan_table[i]; |
| memcpy(pmadapter, &preq_ssid_bssid->ssid, &preq_bss->ssid, |
| sizeof(mlan_802_11_ssid)); |
| memcpy(pmadapter, (t_u8 *) & preq_ssid_bssid->bssid, |
| (t_u8 *) & preq_bss->mac_address, MLAN_MAC_ADDR_LENGTH); |
| |
| /* Make sure we are in the right mode */ |
| if (pmpriv->bss_mode == MLAN_BSS_MODE_AUTO) |
| pmpriv->bss_mode = preq_bss->bss_mode; |
| } |
| |
| if (!preq_ssid_bssid->ssid.ssid_len) { |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| PRINTM(MINFO, "Best network found = [%s], " |
| "[" 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(IN mlan_private * pmpriv, |
| IN t_void * pioctl_buf, IN mlan_802_11_ssid * preq_ssid) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmpriv->adapter->callbacks; |
| wlan_user_scan_cfg *pscan_cfg; |
| pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; |
| |
| ENTER(); |
| |
| if (!preq_ssid) { |
| if (pioctl_req) |
| pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| wlan_scan_delete_ssid_table_entry(pmpriv, preq_ssid); |
| |
| ret = pcb->moal_malloc(pmpriv->adapter->pmoal_handle, |
| sizeof(wlan_user_scan_cfg), MLAN_MEM_DEF, |
| (t_u8 **) & pscan_cfg); |
| |
| if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg) { |
| PRINTM(MERROR, "Memory allocation for pscan_cfg failed!\n"); |
| if (pioctl_req) |
| pioctl_req->status_code = MLAN_ERROR_NO_MEM; |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| memset(pmpriv->adapter, pscan_cfg, 0x00, sizeof(wlan_user_scan_cfg)); |
| |
| memcpy(pmpriv->adapter, pscan_cfg->ssid_list[0].ssid, |
| preq_ssid->ssid, preq_ssid->ssid_len); |
| pscan_cfg->keep_previous_scan = MTRUE; |
| |
| ret = wlan_scan_networks(pmpriv, pioctl_buf, pscan_cfg); |
| |
| if (pscan_cfg) |
| pcb->moal_mfree(pmpriv->adapter->pmoal_handle, |
| (t_u8 *) pscan_cfg); |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Save a beacon buffer of the current bss descriptor |
| * Save the current beacon buffer to restore in the following cases that |
| * makes the bcn_buf not to contain the current ssid's beacon buffer. |
| * - the current ssid was not found somehow in the last scan. |
| * - the current ssid was the last entry of the scan table and overloaded. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_save_curr_bcn(IN mlan_private * pmpriv) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = (pmlan_callbacks) & pmadapter->callbacks; |
| BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| /* save the beacon buffer if it is not saved or updated */ |
| if ((pmpriv->pcurr_bcn_buf == MNULL) || |
| (pmpriv->curr_bcn_size != pcurr_bss->beacon_buf_size) || |
| (memcmp |
| (pmpriv->adapter, pmpriv->pcurr_bcn_buf, pcurr_bss->pbeacon_buf, |
| pcurr_bss->beacon_buf_size))) { |
| |
| if (pmpriv->pcurr_bcn_buf) { |
| pcb->moal_mfree(pmadapter->pmoal_handle, |
| pmpriv->pcurr_bcn_buf); |
| pmpriv->pcurr_bcn_buf = MNULL; |
| } |
| pmpriv->curr_bcn_size = pcurr_bss->beacon_buf_size; |
| |
| if (pmpriv->curr_bcn_size) { |
| ret = pcb->moal_malloc(pmadapter->pmoal_handle, |
| pcurr_bss->beacon_buf_size, |
| MLAN_MEM_DEF, |
| &pmpriv->pcurr_bcn_buf); |
| |
| if ((ret == MLAN_STATUS_SUCCESS) && |
| pmpriv->pcurr_bcn_buf) { |
| memcpy(pmpriv->adapter, pmpriv->pcurr_bcn_buf, |
| pcurr_bss->pbeacon_buf, |
| pcurr_bss->beacon_buf_size); |
| PRINTM(MINFO, "current beacon saved %d\n", |
| pmpriv->curr_bcn_size); |
| } |
| } |
| } |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Free a beacon buffer of the current bss descriptor |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_free_curr_bcn(IN mlan_private * pmpriv) |
| { |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = (pmlan_callbacks) & pmadapter->callbacks; |
| |
| ENTER(); |
| if (pmpriv->pcurr_bcn_buf) { |
| pcb->moal_mfree(pmadapter->pmoal_handle, pmpriv->pcurr_bcn_buf); |
| pmpriv->pcurr_bcn_buf = MNULL; |
| } |
| LEAVE(); |
| } |