From ffd074fc78c1053b1f1697ebbe5bcfbb64949d16 Mon Sep 17 00:00:00 2001 From: Holger Schurig Date: Fri, 7 Dec 2007 16:52:10 +0100 Subject: libertas: implement new scanning logic This changes the code that is used for scanning and makes it hopefully easier to understand: * move function into logical blocks * create a bunch of lbs_scan_add_XXXX_tlv() functions, that help to create the TLV parameter of CMD_802_11_SCAN * all of them are now called from the much simpler lbs_do_scan() * no **puserscancfg double-pointers :-) Signed-off-by: Holger Schurig Acked-by: Dan Williams Signed-off-by: David Woodhouse Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/dev.h | 3 +- drivers/net/wireless/libertas/scan.c | 871 +++++++++++++++-------------------- 2 files changed, 361 insertions(+), 513 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 1efea63c669..96a34c5bce8 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -366,9 +366,8 @@ struct lbs_adapter { struct cmd_ds_802_11_get_log logmsg; u32 monitormode; + int last_scanned_channel; u8 fw_ready; - - u8 last_scanned_channel; }; #endif diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c index 236bc7daf4d..90c18889b71 100644 --- a/drivers/net/wireless/libertas/scan.c +++ b/drivers/net/wireless/libertas/scan.c @@ -79,6 +79,22 @@ static inline void clear_bss_descriptor (struct bss_descriptor * bss) memset(bss, 0, offsetof(struct bss_descriptor, list)); } +/** + * @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 lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len) +{ + if (ssid1_len != ssid2_len) + return -1; + + return memcmp(ssid1, ssid2, ssid1_len); +} + static inline int match_bss_no_security(struct lbs_802_11_security *secinfo, struct bss_descriptor * match_bss) { @@ -149,6 +165,18 @@ static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo, 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 Check if a scanned network compatible with the driver settings * @@ -184,9 +212,9 @@ static int is_network_compatible(struct lbs_adapter *adapter, goto done; } else if ((matched = match_bss_wpa(&adapter->secinfo, bss))) { lbs_deb_scan( - "is_network_compatible() WPA: wpa_ie=%#x " - "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " - "privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], + "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], adapter->secinfo.wep_enabled ? "e" : "d", adapter->secinfo.WPAenabled ? "e" : "d", adapter->secinfo.WPA2enabled ? "e" : "d", @@ -194,9 +222,9 @@ static int is_network_compatible(struct lbs_adapter *adapter, goto done; } else if ((matched = match_bss_wpa2(&adapter->secinfo, bss))) { lbs_deb_scan( - "is_network_compatible() WPA2: wpa_ie=%#x " - "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " - "privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], + "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], adapter->secinfo.wep_enabled ? "e" : "d", adapter->secinfo.WPAenabled ? "e" : "d", adapter->secinfo.WPA2enabled ? "e" : "d", @@ -205,7 +233,7 @@ static int is_network_compatible(struct lbs_adapter *adapter, } else if ((matched = match_bss_dynamic_wep(&adapter->secinfo, bss))) { lbs_deb_scan( "is_network_compatible() dynamic WEP: " - "wpa_ie=%#x wpa2_ie=%#x privacy=%#x\n", + "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; @@ -213,8 +241,8 @@ static int is_network_compatible(struct lbs_adapter *adapter, /* bss security settings don't match those configured on card */ lbs_deb_scan( - "is_network_compatible() FAILED: wpa_ie=%#x " - "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s privacy=%#x\n", + "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], adapter->secinfo.wep_enabled ? "e" : "d", adapter->secinfo.WPAenabled ? "e" : "d", @@ -226,22 +254,6 @@ done: return matched; } -/** - * @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 lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len) -{ - if (ssid1_len != ssid2_len) - return -1; - - return memcmp(ssid1, ssid2, ssid1_len); -} - @@ -251,6 +263,16 @@ int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len) /* */ /*********************************************************************/ +void lbs_scan_worker(struct work_struct *work) +{ + struct lbs_private *priv = + container_of(work, struct lbs_private, scan_work.work); + + lbs_deb_enter(LBS_DEB_SCAN); + lbs_scan_networks(priv, NULL, 0); + lbs_deb_leave(LBS_DEB_SCAN); +} + /** * @brief Create a channel list for the driver to scan based on region info @@ -271,7 +293,7 @@ int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len) * * @return void */ -static void lbs_scan_create_channel_list(struct lbs_private *priv, +static int lbs_scan_create_channel_list(struct lbs_private *priv, struct chanscanparamset * scanchanlist, u8 filteredscan) { @@ -284,8 +306,6 @@ static void lbs_scan_create_channel_list(struct lbs_private *priv, int nextchan; u8 scantype; - lbs_deb_enter_args(LBS_DEB_SCAN, "filteredscan %d", filteredscan); - chanidx = 0; /* Set the default scan type to the user specified type, will later @@ -353,383 +373,151 @@ static void lbs_scan_create_channel_list(struct lbs_private *priv, } } } + return chanidx; } -/* Delayed partial scan worker */ -void lbs_scan_worker(struct work_struct *work) +/* + * Add SSID TLV of the form: + * + * TLV-ID SSID 00 00 + * length 06 00 + * ssid 4d 4e 54 45 53 54 + */ +static int lbs_scan_add_ssid_tlv(u8 *tlv, + const struct lbs_ioctl_user_scan_cfg *user_cfg) { - struct lbs_private *priv = container_of(work, - struct lbs_private, - scan_work.work); - - lbs_scan_networks(priv, NULL, 0); + struct mrvlietypes_ssidparamset *ssid_tlv = + (struct mrvlietypes_ssidparamset *)tlv; + ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid_tlv->header.len = cpu_to_le16(user_cfg->ssid_len); + memcpy(ssid_tlv->ssid, user_cfg->ssid, user_cfg->ssid_len); + return sizeof(ssid_tlv->header) + user_cfg->ssid_len; } -/** - * @brief Construct a lbs_scan_cmd_config structure to use in issue scan cmds - * - * Application layer or other functions can invoke lbs_scan_networks - * with a scan configuration supplied in a lbs_ioctl_user_scan_cfg struct. - * This structure is used as the basis of one or many lbs_scan_cmd_config - * commands that are sent to the command processing module and sent to - * firmware. - * - * Create a lbs_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. - * Qualify the channel +/* + * Add CHANLIST TLV of the form * - * @param priv A pointer to struct lbs_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 + * 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 * - * @return resulting scan configuration */ -static struct lbs_scan_cmd_config * -lbs_scan_setup_scan_config(struct lbs_private *priv, - const struct lbs_ioctl_user_scan_cfg *puserscanin, - struct mrvlietypes_chanlistparamset ** ppchantlvout, - struct chanscanparamset * pscanchanlist, - int *pmaxchanperscan, - u8 * pfilteredscan, - u8 * pscancurrentonly) +static int lbs_scan_add_chanlist_tlv(u8 *tlv, + struct chanscanparamset *chan_list, + int chan_count) { - struct mrvlietypes_ssidparamset *pssidtlv; - struct lbs_scan_cmd_config *pscancfgout = NULL; - u8 *ptlvpos; - - lbs_deb_enter(LBS_DEB_SCAN); - - 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 : CMD_BSS_TYPE_ANY; - - /* - * Set the BSSID filter to the incoming configuration, - * if non-zero. If not set, it will remain disabled (all zeros). - */ - memcpy(pscancfgout->bssid, puserscanin->bssid, - sizeof(pscancfgout->bssid)); - - if (puserscanin->ssid_len) { - pssidtlv = - (struct mrvlietypes_ssidparamset *) pscancfgout-> - tlvbuffer; - pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); - pssidtlv->header.len = cpu_to_le16(puserscanin->ssid_len); - memcpy(pssidtlv->ssid, puserscanin->ssid, - puserscanin->ssid_len); - ptlvpos += sizeof(pssidtlv->header) + puserscanin->ssid_len; - } - - /* - * 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 ( puserscanin->ssid_len - || (compare_ether_addr(pscancfgout->bssid, &zeromac[0]) != 0)) { - *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; - *pfilteredscan = 1; - } - } else { - pscancfgout->bsstype = CMD_BSS_TYPE_ANY; - } - - /* - * Set the output for the channel TLV to the address in the tlv buffer - * past any TLVs that were added in this fuction (SSID). - * channel TLVs will be added past this for each scan command, preserving - * the TLVs that were previously added. - */ - *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; - - lbs_scan_create_channel_list(priv, pscanchanlist, - *pfilteredscan); -out: - return pscancfgout; + size_t size = sizeof(struct chanscanparamset) * chan_count; + struct mrvlietypes_chanlistparamset *chan_tlv = + (struct mrvlietypes_chanlistparamset *) tlv; + + 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 - * - * Only used from lbs_scan_networks() - * - * Previous routines have created a lbs_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 lbs_cmd routines for execution in the firmware. + +/* + * Add RATES TLV of the form * - * @param priv A pointer to struct lbs_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 lbs_scan_channel_list(struct lbs_private *priv, - int maxchanperscan, - u8 filteredscan, - struct lbs_scan_cmd_config *pscancfgout, - struct mrvlietypes_chanlistparamset * pchantlvout, - struct chanscanparamset * pscanchanlist, - const struct lbs_ioctl_user_scan_cfg *puserscanin, - int full_scan) +static int lbs_scan_add_rates_tlv(u8 *tlv) { - struct chanscanparamset *ptmpchan; - struct chanscanparamset *pstartchan; - u8 scanband; - int doneearly; - int tlvidx; - int ret = 0; - int scanned = 0; - union iwreq_data wrqu; - - lbs_deb_enter_args(LBS_DEB_SCAN, "maxchanperscan %d, filteredscan %d, " - "full_scan %d", maxchanperscan, filteredscan, full_scan); - - if (!pscancfgout || !pchantlvout || !pscanchanlist) { - lbs_deb_scan("pscancfgout, pchantlvout or " - "pscanchanlist is NULL\n"); - ret = -1; - goto out; - } - - pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - - /* Set the temp channel struct pointer to the start of the desired list */ - ptmpchan = pscanchanlist; - - if (priv->adapter->last_scanned_channel && !puserscanin) - ptmpchan += priv->adapter->last_scanned_channel; - - /* 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 && scanned < 2) { - - lbs_deb_scan("channel %d, radio %d, passive %d, " - "dischanflt %d, maxscantime %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 */ - /* Ew, it would be _so_ nice if we could just declare the - variable little-endian and let GCC handle it for us */ - pchantlvout->header.len = - cpu_to_le16(le16_to_cpu(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) - + le16_to_cpu(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; - } - - /* Increment the tmp pointer to the next channel to be scanned */ - ptmpchan++; - scanned++; - - /* 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; - } - } - - /* Send the scan command to the firmware with the specified cfg */ - ret = lbs_prepare_and_send_command(priv, CMD_802_11_SCAN, 0, - 0, 0, pscancfgout); - if (scanned >= 2 && !full_scan) { - ret = 0; - goto done; - } - scanned = 0; - } - -done: - priv->adapter->last_scanned_channel = ptmpchan->channumber; - - if (priv->adapter->last_scanned_channel) { - /* Schedule the next part of the partial scan */ - if (!full_scan && !priv->adapter->surpriseremoved) { - cancel_delayed_work(&priv->scan_work); - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(300)); - } - } else { - /* All done, tell userspace the scan table has been updated */ - memset(&wrqu, 0, sizeof(union iwreq_data)); - wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); + int i; + struct mrvlietypes_ratesparamset *rate_tlv = + (struct mrvlietypes_ratesparamset *) tlv; + + 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++; } - -out: - lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); - return ret; + rate_tlv->header.len = cpu_to_le16(i); + return sizeof(rate_tlv->header) + i; } + /* - * Only used from lbs_scan_networks() -*/ -static void clear_selected_scan_list_entries(struct lbs_adapter *adapter, - const struct lbs_ioctl_user_scan_cfg *scan_cfg) + * 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, + u8 bsstype, + struct chanscanparamset *chan_list, + int chan_count, + const struct lbs_ioctl_user_scan_cfg *user_cfg) { - struct bss_descriptor *bss; - struct bss_descriptor *safe; - u32 clear_ssid_flag = 0, clear_bssid_flag = 0; + int ret = -ENOMEM; + struct lbs_scan_cmd_config *scan_cmd; + u8 *tlv; /* pointer into our current, growing TLV storage area */ - lbs_deb_enter(LBS_DEB_SCAN); + lbs_deb_enter_args(LBS_DEB_SCAN, "bsstype %d, chanlist[].chan %d, " + "chan_count %d", + bsstype, chan_list[0].channumber, chan_count); - if (!scan_cfg) + /* create the fixed part for scan command */ + scan_cmd = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); + if (scan_cmd == NULL) goto out; - - if (scan_cfg->clear_ssid && scan_cfg->ssid_len) - clear_ssid_flag = 1; - - if (scan_cfg->clear_bssid - && (compare_ether_addr(scan_cfg->bssid, &zeromac[0]) != 0) - && (compare_ether_addr(scan_cfg->bssid, &bcastmac[0]) != 0)) { - clear_bssid_flag = 1; - } - - if (!clear_ssid_flag && !clear_bssid_flag) - goto out; - - mutex_lock(&adapter->lock); - list_for_each_entry_safe (bss, safe, &adapter->network_list, list) { - u32 clear = 0; - - /* Check for an SSID match */ - if ( clear_ssid_flag - && (bss->ssid_len == scan_cfg->ssid_len) - && !memcmp(bss->ssid, scan_cfg->ssid, bss->ssid_len)) - clear = 1; - - /* Check for a BSSID match */ - if ( clear_bssid_flag - && !compare_ether_addr(bss->bssid, scan_cfg->bssid)) - clear = 1; - - if (clear) { - list_move_tail (&bss->list, &adapter->network_free_list); - clear_bss_descriptor(bss); - } - } - mutex_unlock(&adapter->lock); + tlv = scan_cmd->tlvbuffer; + if (user_cfg) + memcpy(scan_cmd->bssid, user_cfg->bssid, ETH_ALEN); + scan_cmd->bsstype = bsstype; + + /* add TLVs */ + if (user_cfg && user_cfg->ssid_len) + tlv += lbs_scan_add_ssid_tlv(tlv, user_cfg); + 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->tlvbufferlen = tlv - scan_cmd->tlvbuffer; + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, 1+6); + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, + scan_cmd->tlvbufferlen); + + ret = lbs_prepare_and_send_command(priv, CMD_802_11_SCAN, 0, + CMD_OPTION_WAITFORRSP, 0, scan_cmd); out: - lbs_deb_leave(LBS_DEB_SCAN); + kfree(scan_cmd); + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); + return ret; } @@ -745,29 +533,30 @@ out: * @param priv A pointer to struct lbs_private structure * @param puserscanin Pointer to the input configuration for the requested * scan. - * @param full_scan ??? * * @return 0 or < 0 if error */ int lbs_scan_networks(struct lbs_private *priv, - const struct lbs_ioctl_user_scan_cfg *puserscanin, + const struct lbs_ioctl_user_scan_cfg *user_cfg, int full_scan) { struct lbs_adapter *adapter = priv->adapter; - struct mrvlietypes_chanlistparamset *pchantlvout; - struct chanscanparamset * scan_chan_list = NULL; - struct lbs_scan_cmd_config *scan_cfg = NULL; - u8 filteredscan; - u8 scancurrentchanonly; - int maxchanperscan; - int ret; + int ret = -ENOMEM; + struct chanscanparamset *chan_list; + struct chanscanparamset *curr_chans; + int chan_count; + u8 bsstype = CMD_BSS_TYPE_ANY; + int numchannels = MRVDRV_CHANNELS_PER_SCAN_CMD; + int filteredscan = 0; + union iwreq_data wrqu; #ifdef CONFIG_LIBERTAS_DEBUG - struct bss_descriptor * iter_bss; + struct bss_descriptor *iter; int i = 0; DECLARE_MAC_BUF(mac); #endif - lbs_deb_enter_args(LBS_DEB_SCAN, "full_scan %d", full_scan); + lbs_deb_enter_args(LBS_DEB_SCAN, "full_scan %d", + full_scan); /* Cancel any partial outstanding partial scans if this scan * is a full scan. @@ -775,85 +564,130 @@ int lbs_scan_networks(struct lbs_private *priv, if (full_scan && delayed_work_pending(&priv->scan_work)) cancel_delayed_work(&priv->scan_work); - scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * - LBS_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); - if (scan_chan_list == NULL) { - ret = -ENOMEM; - goto out; + /* Determine same scan parameters */ + if (user_cfg) { + if (user_cfg->bsstype) + bsstype = user_cfg->bsstype; + if (compare_ether_addr(user_cfg->bssid, &zeromac[0]) != 0) { + numchannels = MRVDRV_MAX_CHANNELS_PER_SCAN; + filteredscan = 1; + } } + lbs_deb_scan("numchannels %d, bsstype %d, " + "filteredscan %d\n", + numchannels, bsstype, filteredscan); - scan_cfg = lbs_scan_setup_scan_config(priv, - puserscanin, - &pchantlvout, - scan_chan_list, - &maxchanperscan, - &filteredscan, - &scancurrentchanonly); - if (scan_cfg == NULL) { - ret = -ENOMEM; + /* 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; } - clear_selected_scan_list_entries(adapter, puserscanin); + /* We want to scan all channels */ + chan_count = lbs_scan_create_channel_list(priv, chan_list, + filteredscan); - /* Keep the data path active if we are only scanning our current channel */ - if (!scancurrentchanonly) { - netif_stop_queue(priv->dev); - netif_carrier_off(priv->dev); - if (priv->mesh_dev) { + 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); - } } - ret = lbs_scan_channel_list(priv, - maxchanperscan, - filteredscan, - scan_cfg, - pchantlvout, - scan_chan_list, - puserscanin, - full_scan); + /* Prepare to continue an interrupted scan */ + lbs_deb_scan("chan_count %d, last_scanned_channel %d\n", + chan_count, adapter->last_scanned_channel); + curr_chans = chan_list; + /* advance channel list by already-scanned-channels */ + if (adapter->last_scanned_channel > 0) { + curr_chans += adapter->last_scanned_channel; + chan_count -= adapter->last_scanned_channel; + } + + /* Send scan command(s) + * numchannels contains the number of channels we should maximally scan + * chan_count is the total number of channels to scan + */ + + 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, user_cfg); + 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->adapter->surpriseremoved) { + /* -1 marks just that we're currently scanning */ + if (adapter->last_scanned_channel < 0) + adapter->last_scanned_channel = to_scan; + else + adapter->last_scanned_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(&adapter->lock); - lbs_deb_scan("The scan table contains:\n"); - list_for_each_entry (iter_bss, &adapter->network_list, list) { - lbs_deb_scan("scan %02d, %s, RSSI, %d, SSID '%s'\n", - i++, print_mac(mac, iter_bss->bssid), (s32) iter_bss->rssi, - escape_essid(iter_bss->ssid, iter_bss->ssid_len)); - } + lbs_deb_scan("scan table:\n"); + list_for_each_entry(iter, &adapter->network_list, list) + lbs_deb_scan("%02d: BSSID %s, RSSI %d, SSID '%s'\n", + i++, print_mac(mac, iter->bssid), (s32) iter->rssi, + escape_essid(iter->ssid, iter->ssid_len)); mutex_unlock(&adapter->lock); #endif +out2: + adapter->last_scanned_channel = 0; + +out: if (adapter->connect_status == LBS_CONNECTED) { netif_carrier_on(priv->dev); netif_wake_queue(priv->dev); } - if (priv->mesh_dev && (adapter->mesh_connect_status == LBS_CONNECTED)) { netif_carrier_on(priv->mesh_dev); netif_wake_queue(priv->mesh_dev); } - -out: - if (scan_cfg) - kfree(scan_cfg); - - if (scan_chan_list) - kfree(scan_chan_list); + kfree(chan_list); lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); return ret; } + + + +/*********************************************************************/ +/* */ +/* 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 bss Output parameter: Pointer to the BSS Entry * @@ -898,7 +732,7 @@ static int lbs_process_bss(struct bss_descriptor *bss, *bytesleft -= beaconsize; memcpy(bss->bssid, pos, ETH_ALEN); - lbs_deb_scan("process_bss: AP BSSID %s\n", print_mac(mac, bss->bssid)); + lbs_deb_scan("process_bss: BSSID %s\n", print_mac(mac, bss->bssid)); pos += ETH_ALEN; if ((end - pos) < 12) { @@ -914,7 +748,7 @@ static int lbs_process_bss(struct bss_descriptor *bss, /* RSSI is 1 byte long */ bss->rssi = *pos; - lbs_deb_scan("process_bss: RSSI=%02X\n", *pos); + lbs_deb_scan("process_bss: RSSI %d\n", *pos); pos++; /* time stamp is 8 bytes long */ @@ -926,18 +760,18 @@ static int lbs_process_bss(struct bss_descriptor *bss, /* capability information is 2 bytes long */ bss->capability = le16_to_cpup((void *) pos); - lbs_deb_scan("process_bss: capabilities = 0x%4X\n", bss->capability); + lbs_deb_scan("process_bss: capabilities 0x%04x\n", bss->capability); pos += 2; if (bss->capability & WLAN_CAPABILITY_PRIVACY) - lbs_deb_scan("process_bss: AP WEP enabled\n"); + 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; /* rest of the current buffer are IE's */ - lbs_deb_scan("process_bss: IE length for this AP = %zd\n", end - pos); + 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 */ @@ -955,7 +789,7 @@ static int lbs_process_bss(struct bss_descriptor *bss, case MFIE_TYPE_SSID: bss->ssid_len = elem->len; memcpy(bss->ssid, elem->data, elem->len); - lbs_deb_scan("ssid '%s', ssid length %u\n", + lbs_deb_scan("got SSID IE: '%s', len %u\n", escape_essid(bss->ssid, bss->ssid_len), bss->ssid_len); break; @@ -964,16 +798,14 @@ static int lbs_process_bss(struct bss_descriptor *bss, n_basic_rates = min_t(u8, 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 MFIE_TYPE_FH_SET: pFH = (struct ieeetypes_fhparamset *) pos; memmove(&bss->phyparamset.fhparamset, pFH, sizeof(struct ieeetypes_fhparamset)); -#if 0 /* I think we can store these LE */ - bss->phyparamset.fhparamset.dwelltime - = le16_to_cpu(bss->phyparamset.fhparamset.dwelltime); -#endif + lbs_deb_scan("got FH IE\n"); break; case MFIE_TYPE_DS_SET: @@ -981,12 +813,14 @@ static int lbs_process_bss(struct bss_descriptor *bss, 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 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 MFIE_TYPE_IBSS_SET: @@ -994,18 +828,16 @@ static int lbs_process_bss(struct bss_descriptor *bss, bss->atimwindow = le32_to_cpu(pibss->atimwindow); memmove(&bss->ssparamset.ibssparamset, pibss, sizeof(struct ieeetypes_ibssparamset)); -#if 0 - bss->ssparamset.ibssparamset.atimwindow - = le16_to_cpu(bss->ssparamset.ibssparamset.atimwindow); -#endif + lbs_deb_scan("got IBSS IE\n"); break; 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_deb_scan("process_bss: 11D- Err " - "CountryInfo len =%d min=%zd max=254\n", + "CountryInfo len %d, min %zd, max 254\n", pcountryinfo->len, sizeof(pcountryinfo->countrycode)); ret = -1; @@ -1024,8 +856,11 @@ static int lbs_process_bss(struct bss_descriptor *bss, * already found. Data rate IE should come before * extended supported rate IE */ - if (!got_basic_rates) + lbs_deb_scan("got RATESEX IE\n"); + if (!got_basic_rates) { + lbs_deb_scan("... but ignoring it\n"); break; + } n_ex_rates = elem->len; if (n_basic_rates + n_ex_rates > MAX_RATES) @@ -1044,24 +879,36 @@ static int lbs_process_bss(struct bss_descriptor *bss, bss->wpa_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN); memcpy(bss->wpa_ie, elem, bss->wpa_ie_len); - lbs_deb_hex(LBS_DEB_SCAN, "process_bss: WPA IE", bss->wpa_ie, + 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 generiec IE: " + "%02x:%02x:%02x:%02x, len %d\n", + elem->data[0], elem->data[1], + elem->data[2], elem->data[3], + elem->len); } break; 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); + lbs_deb_hex(LBS_DEB_SCAN, "process_bss: RSN_IE", + bss->rsn_ie, elem->len); break; default: + lbs_deb_scan("got IE 0x%04x, len %d\n", + elem->id, elem->len); break; } @@ -1273,8 +1120,6 @@ int lbs_find_best_network_ssid(struct lbs_private *priv, if (adapter->surpriseremoved) goto out; - wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); - found = lbs_find_best_ssid_in_list(adapter, preferred_mode); if (found && (found->ssid_len > 0)) { memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE); @@ -1288,36 +1133,6 @@ out: return ret; } -/** - * @brief Scan Network - * - * @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 - */ -int lbs_set_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - struct lbs_private *priv = dev->priv; - struct lbs_adapter *adapter = priv->adapter; - - lbs_deb_enter(LBS_DEB_SCAN); - - if (!delayed_work_pending(&priv->scan_work)) { - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(50)); - } - - if (adapter->surpriseremoved) - return -1; - - lbs_deb_leave(LBS_DEB_SCAN); - return 0; -} - /** * @brief Send a scan command for all available channels filtered on a spec @@ -1329,8 +1144,6 @@ int lbs_set_scan(struct net_device *dev, struct iw_request_info *info, * @param ssid_len Length of the SSID * @param clear_ssid Should existing scan results with this SSID * be cleared? - * @param prequestedssid A pointer to AP's ssid - * @param keeppreviousscan Flag used to save/clear scan table before scan * * @return 0-success, otherwise fail */ @@ -1357,7 +1170,6 @@ int lbs_send_specific_ssid_scan(struct lbs_private *priv, ret = -1; goto out; } - wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); out: lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); @@ -1373,6 +1185,7 @@ out: /* */ /*********************************************************************/ + #define MAX_CUSTOM_LEN 64 static inline char *lbs_translate_scan(struct lbs_private *priv, @@ -1398,7 +1211,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, goto out; } - /* First entry *MUST* be the AP BSSID */ + /* 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); @@ -1528,6 +1341,55 @@ out: return start; } + +/** + * @brief Handle Scan Network ioctl + * + * @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 + */ +int lbs_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *wrqu, char *extra) +{ + struct lbs_private *priv = dev->priv; + struct lbs_adapter *adapter = priv->adapter; + + lbs_deb_enter(LBS_DEB_SCAN); + + if (!netif_running(dev)) + return -ENETDOWN; + + /* mac80211 does this: + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_xxx) + return -EOPNOTSUPP; + + if (wrqu->data.length == sizeof(struct iw_scan_req) && + wrqu->data.flags & IW_SCAN_THIS_ESSID) { + req = (struct iw_scan_req *)extra; + ssid = req->essid; + ssid_len = req->essid_len; + } + */ + + 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 */ + adapter->last_scanned_channel = -1; + + if (adapter->surpriseremoved) + return -EIO; + + lbs_deb_leave(LBS_DEB_SCAN); + return 0; +} + + /** * @brief Handle Retrieve scan table ioctl * @@ -1552,6 +1414,10 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, lbs_deb_enter(LBS_DEB_SCAN); + /* iwlist should wait until the current scan is finished */ + if (adapter->last_scanned_channel) + return -EAGAIN; + /* Update RSSI if current BSS is a locally created ad-hoc BSS */ if ((adapter->mode == IW_MODE_ADHOC) && adapter->adhoccreate) { lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, @@ -1609,7 +1475,8 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, /** * @brief Prepare a scan command to be sent to the firmware * - * Called from lbs_prepare_and_send_command() in cmd.c + * Called via lbs_prepare_and_send_command(priv, CMD_802_11_SCAN, ...) + * from cmd.c * * Sends a fixed lenght data part (specifying the BSS type and BSSID filters) * as well as a variable number/length of TLVs to the firmware. @@ -1623,7 +1490,7 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, * @return 0 or -1 */ int lbs_cmd_80211_scan(struct lbs_private *priv, - struct cmd_ds_command *cmd, void *pdata_buf) + struct cmd_ds_command *cmd, void *pdata_buf) { struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; struct lbs_scan_cmd_config *pscancfg = pdata_buf; @@ -1635,32 +1502,14 @@ int lbs_cmd_80211_scan(struct lbs_private *priv, memcpy(pscan->bssid, pscancfg->bssid, ETH_ALEN); memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); - 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) + ETH_ALEN + pscancfg->tlvbufferlen + S_DS_GEN); - lbs_deb_scan("SCAN_CMD: command 0x%04x, size %d, seqnum %d\n", - le16_to_cpu(cmd->command), le16_to_cpu(cmd->size), - le16_to_cpu(cmd->seqnum)); - lbs_deb_leave(LBS_DEB_SCAN); 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 This function handles the command response of scan * @@ -1725,7 +1574,7 @@ int lbs_ret_80211_scan(struct lbs_private *priv, struct cmd_ds_command *resp) lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft); scanrespsize = le16_to_cpu(get_unaligned((u16*)&resp->size)); - lbs_deb_scan("SCAN_RESP: returned %d AP before parsing\n", + lbs_deb_scan("SCAN_RESP: scan results %d\n", pscan->nr_sets); pbssinfo = pscan->bssdesc_and_tlvbuffer; @@ -1788,7 +1637,7 @@ int lbs_ret_80211_scan(struct lbs_private *priv, struct cmd_ds_command *resp) continue; } - lbs_deb_scan("SCAN_RESP: BSSID = %s\n", + lbs_deb_scan("SCAN_RESP: BSSID %s\n", print_mac(mac, new.bssid)); /* Copy the locally created newbssentry to the scan table */ -- cgit v1.2.3