X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Flibertas%2Fscan.c;h=1c7003d7de43af39066a54e029063b09ede2b335;hb=697900ac14528e985b66f471ecb81082fc00baa9;hp=23f100a971b5de7594a330ef56dee42752203442;hpb=889c05bdf3056b029b9fa95096eac4f0dfd3fb6f;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c index 23f100a..1c7003d 100644 --- a/drivers/net/wireless/libertas/scan.c +++ b/drivers/net/wireless/libertas/scan.c @@ -8,14 +8,19 @@ #include #include #include +#include #include #include +#include + #include "host.h" #include "decl.h" #include "dev.h" #include "scan.h" +#include "assoc.h" +#include "cmd.h" //! Approximate amount of data needed to pass a scan result back to iwlist #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ @@ -35,11 +40,9 @@ //! Memory needed to store a max number/size SSID TLV for a firmware scan #define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) -//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max -#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ - + sizeof(struct mrvlietypes_numprobes) \ - + CHAN_TLV_MAX_SIZE \ - + SSID_TLV_MAX_SIZE) +//! Maximum memory needed for a cmd_ds_802_11_scan with all TLVs at max +#define MAX_SCAN_CFG_ALLOC (sizeof(struct cmd_ds_802_11_scan) \ + + CHAN_TLV_MAX_SIZE + SSID_TLV_MAX_SIZE) //! The maximum number of channels the firmware can scan per command #define MRVDRV_MAX_CHANNELS_PER_SCAN 14 @@ -58,237 +61,234 @@ //! Scan time specified in the channel TLV for each channel for active scans #define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 -//! Macro to enable/disable SSID checking before storing a scan table -#ifdef DISCARD_BAD_SSID -#define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid) -#else -#define CHECK_SSID_IS_VALID(x) 1 -#endif +static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy, + struct cmd_header *resp); + +/*********************************************************************/ +/* */ +/* Misc helper functions */ +/* */ +/*********************************************************************/ /** - * @brief Check if a scanned network compatible with the driver settings - * - * WEP WPA WPA2 ad-hoc encrypt Network - * enabled enabled enabled AES mode privacy WPA WPA2 Compatible - * 0 0 0 0 NONE 0 0 0 yes No security - * 1 0 0 0 NONE 1 0 0 yes Static WEP - * 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 - * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP - * + * @brief Unsets the MSB on basic rates * - * @param adapter A pointer to wlan_adapter - * @param index Index in scantable to check against current driver settings - * @param mode Network mode: Infrastructure or IBSS + * Scan through an array and unset the MSB for basic data rates. * - * @return Index in scantable, or error code if negative + * @param rates buffer of data rates + * @param len size of buffer */ -static int is_network_compatible(wlan_adapter * adapter, int index, int mode) +static void lbs_unset_basic_rate_flags(u8 *rates, size_t len) { - ENTER(); - - if (adapter->scantable[index].inframode == mode) { - if ( !adapter->secinfo.wep_enabled - && !adapter->secinfo.WPAenabled - && !adapter->secinfo.WPA2enabled - && adapter->scantable[index].wpa_ie[0] != WPA_IE - && adapter->scantable[index].rsn_ie[0] != WPA2_IE - && !adapter->scantable[index].privacy) { - /* no security */ - LEAVE(); - return index; - } else if ( adapter->secinfo.wep_enabled - && !adapter->secinfo.WPAenabled - && !adapter->secinfo.WPA2enabled - && adapter->scantable[index].privacy) { - /* static WEP enabled */ - LEAVE(); - return index; - } else if ( !adapter->secinfo.wep_enabled - && adapter->secinfo.WPAenabled - && !adapter->secinfo.WPA2enabled - && (adapter->scantable[index].wpa_ie[0] == WPA_IE) - /* privacy bit may NOT be set in some APs like LinkSys WRT54G - && adapter->scantable[index].privacy */ - ) { - /* WPA enabled */ - lbs_pr_debug(1, - "is_network_compatible() WPA: index=%d wpa_ie=%#x " - "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " - "privacy=%#x\n", index, - adapter->scantable[index].wpa_ie[0], - adapter->scantable[index].rsn_ie[0], - adapter->secinfo.wep_enabled ? "e" : "d", - adapter->secinfo.WPAenabled ? "e" : "d", - adapter->secinfo.WPA2enabled ? "e" : "d", - adapter->scantable[index].privacy); - LEAVE(); - return index; - } else if ( !adapter->secinfo.wep_enabled - && !adapter->secinfo.WPAenabled - && adapter->secinfo.WPA2enabled - && (adapter->scantable[index].rsn_ie[0] == WPA2_IE) - /* privacy bit may NOT be set in some APs like LinkSys WRT54G - && adapter->scantable[index].privacy */ - ) { - /* WPA2 enabled */ - lbs_pr_debug(1, - "is_network_compatible() WPA2: index=%d wpa_ie=%#x " - "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " - "privacy=%#x\n", index, - adapter->scantable[index].wpa_ie[0], - adapter->scantable[index].rsn_ie[0], - adapter->secinfo.wep_enabled ? "e" : "d", - adapter->secinfo.WPAenabled ? "e" : "d", - adapter->secinfo.WPA2enabled ? "e" : "d", - adapter->scantable[index].privacy); - LEAVE(); - return index; - } else if ( !adapter->secinfo.wep_enabled - && !adapter->secinfo.WPAenabled - && !adapter->secinfo.WPA2enabled - && (adapter->scantable[index].wpa_ie[0] != WPA_IE) - && (adapter->scantable[index].rsn_ie[0] != WPA2_IE) - && adapter->scantable[index].privacy) { - /* dynamic WEP enabled */ - lbs_pr_debug(1, - "is_network_compatible() dynamic WEP: index=%d " - "wpa_ie=%#x wpa2_ie=%#x privacy=%#x\n", - index, - adapter->scantable[index].wpa_ie[0], - adapter->scantable[index].rsn_ie[0], - adapter->scantable[index].privacy); - LEAVE(); - return index; - } + int i; - /* security doesn't match */ - lbs_pr_debug(1, - "is_network_compatible() FAILED: index=%d wpa_ie=%#x " - "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s privacy=%#x\n", - index, - adapter->scantable[index].wpa_ie[0], - adapter->scantable[index].rsn_ie[0], - adapter->secinfo.wep_enabled ? "e" : "d", - adapter->secinfo.WPAenabled ? "e" : "d", - adapter->secinfo.WPA2enabled ? "e" : "d", - adapter->scantable[index].privacy); - LEAVE(); - return -ECONNREFUSED; - } + for (i = 0; i < len; i++) + rates[i] &= 0x7f; +} - /* mode doesn't match */ - LEAVE(); - return -ENETUNREACH; + +static inline void clear_bss_descriptor(struct bss_descriptor *bss) +{ + /* Don't blow away ->list, just BSS data */ + memset(bss, 0, offsetof(struct bss_descriptor, list)); } /** - * @brief This function validates a SSID as being able to be printed + * @brief Compare two SSIDs * - * @param pssid SSID structure to validate + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare * - * @return TRUE or FALSE + * @return 0: ssid is same, otherwise is different */ -static u8 ssid_valid(struct WLAN_802_11_SSID *pssid) +int lbs_ssid_cmp(uint8_t *ssid1, uint8_t ssid1_len, uint8_t *ssid2, + uint8_t ssid2_len) { - int ssididx; + if (ssid1_len != ssid2_len) + return -1; - for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) { - if (!isprint(pssid->ssid[ssididx])) { - return 0; - } - } + return memcmp(ssid1, ssid2, ssid1_len); +} + +static inline int match_bss_no_security(struct lbs_802_11_security *secinfo, + struct bss_descriptor *match_bss) +{ + if (!secinfo->wep_enabled && !secinfo->WPAenabled + && !secinfo->WPA2enabled + && match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC + && match_bss->rsn_ie[0] != MFIE_TYPE_RSN + && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) + return 1; + else + return 0; +} + +static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo, + struct bss_descriptor *match_bss) +{ + if (secinfo->wep_enabled && !secinfo->WPAenabled + && !secinfo->WPA2enabled + && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) + return 1; + else + return 0; +} + +static inline int match_bss_wpa(struct lbs_802_11_security *secinfo, + struct bss_descriptor *match_bss) +{ + if (!secinfo->wep_enabled && secinfo->WPAenabled + && (match_bss->wpa_ie[0] == MFIE_TYPE_GENERIC) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */ + ) + return 1; + else + return 0; +} + +static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo, + struct bss_descriptor *match_bss) +{ + if (!secinfo->wep_enabled && secinfo->WPA2enabled + && (match_bss->rsn_ie[0] == MFIE_TYPE_RSN) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */ + ) + return 1; + else + return 0; +} - return 1; +static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo, + struct bss_descriptor *match_bss) +{ + if (!secinfo->wep_enabled && !secinfo->WPAenabled + && !secinfo->WPA2enabled + && (match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC) + && (match_bss->rsn_ie[0] != MFIE_TYPE_RSN) + && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) + return 1; + else + return 0; +} + +static inline int is_same_network(struct bss_descriptor *src, + struct bss_descriptor *dst) +{ + /* A network is only a duplicate if the channel, BSSID, and ESSID + * all match. We treat all with the same BSSID and channel + * as one network */ + return ((src->ssid_len == dst->ssid_len) && + (src->channel == dst->channel) && + !compare_ether_addr(src->bssid, dst->bssid) && + !memcmp(src->ssid, dst->ssid, src->ssid_len)); } /** - * @brief Post process the scan table after a new scan command has completed + * @brief Check if a scanned network compatible with the driver settings * - * 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. + * 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 + * 1 0 0 0 NONE 1 0 0 yes Static WEP + * 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 + * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP * - * Debug dump the current scan table contents if compiled accordingly. * - * @param priv A pointer to wlan_private structure + * @param priv A pointer to struct lbs_private + * @param index Index in scantable to check against current driver settings + * @param mode Network mode: Infrastructure or IBSS * - * @return void + * @return Index in scantable, or error code if negative */ -static void wlan_scan_process_results(wlan_private * priv) +static int is_network_compatible(struct lbs_private *priv, + struct bss_descriptor *bss, uint8_t mode) { - wlan_adapter *adapter = priv->adapter; - int foundcurrent; - int i; - - foundcurrent = 0; - - if (adapter->connect_status == libertas_connected) { - /* try to find the current BSSID in the new scan list */ - for (i = 0; i < adapter->numinscantable; i++) { - if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, - &adapter->curbssparams.ssid) && - !memcmp(adapter->curbssparams.bssid, - adapter->scantable[i].macaddress, - ETH_ALEN)) { - foundcurrent = 1; - } - } - - if (foundcurrent) { - /* Make a copy of current BSSID descriptor */ - memcpy(&adapter->curbssparams.bssdescriptor, - &adapter->scantable[i], - sizeof(adapter->curbssparams.bssdescriptor)); - } + int matched = 0; + + lbs_deb_enter(LBS_DEB_SCAN); + + if (bss->mode != mode) + goto done; + + if ((matched = match_bss_no_security(&priv->secinfo, bss))) { + goto done; + } else if ((matched = match_bss_static_wep(&priv->secinfo, bss))) { + goto done; + } else if ((matched = match_bss_wpa(&priv->secinfo, bss))) { + lbs_deb_scan("is_network_compatible() WPA: wpa_ie 0x%x " + "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s " + "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0], + priv->secinfo.wep_enabled ? "e" : "d", + priv->secinfo.WPAenabled ? "e" : "d", + priv->secinfo.WPA2enabled ? "e" : "d", + (bss->capability & WLAN_CAPABILITY_PRIVACY)); + goto done; + } else if ((matched = match_bss_wpa2(&priv->secinfo, bss))) { + lbs_deb_scan("is_network_compatible() WPA2: wpa_ie 0x%x " + "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s " + "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0], + priv->secinfo.wep_enabled ? "e" : "d", + priv->secinfo.WPAenabled ? "e" : "d", + priv->secinfo.WPA2enabled ? "e" : "d", + (bss->capability & WLAN_CAPABILITY_PRIVACY)); + goto done; + } else if ((matched = match_bss_dynamic_wep(&priv->secinfo, bss))) { + lbs_deb_scan("is_network_compatible() dynamic WEP: " + "wpa_ie 0x%x wpa2_ie 0x%x privacy 0x%x\n", + bss->wpa_ie[0], bss->rsn_ie[0], + (bss->capability & WLAN_CAPABILITY_PRIVACY)); + goto done; } - for (i = 0; i < adapter->numinscantable; i++) { - lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " - "RSSI[%03d], SSID[%s]\n", - i, - adapter->scantable[i].macaddress[0], - adapter->scantable[i].macaddress[1], - adapter->scantable[i].macaddress[2], - adapter->scantable[i].macaddress[3], - adapter->scantable[i].macaddress[4], - adapter->scantable[i].macaddress[5], - (s32) adapter->scantable[i].rssi, - adapter->scantable[i].ssid.ssid); - } + /* bss security settings don't match those configured on card */ + lbs_deb_scan("is_network_compatible() FAILED: wpa_ie 0x%x " + "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s privacy 0x%x\n", + bss->wpa_ie[0], bss->rsn_ie[0], + priv->secinfo.wep_enabled ? "e" : "d", + priv->secinfo.WPAenabled ? "e" : "d", + priv->secinfo.WPA2enabled ? "e" : "d", + (bss->capability & WLAN_CAPABILITY_PRIVACY)); + +done: + lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched); + return matched; } + + +/*********************************************************************/ +/* */ +/* Main scanning support */ +/* */ +/*********************************************************************/ + /** * @brief Create a channel list for the driver to scan based on region info * + * Only used from lbs_scan_setup_scan_config() + * * 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 priv A pointer to wlan_private structure + * @param priv A pointer to struct lbs_private structure * @param scanchanlist Output parameter: resulting channel list to scan - * @param filteredscan 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 void */ -static void wlan_scan_create_channel_list(wlan_private * priv, - struct chanscanparamset * scanchanlist, - u8 filteredscan) +static int lbs_scan_create_channel_list(struct lbs_private *priv, + struct chanscanparamset *scanchanlist) { - - wlan_adapter *adapter = priv->adapter; struct region_channel *scanregion; struct chan_freq_power *cfp; int rgnidx; int chanidx; int nextchan; - u8 scantype; + uint8_t scantype; chanidx = 0; @@ -296,656 +296,414 @@ static void wlan_scan_create_channel_list(wlan_private * priv, * be changed to passive on a per channel basis if restricted by * regulatory requirements (11d or 11h) */ - scantype = adapter->scantype; + scantype = CMD_SCAN_TYPE_ACTIVE; - for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { - if (priv->adapter->enable11d && - adapter->connect_status != libertas_connected) { + for (rgnidx = 0; rgnidx < ARRAY_SIZE(priv->region_channel); rgnidx++) { + if (priv->enable11d && (priv->connect_status != LBS_CONNECTED) + && (priv->mesh_connect_status != LBS_CONNECTED)) { /* Scan all the supported chan for the first scan */ - if (!adapter->universal_channel[rgnidx].valid) + if (!priv->universal_channel[rgnidx].valid) continue; - scanregion = &adapter->universal_channel[rgnidx]; + scanregion = &priv->universal_channel[rgnidx]; /* clear the parsed_region_chan for the first scan */ - memset(&adapter->parsed_region_chan, 0x00, - sizeof(adapter->parsed_region_chan)); + memset(&priv->parsed_region_chan, 0x00, + sizeof(priv->parsed_region_chan)); } else { - if (!adapter->region_channel[rgnidx].valid) + if (!priv->region_channel[rgnidx].valid) continue; - scanregion = &adapter->region_channel[rgnidx]; + scanregion = &priv->region_channel[rgnidx]; } - for (nextchan = 0; - nextchan < scanregion->nrcfp; nextchan++, chanidx++) { + for (nextchan = 0; nextchan < scanregion->nrcfp; nextchan++, chanidx++) { + struct chanscanparamset *chan = &scanchanlist[chanidx]; cfp = scanregion->CFP + nextchan; - if (priv->adapter->enable11d) { - scantype = - libertas_get_scan_type_11d(cfp->channel, - &adapter-> - parsed_region_chan); - } + if (priv->enable11d) + scantype = lbs_get_scan_type_11d(cfp->channel, + &priv->parsed_region_chan); - switch (scanregion->band) { - case BAND_B: - case BAND_G: - default: - scanchanlist[chanidx].radiotype = - cmd_scan_radio_type_bg; - break; - } + if (scanregion->band == BAND_B || scanregion->band == BAND_G) + chan->radiotype = CMD_SCAN_RADIO_TYPE_BG; - if (scantype == cmd_scan_type_passive) { - scanchanlist[chanidx].maxscantime = - cpu_to_le16 - (MRVDRV_PASSIVE_SCAN_CHAN_TIME); - scanchanlist[chanidx].chanscanmode.passivescan = - 1; + if (scantype == CMD_SCAN_TYPE_PASSIVE) { + chan->maxscantime = cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME); + chan->chanscanmode.passivescan = 1; } else { - scanchanlist[chanidx].maxscantime = - cpu_to_le16 - (MRVDRV_ACTIVE_SCAN_CHAN_TIME); - scanchanlist[chanidx].chanscanmode.passivescan = - 0; + chan->maxscantime = cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME); + chan->chanscanmode.passivescan = 0; } - scanchanlist[chanidx].channumber = cfp->channel; - - if (filteredscan) { - scanchanlist[chanidx].chanscanmode. - disablechanfilt = 1; - } + chan->channumber = cfp->channel; } } + return chanidx; } -/** - * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds - * - * 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 priv A pointer to wlan_private structure - * @param puserscanin NULL or pointer to scan configuration parameters - * @param ppchantlvout Output parameter: Pointer to the start of the - * channel TLV portion of the output scan config - * @param pscanchanlist Output parameter: Pointer to the resulting channel - * list to scan - * @param pmaxchanperscan Output parameter: Number of channels to scan for - * each issuance of the firmware scan command - * @param pfilteredscan 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 pscancurrentonly Output parameter: Flag indicating whether or not - * we are only scanning our current active channel + +/* + * Add SSID TLV of the form: * - * @return resulting scan configuration + * TLV-ID SSID 00 00 + * length 06 00 + * ssid 4d 4e 54 45 53 54 */ -static struct wlan_scan_cmd_config * -wlan_scan_setup_scan_config(wlan_private * priv, - const struct wlan_ioctl_user_scan_cfg * puserscanin, - struct mrvlietypes_chanlistparamset ** ppchantlvout, - struct chanscanparamset * pscanchanlist, - int *pmaxchanperscan, - u8 * pfilteredscan, - u8 * pscancurrentonly) +static int lbs_scan_add_ssid_tlv(struct lbs_private *priv, u8 *tlv) { - wlan_adapter *adapter = priv->adapter; - const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - struct mrvlietypes_numprobes *pnumprobestlv; - struct mrvlietypes_ssidparamset *pssidtlv; - struct wlan_scan_cmd_config * pscancfgout = NULL; - u8 *ptlvpos; - u16 numprobes; - u16 ssidlen; - int chanidx; - int scantype; - int scandur; - int channel; - int radiotype; - - pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); - if (pscancfgout == NULL) - goto out; - - /* The tlvbufferlen 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 *ppchantlvout. The difference - * between the *ppchantlvout and the tlvbuffer start will be used - * to calculate the size of anything we add in this routine. - */ - pscancfgout->tlvbufferlen = 0; - - /* Running tlv pointer. Assigned to ppchantlvout at end of function - * so later routines know where channels can be added to the command buf - */ - ptlvpos = pscancfgout->tlvbuffer; - - /* - * Set the initial scan paramters for progressive scanning. If a specific - * BSSID or SSID is used, the number of channels in the scan command - * will be increased to the absolute maximum - */ - *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; - - /* Initialize the scan as un-filtered by firmware, set to TRUE below if - * a SSID or BSSID filter is sent in the command - */ - *pfilteredscan = 0; - - /* 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. - */ - *pscancurrentonly = 0; - - if (puserscanin) { - - /* Set the bss type scan filter, use adapter setting if unset */ - pscancfgout->bsstype = - (puserscanin->bsstype ? puserscanin->bsstype : adapter-> - scanmode); - - /* Set the number of probes to send, use adapter setting if unset */ - numprobes = (puserscanin->numprobes ? puserscanin->numprobes : - adapter->scanprobes); - - /* - * Set the BSSID filter to the incoming configuration, - * if non-zero. If not set, it will remain disabled (all zeros). - */ - memcpy(pscancfgout->specificBSSID, - puserscanin->specificBSSID, - sizeof(pscancfgout->specificBSSID)); - - ssidlen = strlen(puserscanin->specificSSID); - - if (ssidlen) { - pssidtlv = - (struct mrvlietypes_ssidparamset *) pscancfgout-> - tlvbuffer; - pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); - pssidtlv->header.len = cpu_to_le16(ssidlen); - memcpy(pssidtlv->ssid, puserscanin->specificSSID, - ssidlen); - ptlvpos += sizeof(pssidtlv->header) + ssidlen; - } - - /* - * 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 (ssidlen || (memcmp(pscancfgout->specificBSSID, - &zeromac, sizeof(zeromac)) != 0)) { - *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; - *pfilteredscan = 1; - } - } else { - pscancfgout->bsstype = adapter->scanmode; - numprobes = adapter->scanprobes; - } - - /* If the input config or adapter has the number of Probes set, add tlv */ - if (numprobes) { - pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; - pnumprobestlv->header.type = - cpu_to_le16(TLV_TYPE_NUMPROBES); - pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes); - pnumprobestlv->numprobes = cpu_to_le16(numprobes); - - ptlvpos += - sizeof(pnumprobestlv->header) + pnumprobestlv->header.len; - - pnumprobestlv->header.len = - cpu_to_le16(pnumprobestlv->header.len); - } - - /* - * Set the output for the channel TLV to the address in the tlv buffer - * past any TLVs that were added in this fuction (SSID, numprobes). - * channel TLVs will be added past this for each scan command, preserving - * the TLVs that were previously added. - */ - *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; - - if (puserscanin && puserscanin->chanlist[0].channumber) { - - lbs_pr_debug(1, "Scan: Using supplied channel list\n"); - - for (chanidx = 0; - chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX - && puserscanin->chanlist[chanidx].channumber; chanidx++) { - - channel = puserscanin->chanlist[chanidx].channumber; - (pscanchanlist + chanidx)->channumber = channel; + struct mrvlietypes_ssidparamset *ssid_tlv = (void *)tlv; - radiotype = puserscanin->chanlist[chanidx].radiotype; - (pscanchanlist + chanidx)->radiotype = radiotype; - - scantype = puserscanin->chanlist[chanidx].scantype; - - if (scantype == cmd_scan_type_passive) { - (pscanchanlist + - chanidx)->chanscanmode.passivescan = 1; - } else { - (pscanchanlist + - chanidx)->chanscanmode.passivescan = 0; - } - - if (puserscanin->chanlist[chanidx].scantime) { - scandur = - puserscanin->chanlist[chanidx].scantime; - } else { - if (scantype == cmd_scan_type_passive) { - scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; - } else { - scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; - } - } - - (pscanchanlist + chanidx)->minscantime = - cpu_to_le16(scandur); - (pscanchanlist + chanidx)->maxscantime = - cpu_to_le16(scandur); - } + ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid_tlv->header.len = cpu_to_le16(priv->scan_ssid_len); + memcpy(ssid_tlv->ssid, priv->scan_ssid, priv->scan_ssid_len); + return sizeof(ssid_tlv->header) + priv->scan_ssid_len; +} - /* Check if we are only scanning the current channel */ - if ((chanidx == 1) && (puserscanin->chanlist[0].channumber - == - priv->adapter->curbssparams.channel)) { - *pscancurrentonly = 1; - lbs_pr_debug(1, "Scan: Scanning current channel only"); - } - } else { - lbs_pr_debug(1, "Scan: Creating full region channel list\n"); - wlan_scan_create_channel_list(priv, pscanchanlist, - *pfilteredscan); - } +/* + * Add CHANLIST TLV of the form + * + * TLV-ID CHANLIST 01 01 + * length 5b 00 + * channel 1 00 01 00 00 00 64 00 + * radio type 00 + * channel 01 + * scan type 00 + * min scan time 00 00 + * max scan time 64 00 + * channel 2 00 02 00 00 00 64 00 + * channel 3 00 03 00 00 00 64 00 + * channel 4 00 04 00 00 00 64 00 + * channel 5 00 05 00 00 00 64 00 + * channel 6 00 06 00 00 00 64 00 + * channel 7 00 07 00 00 00 64 00 + * channel 8 00 08 00 00 00 64 00 + * channel 9 00 09 00 00 00 64 00 + * channel 10 00 0a 00 00 00 64 00 + * channel 11 00 0b 00 00 00 64 00 + * channel 12 00 0c 00 00 00 64 00 + * channel 13 00 0d 00 00 00 64 00 + * + */ +static int lbs_scan_add_chanlist_tlv(uint8_t *tlv, + struct chanscanparamset *chan_list, + int chan_count) +{ + size_t size = sizeof(struct chanscanparamset) *chan_count; + struct mrvlietypes_chanlistparamset *chan_tlv = (void *)tlv; -out: - return pscancfgout; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + memcpy(chan_tlv->chanscanparam, chan_list, size); + chan_tlv->header.len = cpu_to_le16(size); + return sizeof(chan_tlv->header) + size; } -/** - * @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 maxchanperscan lists - * and sends the portion of the channel TLV along with the other TLVs - * to the wlan_cmd routines for execution in the firmware. + +/* + * Add RATES TLV of the form * - * @param priv A pointer to wlan_private structure - * @param maxchanperscan Maximum number channels to be included in each - * scan command sent to firmware - * @param filteredscan Flag indicating whether or not a BSSID or SSID - * filter is being used for the firmware command - * scan command sent to firmware - * @param pscancfgout Scan configuration used for this scan. - * @param pchantlvout Pointer in the pscancfgout where the channel TLV - * should start. This is past any other TLVs that - * must be sent down in each firmware command. - * @param pscanchanlist List of channels to scan in maxchanperscan segments + * TLV-ID RATES 01 00 + * length 0e 00 + * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c * - * @return 0 or error return otherwise + * The rates are in lbs_bg_rates[], but for the 802.11b + * rates the high bit isn't set. */ -static int wlan_scan_channel_list(wlan_private * priv, - int maxchanperscan, - u8 filteredscan, - struct wlan_scan_cmd_config * pscancfgout, - struct mrvlietypes_chanlistparamset * pchantlvout, - struct chanscanparamset * pscanchanlist) +static int lbs_scan_add_rates_tlv(uint8_t *tlv) { - struct chanscanparamset *ptmpchan; - struct chanscanparamset *pstartchan; - u8 scanband; - int doneearly; - int tlvidx; - int ret = 0; - - ENTER(); + int i; + struct mrvlietypes_ratesparamset *rate_tlv = (void *)tlv; - if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { - lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n", - pscancfgout, pchantlvout, pscanchanlist); - return -1; + rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); + tlv += sizeof(rate_tlv->header); + for (i = 0; i < MAX_RATES; i++) { + *tlv = lbs_bg_rates[i]; + if (*tlv == 0) + break; + /* This code makes sure that the 802.11b rates (1 MBit/s, 2 + MBit/s, 5.5 MBit/s and 11 MBit/s get's the high bit set. + Note that the values are MBit/s * 2, to mark them as + basic rates so that the firmware likes it better */ + if (*tlv == 0x02 || *tlv == 0x04 || + *tlv == 0x0b || *tlv == 0x16) + *tlv |= 0x80; + tlv++; } + rate_tlv->header.len = cpu_to_le16(i); + return sizeof(rate_tlv->header) + i; +} - pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - - /* Set the temp channel struct pointer to the start of the desired list */ - ptmpchan = pscanchanlist; - - /* Loop through the desired channel list, sending a new firmware scan - * commands for each maxchanperscan channels (or for 1,6,11 individually - * if configured accordingly) - */ - while (ptmpchan->channumber) { - - tlvidx = 0; - pchantlvout->header.len = 0; - scanband = ptmpchan->radiotype; - pstartchan = ptmpchan; - doneearly = 0; - - /* Construct the channel TLV for the scan command. Continue to - * insert channel TLVs until: - * - the tlvidx hits the maximum configured per scan command - * - the next channel to insert is 0 (end of desired channel list) - * - doneearly is set (controlling individual scanning of 1,6,11) - */ - while (tlvidx < maxchanperscan && ptmpchan->channumber - && !doneearly) { - - lbs_pr_debug(1, - "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n", - ptmpchan->channumber, ptmpchan->radiotype, - ptmpchan->chanscanmode.passivescan, - ptmpchan->chanscanmode.disablechanfilt, - ptmpchan->maxscantime); - - /* Copy the current channel TLV to the command being prepared */ - memcpy(pchantlvout->chanscanparam + tlvidx, - ptmpchan, sizeof(pchantlvout->chanscanparam)); - - /* Increment the TLV header length by the size appended */ - pchantlvout->header.len += - sizeof(pchantlvout->chanscanparam); - - /* - * 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. - */ - pscancfgout->tlvbufferlen = ((u8 *) pchantlvout - - pscancfgout->tlvbuffer); - - /* Add the size of the channel tlv header and the data length */ - pscancfgout->tlvbufferlen += - (sizeof(pchantlvout->header) - + pchantlvout->header.len); - - /* Increment the index to the channel tlv we are constructing */ - tlvidx++; - - doneearly = 0; - /* 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 (!filteredscan && (ptmpchan->channumber == 1 - || ptmpchan->channumber == 6 - || ptmpchan->channumber == 11)) { - doneearly = 1; - } +/* + * Generate the CMD_802_11_SCAN command with the proper tlv + * for a bunch of channels. + */ +static int lbs_do_scan(struct lbs_private *priv, uint8_t bsstype, + struct chanscanparamset *chan_list, int chan_count) +{ + int ret = -ENOMEM; + struct cmd_ds_802_11_scan *scan_cmd; + uint8_t *tlv; /* pointer into our current, growing TLV storage area */ - /* Increment the tmp pointer to the next channel to be scanned */ - ptmpchan++; + lbs_deb_enter_args(LBS_DEB_SCAN, "bsstype %d, chanlist[].chan %d, chan_count %d", + bsstype, chan_list[0].channumber, chan_count); - /* 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 (!filteredscan && (ptmpchan->channumber == 1 - || ptmpchan->channumber == 6 - || ptmpchan->channumber == 11)) { - doneearly = 1; - } - } + /* create the fixed part for scan command */ + scan_cmd = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); + if (scan_cmd == NULL) + goto out; - /* Send the scan command to the firmware with the specified cfg */ - ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0, - 0, 0, pscancfgout); - } + tlv = scan_cmd->tlvbuffer; + /* TODO: do we need to scan for a specific BSSID? + memcpy(scan_cmd->bssid, priv->scan_bssid, ETH_ALEN); */ + scan_cmd->bsstype = bsstype; + + /* add TLVs */ + if (priv->scan_ssid_len) + tlv += lbs_scan_add_ssid_tlv(priv, tlv); + if (chan_list && chan_count) + tlv += lbs_scan_add_chanlist_tlv(tlv, chan_list, chan_count); + tlv += lbs_scan_add_rates_tlv(tlv); + + /* This is the final data we are about to send */ + scan_cmd->hdr.size = cpu_to_le16(tlv - (uint8_t *)scan_cmd); + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, + sizeof(*scan_cmd)); + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, + tlv - scan_cmd->tlvbuffer); + + ret = __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr, + le16_to_cpu(scan_cmd->hdr.size), + lbs_ret_80211_scan, 0); - LEAVE(); +out: + kfree(scan_cmd); + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); return ret; } + /** * @brief Internal function used to start a scan based on an input config * + * Also used from debugfs + * * 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 priv A pointer to wlan_private structure - * @param puserscanin Pointer to the input configuration for the requested - * scan. + * @param priv A pointer to struct lbs_private structure + * @param full_scan Do a full-scan (blocking) * * @return 0 or < 0 if error */ -int wlan_scan_networks(wlan_private * priv, - const struct wlan_ioctl_user_scan_cfg * puserscanin) +static int lbs_scan_networks(struct lbs_private *priv, int full_scan) { - wlan_adapter *adapter = priv->adapter; - struct mrvlietypes_chanlistparamset *pchantlvout; - struct chanscanparamset * scan_chan_list = NULL; - struct wlan_scan_cmd_config * scan_cfg = NULL; - u8 keeppreviousscan; - u8 filteredscan; - u8 scancurrentchanonly; - int maxchanperscan; - int ret; - - ENTER(); + int ret = -ENOMEM; + struct chanscanparamset *chan_list; + struct chanscanparamset *curr_chans; + int chan_count; + uint8_t bsstype = CMD_BSS_TYPE_ANY; + int numchannels = MRVDRV_CHANNELS_PER_SCAN_CMD; + union iwreq_data wrqu; +#ifdef CONFIG_LIBERTAS_DEBUG + struct bss_descriptor *iter; + int i = 0; + DECLARE_MAC_BUF(mac); +#endif - scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * - WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); - if (scan_chan_list == NULL) { - ret = -ENOMEM; - goto out; - } + lbs_deb_enter_args(LBS_DEB_SCAN, "full_scan %d", full_scan); - scan_cfg = wlan_scan_setup_scan_config(priv, - puserscanin, - &pchantlvout, - scan_chan_list, - &maxchanperscan, - &filteredscan, - &scancurrentchanonly); - if (scan_cfg == NULL) { - ret = -ENOMEM; + /* Cancel any partial outstanding partial scans if this scan + * is a full scan. + */ + if (full_scan && delayed_work_pending(&priv->scan_work)) + cancel_delayed_work(&priv->scan_work); + + /* User-specified bsstype or channel list + TODO: this can be implemented if some user-space application + need the feature. Formerly, it was accessible from debugfs, + but then nowhere used. + if (user_cfg) { + if (user_cfg->bsstype) + bsstype = user_cfg->bsstype; + } */ + + lbs_deb_scan("numchannels %d, bsstype %d\n", numchannels, bsstype); + + /* Create list of channels to scan */ + chan_list = kzalloc(sizeof(struct chanscanparamset) * + LBS_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); + if (!chan_list) { + lbs_pr_alert("SCAN: chan_list empty\n"); goto out; } - keeppreviousscan = 0; + /* We want to scan all channels */ + chan_count = lbs_scan_create_channel_list(priv, chan_list); - if (puserscanin) { - keeppreviousscan = puserscanin->keeppreviousscan; + netif_stop_queue(priv->dev); + netif_carrier_off(priv->dev); + if (priv->mesh_dev) { + netif_stop_queue(priv->mesh_dev); + netif_carrier_off(priv->mesh_dev); } - if (!keeppreviousscan) { - memset(adapter->scantable, 0x00, - sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST); - adapter->numinscantable = 0; + /* Prepare to continue an interrupted scan */ + lbs_deb_scan("chan_count %d, scan_channel %d\n", + chan_count, priv->scan_channel); + curr_chans = chan_list; + /* advance channel list by already-scanned-channels */ + if (priv->scan_channel > 0) { + curr_chans += priv->scan_channel; + chan_count -= priv->scan_channel; } - /* Keep the data path active if we are only scanning our current channel */ - if (!scancurrentchanonly) { - netif_stop_queue(priv->wlan_dev.netdev); - netif_carrier_off(priv->wlan_dev.netdev); - } - - ret = wlan_scan_channel_list(priv, - maxchanperscan, - filteredscan, - scan_cfg, - pchantlvout, - scan_chan_list); - - /* Process the resulting scan table: - * - Remove any bad ssids - * - Update our current BSS information from scan data + /* Send scan command(s) + * numchannels contains the number of channels we should maximally scan + * chan_count is the total number of channels to scan */ - wlan_scan_process_results(priv); - if (priv->adapter->connect_status == libertas_connected) { - netif_carrier_on(priv->wlan_dev.netdev); - netif_wake_queue(priv->wlan_dev.netdev); + while (chan_count) { + int to_scan = min(numchannels, chan_count); + lbs_deb_scan("scanning %d of %d channels\n", + to_scan, chan_count); + ret = lbs_do_scan(priv, bsstype, curr_chans, + to_scan); + if (ret) { + lbs_pr_err("SCAN_CMD failed\n"); + goto out2; + } + curr_chans += to_scan; + chan_count -= to_scan; + + /* somehow schedule the next part of the scan */ + if (chan_count && !full_scan && + !priv->surpriseremoved) { + /* -1 marks just that we're currently scanning */ + if (priv->scan_channel < 0) + priv->scan_channel = to_scan; + else + priv->scan_channel += to_scan; + cancel_delayed_work(&priv->scan_work); + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(300)); + /* skip over GIWSCAN event */ + goto out; + } + } + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); + +#ifdef CONFIG_LIBERTAS_DEBUG + /* Dump the scan table */ + mutex_lock(&priv->lock); + lbs_deb_scan("scan table:\n"); + list_for_each_entry(iter, &priv->network_list, list) + lbs_deb_scan("%02d: BSSID %s, RSSI %d, SSID '%s'\n", + i++, print_mac(mac, iter->bssid), iter->rssi, + escape_essid(iter->ssid, iter->ssid_len)); + mutex_unlock(&priv->lock); +#endif -out: - if (scan_cfg) - kfree(scan_cfg); +out2: + priv->scan_channel = 0; - if (scan_chan_list) - kfree(scan_chan_list); +out: + if (priv->connect_status == LBS_CONNECTED) { + netif_carrier_on(priv->dev); + if (!priv->tx_pending_len) + netif_wake_queue(priv->dev); + } + if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED)) { + netif_carrier_on(priv->mesh_dev); + if (!priv->tx_pending_len) + netif_wake_queue(priv->mesh_dev); + } + kfree(chan_list); - LEAVE(); + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); return ret; } -/** - * @brief Inspect the scan response buffer for pointers to expected TLVs - * - * TLVs can be included at the end of the scan response BSS information. - * Parse the data in the buffer for pointers to TLVs that can potentially - * be passed back in the response - * - * @param ptlv Pointer to the start of the TLV buffer to parse - * @param tlvbufsize size of the TLV buffer - * @param ptsftlv Output parameter: Pointer to the TSF TLV if found - * - * @return void - */ -static -void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv, - int tlvbufsize, - struct mrvlietypes_tsftimestamp ** ptsftlv) -{ - struct mrvlietypes_data *pcurrenttlv; - int tlvbufleft; - u16 tlvtype; - u16 tlvlen; - - pcurrenttlv = ptlv; - tlvbufleft = tlvbufsize; - *ptsftlv = NULL; - lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize); - lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize); - while (tlvbufleft >= sizeof(struct mrvlietypesheader)) { - tlvtype = le16_to_cpu(pcurrenttlv->header.type); - tlvlen = le16_to_cpu(pcurrenttlv->header.len); - switch (tlvtype) { - case TLV_TYPE_TSFTIMESTAMP: - *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv; - break; +void lbs_scan_worker(struct work_struct *work) +{ + struct lbs_private *priv = + container_of(work, struct lbs_private, scan_work.work); - default: - lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n", - tlvtype); - /* Give up, this seems corrupted */ - return; - } /* switch */ - - tlvbufleft -= (sizeof(ptlv->header) + tlvlen); - pcurrenttlv = - (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen); - } /* while */ + lbs_deb_enter(LBS_DEB_SCAN); + lbs_scan_networks(priv, 0); + lbs_deb_leave(LBS_DEB_SCAN); } + +/*********************************************************************/ +/* */ +/* Result interpretation */ +/* */ +/*********************************************************************/ + /** * @brief Interpret a BSS scan response returned from the firmware * * Parse the various fixed fields and IEs passed back for a a BSS probe - * response or beacon from the scan command. Record information as needed - * in the scan table struct bss_descriptor for that entry. + * response or beacon from the scan command. Record information as needed + * in the scan table struct bss_descriptor for that entry. * - * @param pBSSIDEntry Output parameter: Pointer to the BSS Entry + * @param bss Output parameter: Pointer to the BSS Entry * * @return 0 or -1 */ -static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, - u8 ** pbeaconinfo, int *bytesleft) +static int lbs_process_bss(struct bss_descriptor *bss, + uint8_t **pbeaconinfo, int *bytesleft) { - enum ieeetypes_elementid elemID; struct ieeetypes_fhparamset *pFH; struct ieeetypes_dsparamset *pDS; struct ieeetypes_cfparamset *pCF; struct ieeetypes_ibssparamset *pibss; - struct ieeetypes_capinfo *pcap; - struct WLAN_802_11_FIXED_IEs fixedie; - u8 *pcurrentptr; - u8 *pRate; - u8 elemlen; - u8 bytestocopy; - u8 ratesize; - u16 beaconsize; - u8 founddatarateie; - int bytesleftforcurrentbeacon; - - struct IE_WPA *pIe; - const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; - + DECLARE_MAC_BUF(mac); struct ieeetypes_countryinfoset *pcountryinfo; + uint8_t *pos, *end, *p; + uint8_t n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0; + uint16_t beaconsize = 0; + int ret; - ENTER(); - - founddatarateie = 0; - ratesize = 0; - beaconsize = 0; + lbs_deb_enter(LBS_DEB_SCAN); if (*bytesleft >= sizeof(beaconsize)) { /* Extract & convert beacon size from the command buffer */ - memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize)); - beaconsize = le16_to_cpu(beaconsize); + beaconsize = le16_to_cpu(get_unaligned((__le16 *)*pbeaconinfo)); *bytesleft -= sizeof(beaconsize); *pbeaconinfo += sizeof(beaconsize); } if (beaconsize == 0 || beaconsize > *bytesleft) { - *pbeaconinfo += *bytesleft; *bytesleft = 0; - - return -1; + ret = -1; + goto done; } /* Initialize the current working beacon pointer for this BSS iteration */ - pcurrentptr = *pbeaconinfo; + pos = *pbeaconinfo; + end = pos + beaconsize; /* Advance the return beacon pointer past the current beacon */ *pbeaconinfo += beaconsize; *bytesleft -= beaconsize; - bytesleftforcurrentbeacon = beaconsize; - - memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN); - lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n", - pBSSEntry->macaddress[0], pBSSEntry->macaddress[1], - pBSSEntry->macaddress[2], pBSSEntry->macaddress[3], - pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]); + memcpy(bss->bssid, pos, ETH_ALEN); + lbs_deb_scan("process_bss: BSSID %s\n", print_mac(mac, bss->bssid)); + pos += ETH_ALEN; - pcurrentptr += ETH_ALEN; - bytesleftforcurrentbeacon -= ETH_ALEN; - - if (bytesleftforcurrentbeacon < 12) { - lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n"); - return -1; + if ((end - pos) < 12) { + lbs_deb_scan("process_bss: Not enough bytes left\n"); + ret = -1; + goto done; } /* @@ -954,347 +712,295 @@ static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, */ /* RSSI is 1 byte long */ - pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr)); - lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr); - pcurrentptr += 1; - bytesleftforcurrentbeacon -= 1; + bss->rssi = *pos; + lbs_deb_scan("process_bss: RSSI %d\n", *pos); + pos++; /* time stamp is 8 bytes long */ - memcpy(fixedie.timestamp, pcurrentptr, 8); - memcpy(pBSSEntry->timestamp, pcurrentptr, 8); - pcurrentptr += 8; - bytesleftforcurrentbeacon -= 8; + pos += 8; /* beacon interval is 2 bytes long */ - memcpy(&fixedie.beaconinterval, pcurrentptr, 2); - pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval); - pcurrentptr += 2; - bytesleftforcurrentbeacon -= 2; + bss->beaconperiod = le16_to_cpup((void *) pos); + pos += 2; /* capability information is 2 bytes long */ - memcpy(&fixedie.capabilities, pcurrentptr, 2); - lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n", - fixedie.capabilities); - fixedie.capabilities = le16_to_cpu(fixedie.capabilities); - pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities; - memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo)); - pcurrentptr += 2; - bytesleftforcurrentbeacon -= 2; + bss->capability = le16_to_cpup((void *) pos); + lbs_deb_scan("process_bss: capabilities 0x%04x\n", bss->capability); + pos += 2; - /* rest of the current buffer are IE's */ - lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n", - bytesleftforcurrentbeacon); - - lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr, - bytesleftforcurrentbeacon); - - if (pcap->privacy) { - lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n"); - pBSSEntry->privacy = wlan802_11privfilter8021xWEP; - } else { - pBSSEntry->privacy = wlan802_11privfilteracceptall; - } + if (bss->capability & WLAN_CAPABILITY_PRIVACY) + lbs_deb_scan("process_bss: WEP enabled\n"); + if (bss->capability & WLAN_CAPABILITY_IBSS) + bss->mode = IW_MODE_ADHOC; + else + bss->mode = IW_MODE_INFRA; - if (pcap->ibss == 1) { - pBSSEntry->inframode = wlan802_11ibss; - } else { - pBSSEntry->inframode = wlan802_11infrastructure; - } + /* rest of the current buffer are IE's */ + lbs_deb_scan("process_bss: IE len %zd\n", end - pos); + lbs_deb_hex(LBS_DEB_SCAN, "process_bss: IE info", pos, end - pos); /* process variable IE */ - while (bytesleftforcurrentbeacon >= 2) { - elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr)); - elemlen = *((u8 *) pcurrentptr + 1); - - if (bytesleftforcurrentbeacon < elemlen) { - lbs_pr_debug(1, "InterpretIE: error in processing IE, " - "bytes left < IE length\n"); - bytesleftforcurrentbeacon = 0; - continue; - } - - switch (elemID) { + while (pos <= end - 2) { + struct ieee80211_info_element * elem = (void *)pos; - case SSID: - pBSSEntry->ssid.ssidlength = elemlen; - memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2), - elemlen); - lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid); + if (pos + elem->len > end) { + lbs_deb_scan("process_bss: error in processing IE, " + "bytes left < IE length\n"); break; + } - case SUPPORTED_RATES: - memcpy(pBSSEntry->datarates, (pcurrentptr + 2), - elemlen); - memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2), - elemlen); - ratesize = elemlen; - founddatarateie = 1; + switch (elem->id) { + case MFIE_TYPE_SSID: + bss->ssid_len = elem->len; + memcpy(bss->ssid, elem->data, elem->len); + lbs_deb_scan("got SSID IE: '%s', len %u\n", + escape_essid(bss->ssid, bss->ssid_len), + bss->ssid_len); break; - case EXTRA_IE: - lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n"); - pBSSEntry->extra_ie = 1; + case MFIE_TYPE_RATES: + n_basic_rates = min_t(uint8_t, MAX_RATES, elem->len); + memcpy(bss->rates, elem->data, n_basic_rates); + got_basic_rates = 1; + lbs_deb_scan("got RATES IE\n"); break; - case FH_PARAM_SET: - pFH = (struct ieeetypes_fhparamset *) pcurrentptr; - memmove(&pBSSEntry->phyparamset.fhparamset, pFH, + case MFIE_TYPE_FH_SET: + pFH = (struct ieeetypes_fhparamset *) pos; + memmove(&bss->phyparamset.fhparamset, pFH, sizeof(struct ieeetypes_fhparamset)); - pBSSEntry->phyparamset.fhparamset.dwelltime - = - le16_to_cpu(pBSSEntry->phyparamset.fhparamset. - dwelltime); + lbs_deb_scan("got FH IE\n"); break; - case DS_PARAM_SET: - pDS = (struct ieeetypes_dsparamset *) pcurrentptr; - - pBSSEntry->channel = pDS->currentchan; - - memcpy(&pBSSEntry->phyparamset.dsparamset, pDS, + case MFIE_TYPE_DS_SET: + pDS = (struct ieeetypes_dsparamset *) pos; + bss->channel = pDS->currentchan; + memcpy(&bss->phyparamset.dsparamset, pDS, sizeof(struct ieeetypes_dsparamset)); + lbs_deb_scan("got DS IE, channel %d\n", bss->channel); break; - case CF_PARAM_SET: - pCF = (struct ieeetypes_cfparamset *) pcurrentptr; - - memcpy(&pBSSEntry->ssparamset.cfparamset, pCF, + case MFIE_TYPE_CF_SET: + pCF = (struct ieeetypes_cfparamset *) pos; + memcpy(&bss->ssparamset.cfparamset, pCF, sizeof(struct ieeetypes_cfparamset)); + lbs_deb_scan("got CF IE\n"); break; - case IBSS_PARAM_SET: - pibss = (struct ieeetypes_ibssparamset *) pcurrentptr; - pBSSEntry->atimwindow = - le32_to_cpu(pibss->atimwindow); - - memmove(&pBSSEntry->ssparamset.ibssparamset, pibss, + case MFIE_TYPE_IBSS_SET: + pibss = (struct ieeetypes_ibssparamset *) pos; + bss->atimwindow = le16_to_cpu(pibss->atimwindow); + memmove(&bss->ssparamset.ibssparamset, pibss, sizeof(struct ieeetypes_ibssparamset)); - - pBSSEntry->ssparamset.ibssparamset.atimwindow - = - le16_to_cpu(pBSSEntry->ssparamset.ibssparamset. - atimwindow); + lbs_deb_scan("got IBSS IE\n"); break; - /* Handle Country Info IE */ - case COUNTRY_INFO: - pcountryinfo = - (struct ieeetypes_countryinfoset *) pcurrentptr; - - if (pcountryinfo->len < - sizeof(pcountryinfo->countrycode) + case MFIE_TYPE_COUNTRY: + pcountryinfo = (struct ieeetypes_countryinfoset *) pos; + lbs_deb_scan("got COUNTRY IE\n"); + if (pcountryinfo->len < sizeof(pcountryinfo->countrycode) || pcountryinfo->len > 254) { - lbs_pr_debug(1, "InterpretIE: 11D- Err " - "CountryInfo len =%d min=%d max=254\n", - pcountryinfo->len, - sizeof(pcountryinfo->countrycode)); - LEAVE(); - return -1; + lbs_deb_scan("process_bss: 11D- Err CountryInfo len %d, min %zd, max 254\n", + pcountryinfo->len, sizeof(pcountryinfo->countrycode)); + ret = -1; + goto done; } - memcpy(&pBSSEntry->countryinfo, - pcountryinfo, pcountryinfo->len + 2); - lbs_dbg_hex("InterpretIE: 11D- CountryInfo:", - (u8 *) pcountryinfo, - (u32) (pcountryinfo->len + 2)); + memcpy(&bss->countryinfo, pcountryinfo, pcountryinfo->len + 2); + lbs_deb_hex(LBS_DEB_SCAN, "process_bss: 11d countryinfo", + (uint8_t *) pcountryinfo, + (int) (pcountryinfo->len + 2)); break; - case EXTENDED_SUPPORTED_RATES: - /* - * only process extended supported rate - * if data rate is already found. - * data rate IE should come before + case MFIE_TYPE_RATES_EX: + /* only process extended supported rate if data rate is + * already found. Data rate IE should come before * extended supported rate IE */ - if (founddatarateie) { - if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) { - bytestocopy = - (WLAN_SUPPORTED_RATES - ratesize); - } else { - bytestocopy = elemlen; - } - - pRate = (u8 *) pBSSEntry->datarates; - pRate += ratesize; - memmove(pRate, (pcurrentptr + 2), bytestocopy); - - pRate = (u8 *) pBSSEntry->libertas_supported_rates; - - pRate += ratesize; - memmove(pRate, (pcurrentptr + 2), bytestocopy); + lbs_deb_scan("got RATESEX IE\n"); + if (!got_basic_rates) { + lbs_deb_scan("... but ignoring it\n"); + break; } - break; - - case VENDOR_SPECIFIC_221: -#define IE_ID_LEN_FIELDS_BYTES 2 - pIe = (struct IE_WPA *)pcurrentptr; - if (memcmp(pIe->oui, oui01, sizeof(oui01))) - break; + n_ex_rates = elem->len; + if (n_basic_rates + n_ex_rates > MAX_RATES) + n_ex_rates = MAX_RATES - n_basic_rates; - pBSSEntry->wpa_ie_len = min_t(size_t, - elemlen + IE_ID_LEN_FIELDS_BYTES, - sizeof(pBSSEntry->wpa_ie)); - memcpy(pBSSEntry->wpa_ie, pcurrentptr, - pBSSEntry->wpa_ie_len); - lbs_dbg_hex("InterpretIE: Resp WPA_IE", - pBSSEntry->wpa_ie, elemlen); + p = bss->rates + n_basic_rates; + memcpy(p, elem->data, n_ex_rates); break; - case WPA2_IE: - pIe = (struct IE_WPA *)pcurrentptr; - - pBSSEntry->rsn_ie_len = min_t(size_t, - elemlen + IE_ID_LEN_FIELDS_BYTES, - sizeof(pBSSEntry->rsn_ie)); - memcpy(pBSSEntry->rsn_ie, pcurrentptr, - pBSSEntry->rsn_ie_len); - lbs_dbg_hex("InterpretIE: Resp WPA2_IE", - pBSSEntry->rsn_ie, elemlen); + + case MFIE_TYPE_GENERIC: + if (elem->len >= 4 && + elem->data[0] == 0x00 && elem->data[1] == 0x50 && + elem->data[2] == 0xf2 && elem->data[3] == 0x01) { + bss->wpa_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN); + memcpy(bss->wpa_ie, elem, bss->wpa_ie_len); + lbs_deb_scan("got WPA IE\n"); + lbs_deb_hex(LBS_DEB_SCAN, "WPA IE", bss->wpa_ie, elem->len); + } else if (elem->len >= MARVELL_MESH_IE_LENGTH && + elem->data[0] == 0x00 && elem->data[1] == 0x50 && + elem->data[2] == 0x43 && elem->data[3] == 0x04) { + lbs_deb_scan("got mesh IE\n"); + bss->mesh = 1; + } else { + lbs_deb_scan("got generic IE: %02x:%02x:%02x:%02x, len %d\n", + elem->data[0], elem->data[1], + elem->data[2], elem->data[3], + elem->len); + } break; - case TIM: + + case MFIE_TYPE_RSN: + lbs_deb_scan("got RSN IE\n"); + bss->rsn_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN); + memcpy(bss->rsn_ie, elem, bss->rsn_ie_len); + lbs_deb_hex(LBS_DEB_SCAN, "process_bss: RSN_IE", + bss->rsn_ie, elem->len); break; - case CHALLENGE_TEXT: + default: + lbs_deb_scan("got IE 0x%04x, len %d\n", + elem->id, elem->len); break; } - pcurrentptr += elemlen + 2; - - /* need to account for IE ID and IE len */ - bytesleftforcurrentbeacon -= (elemlen + 2); - - } /* while (bytesleftforcurrentbeacon > 2) */ - - return 0; -} + pos += elem->len + 2; + } -/** - * @brief Compare two SSIDs - * - * @param ssid1 A pointer to ssid to compare - * @param ssid2 A pointer to ssid to compare - * - * @return 0--ssid is same, otherwise is different - */ -int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2) -{ - if (!ssid1 || !ssid2) - return -1; + /* Timestamp */ + bss->last_scanned = jiffies; + lbs_unset_basic_rate_flags(bss->rates, sizeof(bss->rates)); - if (ssid1->ssidlength != ssid2->ssidlength) - return -1; + ret = 0; - return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength); +done: + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); + return ret; } /** * @brief This function finds a specific compatible BSSID in the scan list * - * @param adapter A pointer to wlan_adapter + * Used in association code + * + * @param priv A pointer to struct lbs_private * @param bssid BSSID to find in the scan list * @param mode Network mode: Infrastructure or IBSS * * @return index in BSSID list, or error return code (< 0) */ -int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode) +struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv, + uint8_t *bssid, uint8_t mode) { - int ret = -ENETUNREACH; - int i; + struct bss_descriptor *iter_bss; + struct bss_descriptor *found_bss = NULL; + + lbs_deb_enter(LBS_DEB_SCAN); if (!bssid) - return -EFAULT; + goto out; - lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n", - adapter->numinscantable); + lbs_deb_hex(LBS_DEB_SCAN, "looking for", bssid, ETH_ALEN); - /* 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 + /* Look through the scan table for a compatible match. 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; ret < 0 && i < adapter->numinscantable; i++) { - if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) { - switch (mode) { - case wlan802_11infrastructure: - case wlan802_11ibss: - ret = is_network_compatible(adapter, i, mode); - break; - default: - ret = i; + mutex_lock(&priv->lock); + list_for_each_entry (iter_bss, &priv->network_list, list) { + if (compare_ether_addr(iter_bss->bssid, bssid)) + continue; /* bssid doesn't match */ + switch (mode) { + case IW_MODE_INFRA: + case IW_MODE_ADHOC: + if (!is_network_compatible(priv, iter_bss, mode)) break; - } + found_bss = iter_bss; + break; + default: + found_bss = iter_bss; + break; } } + mutex_unlock(&priv->lock); - return ret; +out: + lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss); + return found_bss; } /** * @brief This function finds ssid in ssid list. * - * @param adapter A pointer to wlan_adapter + * Used in association code + * + * @param priv A pointer to struct lbs_private * @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 */ -int libertas_find_SSID_in_list(wlan_adapter * adapter, - struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode) +struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv, + uint8_t *ssid, uint8_t ssid_len, + uint8_t *bssid, uint8_t mode, + int channel) { - int net = -ENETUNREACH; - u8 bestrssi = 0; - int i; - int j; + u32 bestrssi = 0; + struct bss_descriptor * iter_bss = NULL; + struct bss_descriptor * found_bss = NULL; + struct bss_descriptor * tmp_oldest = NULL; - lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable); - - for (i = 0; i < adapter->numinscantable; i++) { - if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) && - (!bssid || - !memcmp(adapter->scantable[i]. - macaddress, bssid, ETH_ALEN))) { - switch (mode) { - case wlan802_11infrastructure: - case wlan802_11ibss: - j = is_network_compatible(adapter, i, mode); - - if (j >= 0) { - if (bssid) { - return i; - } - - if (SCAN_RSSI - (adapter->scantable[i].rssi) - > bestrssi) { - bestrssi = - SCAN_RSSI(adapter-> - scantable[i]. - rssi); - net = i; - } - } else { - if (net == -ENETUNREACH) { - net = j; - } - } - break; - case wlan802_11autounknown: - default: - if (SCAN_RSSI(adapter->scantable[i].rssi) - > bestrssi) { - bestrssi = - SCAN_RSSI(adapter->scantable[i]. - rssi); - net = i; - } + lbs_deb_enter(LBS_DEB_SCAN); + + mutex_lock(&priv->lock); + + list_for_each_entry (iter_bss, &priv->network_list, list) { + if ( !tmp_oldest + || (iter_bss->last_scanned < tmp_oldest->last_scanned)) + tmp_oldest = iter_bss; + + if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len, + ssid, ssid_len) != 0) + continue; /* ssid doesn't match */ + if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0) + continue; /* bssid doesn't match */ + if ((channel > 0) && (iter_bss->channel != channel)) + continue; /* channel doesn't match */ + + switch (mode) { + case IW_MODE_INFRA: + case IW_MODE_ADHOC: + if (!is_network_compatible(priv, iter_bss, mode)) break; + + if (bssid) { + /* Found requested BSSID */ + found_bss = iter_bss; + goto out; + } + + if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { + bestrssi = SCAN_RSSI(iter_bss->rssi); + found_bss = iter_bss; + } + break; + case IW_MODE_AUTO: + default: + if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { + bestrssi = SCAN_RSSI(iter_bss->rssi); + found_bss = iter_bss; } + break; } } - return net; +out: + mutex_unlock(&priv->lock); + lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss); + return found_bss; } /** @@ -1303,201 +1009,341 @@ int libertas_find_SSID_in_list(wlan_adapter * adapter, * Search the scan table for the best SSID that also matches the current * adapter network preference (infrastructure or adhoc) * - * @param adapter A pointer to wlan_adapter + * @param priv A pointer to struct lbs_private * * @return index in BSSID list */ -int libertas_find_best_SSID_in_list(wlan_adapter * adapter, - enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode) +static struct bss_descriptor *lbs_find_best_ssid_in_list(struct lbs_private *priv, + uint8_t mode) { - int bestnet = -ENETUNREACH; - u8 bestrssi = 0; - int i; + uint8_t bestrssi = 0; + struct bss_descriptor *iter_bss; + struct bss_descriptor *best_bss = NULL; - ENTER(); + lbs_deb_enter(LBS_DEB_SCAN); - lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable); + mutex_lock(&priv->lock); - for (i = 0; i < adapter->numinscantable; i++) { + list_for_each_entry (iter_bss, &priv->network_list, list) { switch (mode) { - case wlan802_11infrastructure: - case wlan802_11ibss: - if (is_network_compatible(adapter, i, mode) >= 0) { - if (SCAN_RSSI(adapter->scantable[i].rssi) > - bestrssi) { - bestrssi = - SCAN_RSSI(adapter->scantable[i]. - rssi); - bestnet = i; - } - } + case IW_MODE_INFRA: + case IW_MODE_ADHOC: + if (!is_network_compatible(priv, iter_bss, mode)) + break; + if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) + break; + bestrssi = SCAN_RSSI(iter_bss->rssi); + best_bss = iter_bss; break; - case wlan802_11autounknown: + case IW_MODE_AUTO: default: - if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) { - bestrssi = - SCAN_RSSI(adapter->scantable[i].rssi); - bestnet = i; - } + if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) + break; + bestrssi = SCAN_RSSI(iter_bss->rssi); + best_bss = iter_bss; break; } } - LEAVE(); - return bestnet; + mutex_unlock(&priv->lock); + lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss); + return best_bss; } /** - * @brief Find the AP with specific ssid in the scan list + * @brief Find the best AP + * + * Used from association worker. * - * @param priv A pointer to wlan_private structure + * @param priv A pointer to struct lbs_private structure * @param pSSID A pointer to AP's ssid * * @return 0--success, otherwise--fail */ -int libertas_find_best_network_SSID(wlan_private * priv, - struct WLAN_802_11_SSID *pSSID, - enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, - enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode) +int lbs_find_best_network_ssid(struct lbs_private *priv, uint8_t *out_ssid, + uint8_t *out_ssid_len, uint8_t preferred_mode, + uint8_t *out_mode) { - wlan_adapter *adapter = priv->adapter; - int ret = 0; - struct bss_descriptor *preqbssid; - int i; - - ENTER(); + int ret = -1; + struct bss_descriptor *found; - memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID)); - - wlan_scan_networks(priv, NULL); - if (adapter->surpriseremoved) - return -1; - wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + lbs_deb_enter(LBS_DEB_SCAN); - i = libertas_find_best_SSID_in_list(adapter, preferred_mode); - if (i < 0) { - ret = -1; + priv->scan_ssid_len = 0; + lbs_scan_networks(priv, 1); + if (priv->surpriseremoved) goto out; - } - - preqbssid = &adapter->scantable[i]; - memcpy(pSSID, &preqbssid->ssid, - sizeof(struct WLAN_802_11_SSID)); - *out_mode = preqbssid->inframode; - if (!pSSID->ssidlength) { - ret = -1; + found = lbs_find_best_ssid_in_list(priv, preferred_mode); + if (found && (found->ssid_len > 0)) { + memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE); + *out_ssid_len = found->ssid_len; + *out_mode = found->mode; + ret = 0; } out: - LEAVE(); + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); return ret; } + /** - * @brief Scan Network + * @brief Send a scan command for all available channels filtered on a spec * - * @param dev A pointer to net_device structure - * @param info A pointer to iw_request_info structure - * @param vwrq A pointer to iw_param structure - * @param extra A pointer to extra data buf + * Used in association code and from debugfs * - * @return 0 --success, otherwise fail + * @param priv A pointer to struct lbs_private structure + * @param ssid A pointer to the SSID to scan for + * @param ssid_len Length of the SSID + * + * @return 0-success, otherwise fail */ -int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +int lbs_send_specific_ssid_scan(struct lbs_private *priv, uint8_t *ssid, + uint8_t ssid_len) { - wlan_private *priv = dev->priv; - wlan_adapter *adapter = priv->adapter; - union iwreq_data wrqu; + int ret = 0; - ENTER(); + lbs_deb_enter_args(LBS_DEB_SCAN, "SSID '%s'\n", + escape_essid(ssid, ssid_len)); - if (!wlan_scan_networks(priv, NULL)) { - memset(&wrqu, 0, sizeof(union iwreq_data)); - wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, - NULL); - } + if (!ssid_len) + goto out; - if (adapter->surpriseremoved) - return -1; + memcpy(priv->scan_ssid, ssid, ssid_len); + priv->scan_ssid_len = ssid_len; - LEAVE(); - return 0; + lbs_scan_networks(priv, 1); + if (priv->surpriseremoved) { + ret = -1; + goto out; + } + +out: + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); + return ret; } -/** - * @brief Send a scan command for all available channels filtered on a spec - * - * @param priv A pointer to wlan_private structure - * @param prequestedssid A pointer to AP's ssid - * @param keeppreviousscan Flag used to save/clear scan table before scan - * - * @return 0-success, otherwise fail - */ -int libertas_send_specific_SSID_scan(wlan_private * priv, - struct WLAN_802_11_SSID *prequestedssid, - u8 keeppreviousscan) + + + +/*********************************************************************/ +/* */ +/* Support for Wireless Extensions */ +/* */ +/*********************************************************************/ + + +#define MAX_CUSTOM_LEN 64 + +static inline char *lbs_translate_scan(struct lbs_private *priv, + char *start, char *stop, + struct bss_descriptor *bss) { - wlan_adapter *adapter = priv->adapter; - struct wlan_ioctl_user_scan_cfg scancfg; + struct chan_freq_power *cfp; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + int j; +#define PERFECT_RSSI ((uint8_t)50) +#define WORST_RSSI ((uint8_t)0) +#define RSSI_DIFF ((uint8_t)(PERFECT_RSSI - WORST_RSSI)) + uint8_t rssi; - ENTER(); + lbs_deb_enter(LBS_DEB_SCAN); - if (prequestedssid == NULL) { - return -1; + cfp = lbs_find_cfp_by_band_and_channel(priv, 0, bss->channel); + if (!cfp) { + lbs_deb_scan("Invalid channel number %d\n", bss->channel); + start = NULL; + goto out; } - memset(&scancfg, 0x00, sizeof(scancfg)); + /* First entry *MUST* be the BSSID */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN); + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + + /* SSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IW_ESSID_MAX_SIZE); + start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); + + /* Mode */ + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = bss->mode; + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); + + /* Frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long)cfp->freq * 100000; + iwe.u.freq.e = 1; + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; + iwe.u.qual.level = SCAN_RSSI(bss->rssi); + + rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; + iwe.u.qual.qual = + (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * + (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / + (RSSI_DIFF * RSSI_DIFF); + if (iwe.u.qual.qual > 100) + iwe.u.qual.qual = 100; + + if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + iwe.u.qual.noise = CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]); + } - memcpy(scancfg.specificSSID, prequestedssid->ssid, - prequestedssid->ssidlength); - scancfg.keeppreviousscan = keeppreviousscan; + /* Locally created ad-hoc BSSs won't have beacons if this is the + * only station in the adhoc network; so get signal strength + * from receive statistics. + */ + if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate + && !lbs_ssid_cmp(priv->curbssparams.ssid, + priv->curbssparams.ssid_len, + bss->ssid, bss->ssid_len)) { + int snr, nf; + snr = priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + nf = priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + iwe.u.qual.level = CAL_RSSI(snr, nf); + } + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); - wlan_scan_networks(priv, &scancfg); - if (adapter->surpriseremoved) - return -1; - wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (bss->capability & WLAN_CAPABILITY_PRIVACY) { + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + } else { + iwe.u.data.flags = IW_ENCODE_DISABLED; + } + iwe.u.data.length = 0; + start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); + + current_val = start + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; - LEAVE(); - return 0; + for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) { + /* Bit rate given in 500 kb/s units */ + iwe.u.bitrate.value = bss->rates[j] * 500000; + current_val = iwe_stream_add_value(start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); + } + if ((bss->mode == IW_MODE_ADHOC) && priv->adhoccreate + && !lbs_ssid_cmp(priv->curbssparams.ssid, + priv->curbssparams.ssid_len, + bss->ssid, bss->ssid_len)) { + iwe.u.bitrate.value = 22 * 500000; + current_val = iwe_stream_add_value(start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if((current_val - start) > IW_EV_LCP_LEN) + start = current_val; + + memset(&iwe, 0, sizeof(iwe)); + if (bss->wpa_ie_len) { + char buf[MAX_WPA_IE_LEN]; + memcpy(buf, bss->wpa_ie, bss->wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->wpa_ie_len; + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + + memset(&iwe, 0, sizeof(iwe)); + if (bss->rsn_ie_len) { + char buf[MAX_WPA_IE_LEN]; + memcpy(buf, bss->rsn_ie, bss->rsn_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->rsn_ie_len; + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + + if (bss->mesh) { + char custom[MAX_CUSTOM_LEN]; + char *p = custom; + + iwe.cmd = IWEVCUSTOM; + p += snprintf(p, MAX_CUSTOM_LEN, "mesh-type: olpc"); + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + } + +out: + lbs_deb_leave_args(LBS_DEB_SCAN, "start %p", start); + return start; } + /** - * @brief scan an AP with specific BSSID + * @brief Handle Scan Network ioctl * - * @param priv A pointer to wlan_private structure - * @param bssid A pointer to AP's bssid - * @param keeppreviousscan Flag used to save/clear scan table before scan + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf * - * @return 0-success, otherwise fail + * @return 0 --success, otherwise fail */ -int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan) +int lbs_set_scan(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { - struct wlan_ioctl_user_scan_cfg scancfg; + struct lbs_private *priv = dev->priv; + int ret = 0; - ENTER(); + lbs_deb_enter(LBS_DEB_WEXT); - if (bssid == NULL) { - return -1; + if (!netif_running(dev)) { + ret = -ENETDOWN; + goto out; + } + + /* mac80211 does this: + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_xxx) { + ret = -EOPNOTSUPP; + goto out; + } + */ + + if (wrqu->data.length == sizeof(struct iw_scan_req) && + wrqu->data.flags & IW_SCAN_THIS_ESSID) { + struct iw_scan_req *req = (struct iw_scan_req *)extra; + priv->scan_ssid_len = req->essid_len; + memcpy(priv->scan_ssid, req->essid, priv->scan_ssid_len); + lbs_deb_wext("set_scan, essid '%s'\n", + escape_essid(priv->scan_ssid, priv->scan_ssid_len)); + } else { + priv->scan_ssid_len = 0; } - memset(&scancfg, 0x00, sizeof(scancfg)); - memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID)); - scancfg.keeppreviousscan = keeppreviousscan; + if (!delayed_work_pending(&priv->scan_work)) + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(50)); + /* set marker that currently a scan is taking place */ + priv->scan_channel = -1; - wlan_scan_networks(priv, &scancfg); - if (priv->adapter->surpriseremoved) - return -1; - wait_event_interruptible(priv->adapter->cmd_pending, - !priv->adapter->nr_cmd_pending); + if (priv->surpriseremoved) + ret = -EIO; - LEAVE(); - return 0; +out: + lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); + return ret; } + /** - * @brief Retrieve the scan table entries via wireless tools IOCTL call + * @brief Handle Retrieve scan table ioctl * * @param dev A pointer to net_device structure * @param info A pointer to iw_request_info structure @@ -1506,323 +1352,80 @@ int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppr * * @return 0 --success, otherwise fail */ -int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) +int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) { - wlan_private *priv = dev->priv; - wlan_adapter *adapter = priv->adapter; - int ret = 0; - char *current_ev = extra; - char *end_buf = extra + IW_SCAN_MAX_DATA; - struct chan_freq_power *cfp; - struct bss_descriptor *pscantable; - char *current_val; /* For rates */ - struct iw_event iwe; /* Temporary buffer */ - int i; - int j; - int rate; -#define PERFECT_RSSI ((u8)50) -#define WORST_RSSI ((u8)0) -#define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) - u8 rssi; - - u8 buf[16 + 256 * 2]; - u8 *ptr; - - ENTER(); - - /* - * if there's either commands in the queue or one being - * processed return -EAGAIN for iwlist to retry later. - */ - if (adapter->nr_cmd_pending) +#define SCAN_ITEM_SIZE 128 + struct lbs_private *priv = dev->priv; + int err = 0; + char *ev = extra; + char *stop = ev + dwrq->length; + struct bss_descriptor *iter_bss; + struct bss_descriptor *safe; + + lbs_deb_enter(LBS_DEB_WEXT); + + /* iwlist should wait until the current scan is finished */ + if (priv->scan_channel) return -EAGAIN; - if (adapter->connect_status == libertas_connected) - lbs_pr_debug(1, "Current ssid: %32s\n", - adapter->curbssparams.ssid.ssid); + /* Update RSSI if current BSS is a locally created ad-hoc BSS */ + if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate) + lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, + CMD_OPTION_WAITFORRSP, 0, NULL); - lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n", - adapter->numinscantable); + mutex_lock(&priv->lock); + list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) { + char *next_ev; + unsigned long stale_time; - /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. - * The new API using SIOCGIWSCAN is only limited by buffer size - * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes - * which is 4096. - */ - for (i = 0; i < adapter->numinscantable; i++) { - if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { - lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p " - "MAX_SCAN_CELL_SIZE=%d\n", - i, current_ev, end_buf, MAX_SCAN_CELL_SIZE); + if (stop - ev < SCAN_ITEM_SIZE) { + err = -E2BIG; break; } - pscantable = &adapter->scantable[i]; - - lbs_pr_debug(1, "i=%d ssid: %32s\n", i, pscantable->ssid.ssid); - - cfp = - libertas_find_cfp_by_band_and_channel(adapter, 0, - pscantable->channel); - if (!cfp) { - lbs_pr_debug(1, "Invalid channel number %d\n", - pscantable->channel); + /* For mesh device, list only mesh networks */ + if (dev == priv->mesh_dev && !iter_bss->mesh) continue; - } - if (!ssid_valid(&adapter->scantable[i].ssid)) { + /* Prune old an old scan result */ + stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE; + if (time_after(jiffies, stale_time)) { + list_move_tail(&iter_bss->list, &priv->network_free_list); + clear_bss_descriptor(iter_bss); continue; } - /* First entry *MUST* be the AP MAC address */ - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, - &adapter->scantable[i].macaddress, ETH_ALEN); - - iwe.len = IW_EV_ADDR_LEN; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); - - //Add the ESSID - iwe.u.data.length = adapter->scantable[i].ssid.ssidlength; - - if (iwe.u.data.length > 32) { - iwe.u.data.length = 32; - } - - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - adapter->scantable[i].ssid. - ssid); - - //Add mode - iwe.cmd = SIOCGIWMODE; - iwe.u.mode = adapter->scantable[i].inframode + 1; - iwe.len = IW_EV_UINT_LEN; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); - - //frequency - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = (long)cfp->freq * 100000; - iwe.u.freq.e = 1; - iwe.len = IW_EV_FREQ_LEN; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); - - /* Add quality statistics */ - iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; - iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi); - - rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; - iwe.u.qual.qual = - (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * - (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / - (RSSI_DIFF * RSSI_DIFF); - if (iwe.u.qual.qual > 100) - iwe.u.qual.qual = 100; - else if (iwe.u.qual.qual < 1) - iwe.u.qual.qual = 0; - - if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { - iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; - } else { - iwe.u.qual.noise = - CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); - } - if ((adapter->inframode == wlan802_11ibss) && - !libertas_SSID_cmp(&adapter->curbssparams.ssid, - &adapter->scantable[i].ssid) - && adapter->adhoccreate) { - ret = libertas_prepare_and_send_command(priv, - cmd_802_11_rssi, - 0, - cmd_option_waitforrsp, - 0, NULL); - - if (!ret) { - iwe.u.qual.level = - CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / - AVG_SCALE, - adapter->NF[TYPE_RXPD][TYPE_AVG] / - AVG_SCALE); - } - } - iwe.len = IW_EV_QUAL_LEN; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); - - /* Add encryption capability */ - iwe.cmd = SIOCGIWENCODE; - if (adapter->scantable[i].privacy) { - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - } else { - iwe.u.data.flags = IW_ENCODE_DISABLED; - } - iwe.u.data.length = 0; - iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - adapter->scantable->ssid. - ssid); - - current_val = current_ev + IW_EV_LCP_LEN; - - iwe.cmd = SIOCGIWRATE; - - iwe.u.bitrate.fixed = 0; - iwe.u.bitrate.disabled = 0; - iwe.u.bitrate.value = 0; - - /* Bit rate given in 500 kb/s units (+ 0x80) */ - for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates); - j++) { - if (adapter->scantable[i].libertas_supported_rates[j] == 0) { - break; - } - rate = - (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) * - 500000; - if (rate > iwe.u.bitrate.value) { - iwe.u.bitrate.value = rate; - } - - iwe.u.bitrate.value = - (adapter->scantable[i].libertas_supported_rates[j] - & 0x7f) * 500000; - iwe.len = IW_EV_PARAM_LEN; - current_ev = - iwe_stream_add_value(current_ev, current_val, - end_buf, &iwe, iwe.len); - - } - if ((adapter->scantable[i].inframode == wlan802_11ibss) - && !libertas_SSID_cmp(&adapter->curbssparams.ssid, - &adapter->scantable[i].ssid) - && adapter->adhoccreate) { - iwe.u.bitrate.value = 22 * 500000; - } - iwe.len = IW_EV_PARAM_LEN; - current_ev = - iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, - iwe.len); - - /* Add new value to event */ - current_val = current_ev + IW_EV_LCP_LEN; - - if (adapter->scantable[i].rsn_ie[0] == WPA2_IE) { - memset(&iwe, 0, sizeof(iwe)); - memset(buf, 0, sizeof(buf)); - memcpy(buf, adapter->scantable[i].rsn_ie, - adapter->scantable[i].rsn_ie_len); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = adapter->scantable[i].rsn_ie_len; - iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, buf); - } - if (adapter->scantable[i].wpa_ie[0] == WPA_IE) { - memset(&iwe, 0, sizeof(iwe)); - memset(buf, 0, sizeof(buf)); - memcpy(buf, adapter->scantable[i].wpa_ie, - adapter->scantable[i].wpa_ie_len); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = adapter->scantable[i].wpa_ie_len; - iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, buf); - } - - - if (adapter->scantable[i].extra_ie != 0) { - memset(&iwe, 0, sizeof(iwe)); - memset(buf, 0, sizeof(buf)); - ptr = buf; - ptr += sprintf(ptr, "extra_ie"); - iwe.u.data.length = strlen(buf); - - lbs_pr_debug(1, "iwe.u.data.length %d\n", - iwe.u.data.length); - lbs_pr_debug(1, "BUF: %s \n", buf); - - iwe.cmd = IWEVCUSTOM; - iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; - current_ev = - iwe_stream_add_point(current_ev, end_buf, &iwe, - buf); - } - - current_val = current_ev + IW_EV_LCP_LEN; - - /* - * Check if we added any event - */ - if ((current_val - current_ev) > IW_EV_LCP_LEN) - current_ev = current_val; + /* Translate to WE format this entry */ + next_ev = lbs_translate_scan(priv, ev, stop, iter_bss); + if (next_ev == NULL) + continue; + ev = next_ev; } + mutex_unlock(&priv->lock); - dwrq->length = (current_ev - extra); + dwrq->length = (ev - extra); dwrq->flags = 0; - LEAVE(); - return 0; + lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err); + return err; } -/** - * @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 libertas_prepare_and_send_command to configure a cmd_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 priv A pointer to wlan_private structure - * @param cmd A pointer to cmd_ds_command structure to be sent to - * firmware with the cmd_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 0 or -1 - * - * @sa wlan_scan_create_channel_list - */ -int libertas_cmd_80211_scan(wlan_private * priv, - struct cmd_ds_command *cmd, void *pdata_buf) -{ - struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; - struct wlan_scan_cmd_config *pscancfg; - ENTER(); - pscancfg = pdata_buf; - /* Set fixed field variables in scan command */ - pscan->bsstype = pscancfg->bsstype; - memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID)); - memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); +/*********************************************************************/ +/* */ +/* Command execution */ +/* */ +/*********************************************************************/ - cmd->command = cpu_to_le16(cmd_802_11_scan); - - /* size is equal to the sizeof(fixed portions) + the TLV len + header */ - cmd->size = cpu_to_le16(sizeof(pscan->bsstype) - + sizeof(pscan->BSSID) - + pscancfg->tlvbufferlen + S_DS_GEN); - - lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n", - cmd->command, cmd->size, cmd->seqnum); - LEAVE(); - return 0; -} /** * @brief This function handles the command response of scan * + * Called from handle_cmd_response() in cmdrespc. + * * The response buffer for the scan command has the following * memory layout: * @@ -1839,169 +1442,117 @@ int libertas_cmd_80211_scan(wlan_private * priv, * | bufsize and sizeof the fixed fields above) | * .-----------------------------------------------------------. * - * @param priv A pointer to wlan_private structure + * @param priv A pointer to struct lbs_private structure * @param resp A pointer to cmd_ds_command * * @return 0 or -1 */ -int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) +static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy, + struct cmd_header *resp) { - wlan_adapter *adapter = priv->adapter; - struct cmd_ds_802_11_scan_rsp *pscan; - struct bss_descriptor newbssentry; - struct mrvlietypes_data *ptlv; - struct mrvlietypes_tsftimestamp *ptsftlv; - u8 *pbssinfo; - u16 scanrespsize; + struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; + struct bss_descriptor *iter_bss; + struct bss_descriptor *safe; + uint8_t *bssinfo; + uint16_t scanrespsize; int bytesleft; - int numintable; - int bssIdx; int idx; int tlvbufsize; - u64 tsfval; + int ret; - ENTER(); + lbs_deb_enter(LBS_DEB_SCAN); - pscan = &resp->params.scanresp; + /* Prune old entries from scan table */ + list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) { + unsigned long stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE; + if (time_before(jiffies, stale_time)) + continue; + list_move_tail (&iter_bss->list, &priv->network_free_list); + clear_bss_descriptor(iter_bss); + } - if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) { - lbs_pr_debug(1, - "SCAN_RESP: Invalid number of AP returned (%d)!!\n", - pscan->nr_sets); - LEAVE(); - return -1; + if (scanresp->nr_sets > MAX_NETWORK_COUNT) { + lbs_deb_scan("SCAN_RESP: too many scan results (%d, max %d)\n", + scanresp->nr_sets, MAX_NETWORK_COUNT); + ret = -1; + goto done; } - bytesleft = le16_to_cpu(pscan->bssdescriptsize); - lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft); + bytesleft = le16_to_cpu(scanresp->bssdescriptsize); + lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft); scanrespsize = le16_to_cpu(resp->size); - lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n", - pscan->nr_sets); + lbs_deb_scan("SCAN_RESP: scan results %d\n", scanresp->nr_sets); - numintable = adapter->numinscantable; - pbssinfo = pscan->bssdesc_and_tlvbuffer; + bssinfo = scanresp->bssdesc_and_tlvbuffer; /* The size of the TLV buffer is equal to the entire command response * size (scanrespsize) minus the fixed fields (sizeof()'s), the * BSS Descriptions (bssdescriptsize as bytesLef) and the command * response header (S_DS_GEN) */ - tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) - + sizeof(pscan->nr_sets) + tlvbufsize = scanrespsize - (bytesleft + sizeof(scanresp->bssdescriptsize) + + sizeof(scanresp->nr_sets) + S_DS_GEN); - ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft); - - /* Search the TLV buffer space in the scan response for any valid TLVs */ - wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv); - /* - * Process each scan response returned (pscan->nr_sets). Save + * Process each scan response returned (scanresp->nr_sets). Save * the information in the newbssentry 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 */ - for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { - /* Zero out the newbssentry we are about to store info in */ - memset(&newbssentry, 0x00, sizeof(newbssentry)); + for (idx = 0; idx < scanresp->nr_sets && bytesleft; idx++) { + struct bss_descriptor new; + struct bss_descriptor *found = NULL; + struct bss_descriptor *oldest = NULL; + DECLARE_MAC_BUF(mac); /* Process the data fields and IEs returned for this BSS */ - if ((InterpretBSSDescriptionWithIE(&newbssentry, - &pbssinfo, - &bytesleft) == - 0) - && CHECK_SSID_IS_VALID(&newbssentry.ssid)) { - - lbs_pr_debug(1, - "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", - newbssentry.macaddress[0], - newbssentry.macaddress[1], - newbssentry.macaddress[2], - newbssentry.macaddress[3], - newbssentry.macaddress[4], - newbssentry.macaddress[5]); - - /* - * Search the scan table for the same bssid - */ - for (bssIdx = 0; bssIdx < numintable; bssIdx++) { - if (memcmp(newbssentry.macaddress, - adapter->scantable[bssIdx]. - macaddress, - sizeof(newbssentry.macaddress)) == - 0) { - /* - * If the SSID matches as well, it is a duplicate of - * this entry. Keep the bssIdx set to this - * entry so we replace the old contents in the table - */ - if ((newbssentry.ssid.ssidlength == - adapter->scantable[bssIdx].ssid. - ssidlength) - && - (memcmp - (newbssentry.ssid.ssid, - adapter->scantable[bssIdx].ssid. - ssid, - newbssentry.ssid.ssidlength) == - 0)) { - lbs_pr_debug(1, - "SCAN_RESP: Duplicate of index: %d\n", - bssIdx); - break; - } - } - } - /* - * If the bssIdx is equal to the number of entries in the table, - * the new entry was not a duplicate; append it to the scan - * table - */ - if (bssIdx == numintable) { - /* Range check the bssIdx, keep it limited to the last entry */ - if (bssIdx == MRVDRV_MAX_BSSID_LIST) { - bssIdx--; - } else { - numintable++; - } - } - - /* - * If the TSF TLV was appended to the scan results, save the - * this entries 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 (ptsftlv) { - memcpy(&tsfval, &ptsftlv->tsftable[idx], - sizeof(tsfval)); - tsfval = le64_to_cpu(tsfval); + memset(&new, 0, sizeof (struct bss_descriptor)); + if (lbs_process_bss(&new, &bssinfo, &bytesleft) != 0) { + /* error parsing the scan response, skipped */ + lbs_deb_scan("SCAN_RESP: process_bss returned ERROR\n"); + continue; + } - memcpy(&newbssentry.networktsf, - &tsfval, sizeof(newbssentry.networktsf)); + /* Try to find this bss in the scan table */ + list_for_each_entry (iter_bss, &priv->network_list, list) { + if (is_same_network(iter_bss, &new)) { + found = iter_bss; + break; } - /* Copy the locally created newbssentry to the scan table */ - memcpy(&adapter->scantable[bssIdx], - &newbssentry, - sizeof(adapter->scantable[bssIdx])); + if ((oldest == NULL) || + (iter_bss->last_scanned < oldest->last_scanned)) + oldest = iter_bss; + } + if (found) { + /* found, clear it */ + clear_bss_descriptor(found); + } else if (!list_empty(&priv->network_free_list)) { + /* Pull one from the free list */ + found = list_entry(priv->network_free_list.next, + struct bss_descriptor, list); + list_move_tail(&found->list, &priv->network_list); + } else if (oldest) { + /* If there are no more slots, expire the oldest */ + found = oldest; + clear_bss_descriptor(found); + list_move_tail(&found->list, &priv->network_list); } else { - - /* error parsing/interpreting the scan response, skipped */ - lbs_pr_debug(1, "SCAN_RESP: " - "InterpretBSSDescriptionWithIE returned ERROR\n"); + continue; } - } - lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", - pscan->nr_sets, numintable - adapter->numinscantable, - numintable); + lbs_deb_scan("SCAN_RESP: BSSID %s\n", print_mac(mac, new.bssid)); - /* Update the total number of BSSIDs in the scan table */ - adapter->numinscantable = numintable; + /* Copy the locally created newbssentry to the scan table */ + memcpy(found, &new, offsetof(struct bss_descriptor, list)); + } + + ret = 0; - LEAVE(); - return 0; +done: + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); + return ret; }