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