Return-path: Received: from xc.sipsolutions.net ([83.246.72.84]:39116 "EHLO sipsolutions.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755944AbYISDTZ (ORCPT ); Thu, 18 Sep 2008 23:19:25 -0400 Subject: technology preview: scan with cfg80211 From: Johannes Berg To: linux-wireless Cc: Dan Williams Content-Type: text/plain Date: Fri, 19 Sep 2008 05:18:48 +0200 Message-Id: <1221794329.10419.9.camel@johannes.berg> (sfid-20080919_052010_602864_D520EF40) Mime-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org List-ID: This is totally incomplete: cfg80211 needs to * actually hash on the BSSID instead of putting things into a list * provide BSS list functions so mac80211 need not keep track * allow a "private" area in each scan result with hw-provided size * handle mesh properly * provide a way for the hw to limit the number of SSIDs in a scan in mac80211, we need to * pass the scan request to the hardware offload * handle multiple SSIDs in a scan * use BSS list stuff cfg80211 needs to provide * handle active/passive properly and more... grab the scan-test branch from iw, and you may need more patches than this one for the kernel, I recommend my quilt series from http://johannes.sipsolutions.net/patches/kernel/all/LATEST/ johannes --- include/linux/nl80211.h | 36 +++++ include/net/cfg80211.h | 80 ++++++++++++ net/mac80211/cfg.c | 18 ++ net/mac80211/ieee80211_i.h | 16 +- net/mac80211/main.c | 31 ++++ net/mac80211/mlme.c | 35 ++++- net/mac80211/scan.c | 97 +++++++-------- net/mac80211/wext.c | 11 - net/wireless/Makefile | 2 net/wireless/core.c | 5 net/wireless/core.h | 5 net/wireless/nl80211.c | 285 +++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 6 net/wireless/scan.c | 57 +++++++++ 14 files changed, 612 insertions(+), 72 deletions(-) --- everything.orig/include/linux/nl80211.h 2008-09-19 01:46:24.000000000 +0200 +++ everything/include/linux/nl80211.h 2008-09-19 03:57:50.000000000 +0200 @@ -106,6 +106,11 @@ * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will * store this as a valid request and then query userspace for it. * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN: scan notification (as a reply to NL80211_CMD_GET_SCAN + * and on the "scan" multicast group) + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -148,6 +153,10 @@ enum nl80211_commands { NL80211_CMD_SET_REG, NL80211_CMD_REQ_SET_REG, + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -242,6 +251,11 @@ enum nl80211_commands { * supported interface types, each a flag attribute with the number * of the interface mode. * + * @NL80211_ATTR_SCAN_PASSIVE: flag indicating passive scan + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs + * @NL80211_ATTR_BSS: scan result BSS + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -296,6 +310,14 @@ enum nl80211_attrs { NL80211_ATTR_REG_ALPHA2, NL80211_ATTR_REG_RULES, + NL80211_ATTR_INFORMATION_ELEMENT, + + NL80211_ATTR_SCAN_PASSIVE, + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_SCAN_GENERATION, + NL80211_ATTR_BSS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -594,4 +616,18 @@ enum nl80211_mntr_flags { NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 }; +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + #endif /* __LINUX_NL80211_H */ --- everything.orig/include/net/cfg80211.h 2008-09-19 01:46:24.000000000 +0200 +++ everything/include/net/cfg80211.h 2008-09-19 04:25:53.000000000 +0200 @@ -4,6 +4,7 @@ #include #include #include +#include #include /* @@ -351,6 +352,61 @@ struct ieee80211_regdomain { struct wiphy; /** + * struct cfg80211_ssid - SSID description + * @ssid: the SSID + * @ssid_len: length of the ssid + */ +struct cfg80211_ssid { + u8 *ssid; + u8 ssid_len; +}; + +/** + * struct cfg80211_scan_request - scan request description + * + * @extra_elements: extra information elements to add to probe requests + * @len_extra_elements: length of the IEs + * @ssids: SSIDs to scan for (active scan only) + * @n_ssids: number of SSIDs + * @channels: channels to scan on. + * @n_channels: number of channels for each band + * @active: whether to do active scanning (subject to regulatory + * restrictions in the channels!) + * @wiphy: the wiphy this was for + * @ifidx: the interface index + */ +struct cfg80211_scan_request { + u8 *extra_elements; + size_t len_extra_elements; + struct cfg80211_ssid *ssids; + int n_ssids; + struct ieee80211_channel **channels; + u32 n_channels; + bool active; + + /* internal */ + struct wiphy *wiphy; + int ifidx; +}; + +struct cfg80211_scan_result { + struct ieee80211_channel *channel; + + /* XXX: add flags which members are valid? */ + + u8 bssid[ETH_ALEN]; + u64 tsf; + u16 beacon_interval; + u16 capability; + u8 *information_elements; + size_t len_information_elements; + + /* internal */ + struct list_head list; + unsigned long ts; +}; + +/** * struct cfg80211_ops - backend description for wireless configuration * * This struct is registered by fullmac card drivers and/or wireless stacks @@ -400,6 +456,10 @@ struct wiphy; * @set_mesh_cfg: set mesh parameters (by now, just mesh id) * * @change_bss: Modify parameters for a given BSS. + * + * @scan: Request to do a scan. If returning zero, the scan request is given + * the driver, and will be valid until passed to cfg80211_scan_done(). + * For scan results, call cfg80211_scan_result(). */ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, @@ -455,6 +515,26 @@ struct cfg80211_ops { int (*change_bss)(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params); + + int (*scan)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); }; +/** + * cfg80211_scan_done - notify that scan finished + * + * @request: the corresponding scan request + */ +void cfg80211_scan_done(struct cfg80211_scan_request *request); + +/** + * cfg80211_scan_result - pass scan result to cfg80211 + * + * @request: the scan request + * @result: the result information + */ +void cfg80211_scan_result(struct cfg80211_scan_request *request, + struct cfg80211_scan_result *result, + gfp_t gfp); + #endif /* __NET_CFG80211_H */ --- everything.orig/net/wireless/nl80211.c 2008-09-19 01:46:24.000000000 +0200 +++ everything/net/wireless/nl80211.c 2008-09-19 04:41:38.000000000 +0200 @@ -98,6 +98,11 @@ static struct nla_policy nl80211_policy[ [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, .len = NL80211_HT_CAPABILITY_LEN }, + [NL80211_ATTR_INFORMATION_ELEMENT] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_SCAN_PASSIVE] = { .type = NLA_FLAG }, + [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, + [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, }; /* message building helper */ @@ -1753,6 +1758,226 @@ bad_reg: return -EINVAL; } +static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_scan_request *request; + struct cfg80211_ssid *ssid; + struct ieee80211_channel *channel; + struct nlattr *attr; + struct wiphy *wiphy; + int err, tmp, n_ssids = 0, n_channels = 0, i; + enum ieee80211_band band; + + if (!info->attrs[NL80211_ATTR_SCAN_SSIDS]) + return -EINVAL; + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + return err; + + wiphy = &drv->wiphy; + + if (!drv->ops->scan) { + err = -EOPNOTSUPP; + goto out; + } + + /* + * XXX: could implement some form of scan queueing etc so only a single + * scan can be active at a time... for now, not here. + */ + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) + n_channels++; + if (!n_channels) { + err = -EINVAL; + goto out; + } + } else { + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) + n_ssids++; + + if (n_ssids == 0) { + err = -EINVAL; + goto out; + } + + request = kzalloc(sizeof(*request) + + sizeof(*ssid) * n_ssids + + sizeof(channel) * n_channels, GFP_KERNEL); + if (!request) { + err = -ENOMEM; + goto out; + } + + request->channels = (void *)((char *)request + sizeof(*request)); + request->n_channels = n_channels; + request->ssids = (void *)(request->channels + n_channels); + request->n_ssids = n_ssids; + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + /* user specified, bail out if channel not found */ + request->n_channels = n_channels; + i = 0; + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { + request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + if (!request->channels[i]) { + err = -EINVAL; + goto out_free; + } + i++; + } + } else { + /* all channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + request->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + } + + i = 0; + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { + request->ssids[i].ssid = nla_data(attr); + request->ssids[i].ssid_len = nla_len(attr); + if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + i++; + } + + request->active = !info->attrs[NL80211_ATTR_SCAN_PASSIVE]; + + request->ifidx = dev->ifindex; + request->wiphy = &drv->wiphy; + + rtnl_lock(); + err = drv->ops->scan(&drv->wiphy, dev, request); + rtnl_unlock(); + + out_free: + if (err) + kfree(request); + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, + struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_scan_result *res) +{ + void *hdr; + struct nlattr *bss; + + hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_SCAN); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION, + rdev->scan_generation); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + + bss = nla_nest_start(msg, NL80211_ATTR_BSS); + if (!bss) + goto nla_put_failure; + if (res->bssid) + NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid); + if (res->information_elements && res->len_information_elements) + NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, + res->len_information_elements, + res->information_elements); + if (res->tsf) + NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); + NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); + NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); + + nla_nest_end(msg, bss); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_scan(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *dev; + struct net_device *netdev; + struct cfg80211_scan_result *scan; + int ifidx = cb->args[0]; + int start = cb->args[1], idx = 0; + int err; + + if (!ifidx) { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) + return err; + + if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) + return -EINVAL; + + ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); + if (!ifidx) + return -EINVAL; + cb->args[0] = ifidx; + } + + netdev = dev_get_by_index(&init_net, ifidx); + if (!netdev) + return -ENODEV; + + dev = cfg80211_get_dev_from_ifindex(ifidx); + if (IS_ERR(dev)) { + err = PTR_ERR(dev); + goto out_put_netdev; + } + + spin_lock(&dev->scan_lock); + list_for_each_entry(scan, &dev->scan_list, list) { + if (++idx <= start) + continue; + if (nl80211_send_bss(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + dev, netdev, scan) < 0) { + idx--; + goto out; + } + } + + out: + spin_unlock(&dev->scan_lock); + + cb->args[1] = idx; + err = skb->len; + cfg80211_put_dev(dev); + out_put_netdev: + dev_put(netdev); + + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -1902,12 +2127,26 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_TRIGGER_SCAN, + .doit = nl80211_trigger_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_SCAN, + .policy = nl80211_policy, + .dumpit = nl80211_dump_scan, + }, }; /* multicast groups */ static struct genl_multicast_group nl80211_config_mcgrp = { .name = "config", }; +static struct genl_multicast_group nl80211_scan_mcgrp = { + .name = "scan", +}; /* notification functions */ @@ -1927,6 +2166,48 @@ void nl80211_notify_dev_rename(struct cf genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); } +static int nl80211_send_scan_donemsg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_SCAN); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + /* XXX: I have no idea how to multicast large result sets */ + + /* XXX: we should probably just bounce back the request */ + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); +} + /* initialisation/exit functions */ int nl80211_init(void) @@ -1947,6 +2228,10 @@ int nl80211_init(void) if (err) goto err_out; + err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); + if (err) + goto err_out; + return 0; err_out: genl_unregister_family(&nl80211_fam); --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ everything/net/wireless/scan.c 2008-09-19 04:38:18.000000000 +0200 @@ -0,0 +1,57 @@ +/* + * cfg80211 scan result handling + * + * Copyright 2008 Johannes Berg + */ +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +void cfg80211_scan_done(struct cfg80211_scan_request *request) +{ + struct net_device *dev; + + dev = dev_get_by_index(&init_net, request->ifidx); + if (!dev) + goto out; + + nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); + + dev_put(dev); + /* XXX notify wext too if configured! */ + out: + kfree(request); +} +EXPORT_SYMBOL(cfg80211_scan_done); + +void cfg80211_scan_result(struct cfg80211_scan_request *request, + struct cfg80211_scan_result *result, + gfp_t gfp) +{ + struct cfg80211_scan_result *res; + + res = kzalloc(sizeof(*res) + result->len_information_elements, gfp); + if (!res) + return; + + memcpy(res, result, sizeof(*res)); + res->information_elements = (char *)(res + 1); + memcpy(res->information_elements, + result->information_elements, + result->len_information_elements); + + res->ts = jiffies; + + printk(KERN_DEBUG "cfg80211: got scan result\n"); + /* XXX This is horrible!! */ + + spin_lock(&wiphy_to_dev(request->wiphy)->scan_lock); + list_add_tail(&res->list, &wiphy_to_dev(request->wiphy)->scan_list); + wiphy_to_dev(request->wiphy)->scan_generation++; + spin_unlock(&wiphy_to_dev(request->wiphy)->scan_lock); +} +EXPORT_SYMBOL(cfg80211_scan_result); --- everything.orig/net/mac80211/ieee80211_i.h 2008-09-19 01:46:24.000000000 +0200 +++ everything/net/mac80211/ieee80211_i.h 2008-09-19 02:45:50.000000000 +0200 @@ -651,16 +651,20 @@ struct ieee80211_local { /* Scanning and BSS list */ bool sw_scanning, hw_scanning; + char scan_ssid_value[IEEE80211_MAX_SSID_LEN]; + struct cfg80211_ssid scan_ssid; + struct cfg80211_scan_request int_scan_req; + struct cfg80211_scan_request *scan_req; + struct ieee80211_channel *scan_channel; int scan_channel_idx; - enum ieee80211_band scan_band; enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; unsigned long last_scan_completed; struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; - struct ieee80211_channel *oper_channel, *scan_channel; - u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; - size_t scan_ssid_len; + + struct ieee80211_channel *oper_channel; + struct list_head bss_list; struct ieee80211_bss *bss_hash[STA_HASH_SIZE]; spinlock_t bss_lock; @@ -921,7 +925,7 @@ void ieee80211_send_probe_req(struct iee /* scan/BSS handling */ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, - u8 *ssid, size_t ssid_len); + struct cfg80211_scan_request *req); int ieee80211_scan_results(struct ieee80211_local *local, struct iw_request_info *info, char *buf, size_t len); @@ -936,7 +940,7 @@ int ieee80211_sta_set_extra_ie(struct ie void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, - u8 *ssid, size_t ssid_len); + struct cfg80211_scan_request *req); struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, --- everything.orig/net/mac80211/scan.c 2008-09-19 01:46:24.000000000 +0200 +++ everything/net/mac80211/scan.c 2008-09-19 04:34:37.000000000 +0200 @@ -330,6 +330,7 @@ ieee80211_rx_result ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) { + struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; struct ieee80211_bss *bss; u8 *elements; @@ -339,6 +340,7 @@ ieee80211_scan_rx(struct ieee80211_sub_i __le16 fc; bool presp, beacon = false; struct ieee802_11_elems elems; + struct cfg80211_scan_result cfgres; if (skb->len < 2) return RX_DROP_UNUSABLE; @@ -385,6 +387,18 @@ ieee80211_scan_rx(struct ieee80211_sub_i if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return RX_DROP_MONITOR; + if (local->scan_req && local->scan_req != &local->int_scan_req) { + memset(&cfgres, 0, sizeof(cfgres)); + memcpy(cfgres.bssid, mgmt->bssid, ETH_ALEN); + cfgres.channel = channel; + cfgres.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + cfgres.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); + cfgres.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); + cfgres.information_elements = elements; + cfgres.len_information_elements = skb->len - baselen; + cfg80211_scan_result(local->scan_req, &cfgres, GFP_ATOMIC); + } + bss = ieee80211_bss_info_update(sdata->local, rx_status, mgmt, skb->len, &elems, freq, beacon); @@ -433,6 +447,13 @@ void ieee80211_scan_completed(struct iee if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) return; + if (WARN_ON(!local->scan_req)) + return; + + if (local->scan_req != &local->int_scan_req) + cfg80211_scan_done(local->scan_req); + local->scan_req = NULL; + local->last_scan_completed = jiffies; memset(&wrqu, 0, sizeof(wrqu)); @@ -497,7 +518,6 @@ void ieee80211_scan_work(struct work_str struct ieee80211_local *local = container_of(work, struct ieee80211_local, scan_work.work); struct ieee80211_sub_if_data *sdata = local->scan_sdata; - struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; int skip; unsigned long next_delay = 0; @@ -510,33 +530,13 @@ void ieee80211_scan_work(struct work_str switch (local->scan_state) { case SCAN_SET_CHANNEL: - /* - * Get current scan band. scan_band may be IEEE80211_NUM_BANDS - * after we successfully scanned the last channel of the last - * band (and the last band is supported by the hw) - */ - if (local->scan_band < IEEE80211_NUM_BANDS) - sband = local->hw.wiphy->bands[local->scan_band]; - else - sband = NULL; - - /* - * If we are at an unsupported band and have more bands - * left to scan, advance to the next supported one. - */ - while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) { - local->scan_band++; - sband = local->hw.wiphy->bands[local->scan_band]; - local->scan_channel_idx = 0; - } - /* if no more bands/channels left, complete scan */ - if (!sband || local->scan_channel_idx >= sband->n_channels) { + if (local->scan_channel_idx >= local->scan_req->n_channels) { ieee80211_scan_completed(local_to_hw(local)); return; } skip = 0; - chan = &sband->channels[local->scan_channel_idx]; + chan = local->scan_req->channels[local->scan_channel_idx]; if (chan->flags & IEEE80211_CHAN_DISABLED || (sdata->vif.type == NL80211_IFTYPE_ADHOC && @@ -555,15 +555,6 @@ void ieee80211_scan_work(struct work_str /* advance state machine to next channel/band */ local->scan_channel_idx++; - if (local->scan_channel_idx >= sband->n_channels) { - /* - * scan_band may end up == IEEE80211_NUM_BANDS, but - * we'll catch that case above and complete the scan - * if that is the case. - */ - local->scan_band++; - local->scan_channel_idx = 0; - } if (skip) break; @@ -578,8 +569,9 @@ void ieee80211_scan_work(struct work_str if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN) break; - ieee80211_send_probe_req(sdata, NULL, local->scan_ssid, - local->scan_ssid_len); + ieee80211_send_probe_req(sdata, NULL, + local->scan_req->ssids[0].ssid, + local->scan_req->ssids[0].ssid_len); next_delay = IEEE80211_CHANNEL_TIME; break; } @@ -590,14 +582,19 @@ void ieee80211_scan_work(struct work_str int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, - u8 *ssid, size_t ssid_len) + struct cfg80211_scan_request *req) { struct ieee80211_local *local = scan_sdata->local; struct ieee80211_sub_if_data *sdata; - if (ssid_len > IEEE80211_MAX_SSID_LEN) + if (!req) return -EINVAL; + if (local->scan_req && local->scan_req != req) + return -EBUSY; + + local->scan_req = req; + /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS * BSSID: MACAddress @@ -625,7 +622,10 @@ int ieee80211_start_scan(struct ieee8021 int rc; local->hw_scanning = true; - rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len); + /* XXX: pass full request */ + rc = local->ops->hw_scan(local_to_hw(local), + req->ssids[0].ssid, + req->ssids[0].ssid_len); if (rc) { local->hw_scanning = false; return rc; @@ -648,15 +648,10 @@ int ieee80211_start_scan(struct ieee8021 } rcu_read_unlock(); - if (ssid) { - local->scan_ssid_len = ssid_len; - memcpy(local->scan_ssid, ssid, ssid_len); - } else - local->scan_ssid_len = 0; local->scan_state = SCAN_SET_CHANNEL; local->scan_channel_idx = 0; - local->scan_band = IEEE80211_BAND_2GHZ; local->scan_sdata = scan_sdata; + local->scan_req = req; netif_addr_lock_bh(local->mdev); local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; @@ -676,13 +671,21 @@ int ieee80211_start_scan(struct ieee8021 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, - u8 *ssid, size_t ssid_len) + struct cfg80211_scan_request *req) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_sta *ifsta; + if (!req) + return -EINVAL; + + if (local->scan_req && local->scan_req != req) + return -EBUSY; + + local->scan_req = req; + if (sdata->vif.type != NL80211_IFTYPE_STATION) - return ieee80211_start_scan(sdata, ssid, ssid_len); + return ieee80211_start_scan(sdata, req); /* * STA has a state machine that might need to defer scanning @@ -697,10 +700,6 @@ int ieee80211_request_scan(struct ieee80 } ifsta = &sdata->u.sta; - - ifsta->scan_ssid_len = ssid_len; - if (ssid_len) - memcpy(ifsta->scan_ssid, ssid, ssid_len); set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); queue_work(local->hw.workqueue, &ifsta->work); --- everything.orig/net/mac80211/wext.c 2008-09-19 01:46:24.000000000 +0200 +++ everything/net/mac80211/wext.c 2008-09-19 02:45:50.000000000 +0200 @@ -532,8 +532,6 @@ static int ieee80211_ioctl_siwscan(struc { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct iw_scan_req *req = NULL; - u8 *ssid = NULL; - size_t ssid_len = 0; if (!netif_running(dev)) return -ENETDOWN; @@ -548,11 +546,14 @@ static int ieee80211_ioctl_siwscan(struc 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 (req->essid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + memcpy(sdata->local->scan_ssid_value, req->essid, + req->essid_len); + sdata->local->scan_ssid.ssid_len = req->essid_len; } - return ieee80211_request_scan(sdata, ssid, ssid_len); + return ieee80211_request_scan(sdata, &sdata->local->int_scan_req); } --- everything.orig/net/mac80211/mlme.c 2008-09-19 01:47:20.000000000 +0200 +++ everything/net/mac80211/mlme.c 2008-09-19 02:55:12.000000000 +0200 @@ -1903,7 +1903,15 @@ static void ieee80211_sta_merge_ibss(str printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " "IBSS networks with same SSID (merge)\n", sdata->dev->name); - ieee80211_request_scan(sdata, ifsta->ssid, ifsta->ssid_len); + + /* XXX maybe racy? */ + if (sdata->local->scan_req) + return; + + memcpy(sdata->local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); + sdata->local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + ieee80211_request_scan(sdata, &sdata->local->int_scan_req); } @@ -2120,8 +2128,15 @@ dont_join: IEEE80211_SCAN_INTERVAL)) { printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " "join\n", sdata->dev->name); - return ieee80211_request_scan(sdata, ifsta->ssid, - ifsta->ssid_len); + + /* XXX maybe racy? */ + if (local->scan_req) + return -EBUSY; + + memcpy(local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); + local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + return ieee80211_request_scan(sdata, &local->int_scan_req); } else if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED) { int interval = IEEE80211_SCAN_INTERVAL; @@ -2216,11 +2231,16 @@ static int ieee80211_sta_config_auth(str } else { if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) { ifsta->assoc_scan_tries++; + /* XXX maybe racy? */ + if (local->scan_req) + return -1; + memcpy(local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) - ieee80211_start_scan(sdata, NULL, 0); + local->int_scan_req.ssids[0].ssid_len = 0; else - ieee80211_start_scan(sdata, ifsta->ssid, - ifsta->ssid_len); + local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + ieee80211_start_scan(sdata, &local->int_scan_req); ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE; set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); } else @@ -2256,8 +2276,7 @@ static void ieee80211_sta_work(struct wo ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE && ifsta->state != IEEE80211_STA_MLME_ASSOCIATE && test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) { - ieee80211_start_scan(sdata, ifsta->scan_ssid, - ifsta->scan_ssid_len); + ieee80211_start_scan(sdata, local->scan_req); return; } --- everything.orig/net/mac80211/cfg.c 2008-09-19 01:53:51.000000000 +0200 +++ everything/net/mac80211/cfg.c 2008-09-19 02:23:46.000000000 +0200 @@ -993,6 +993,23 @@ static int ieee80211_change_bss(struct w return 0; } +static int ieee80211_scan(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_scan_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (req->n_ssids != 1) + return -EINVAL; + + return ieee80211_request_scan(sdata, req); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1017,4 +1034,5 @@ struct cfg80211_ops mac80211_config_ops .dump_mpath = ieee80211_dump_mpath, #endif .change_bss = ieee80211_change_bss, + .scan = ieee80211_scan, }; --- everything.orig/net/wireless/Makefile 2008-09-19 02:04:30.000000000 +0200 +++ everything/net/wireless/Makefile 2008-09-19 02:04:36.000000000 +0200 @@ -1,5 +1,5 @@ obj-$(CONFIG_WIRELESS_EXT) += wext.o obj-$(CONFIG_CFG80211) += cfg80211.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o cfg80211-$(CONFIG_NL80211) += nl80211.o --- everything.orig/net/mac80211/main.c 2008-09-19 02:27:31.000000000 +0200 +++ everything/net/mac80211/main.c 2008-09-19 02:45:50.000000000 +0200 @@ -793,25 +793,33 @@ int ieee80211_register_hw(struct ieee802 enum ieee80211_band band; struct net_device *mdev; struct ieee80211_master_priv *mpriv; + int channels, i, j; /* * generic code guarantees at least one band, * set this very early because much code assumes * that hw.conf.channel is assigned */ + channels = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[band]; - if (sband) { + if (sband && !local->oper_channel) { /* init channel we're on */ local->hw.conf.channel = local->oper_channel = local->scan_channel = &sband->channels[0]; - break; } + if (sband) + channels += sband->n_channels; } + local->int_scan_req.n_channels = channels; + local->int_scan_req.channels = kzalloc(sizeof(void *) * channels, GFP_KERNEL); + if (!local->int_scan_req.channels) + return -ENOMEM; + /* if low-level driver supports AP, we also support VLAN */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); @@ -821,7 +829,7 @@ int ieee80211_register_hw(struct ieee802 result = wiphy_register(local->hw.wiphy); if (result < 0) - return result; + goto fail_wiphy_register; /* * We use the number of queues for feature tests (QoS, HT) internally @@ -932,6 +940,20 @@ int ieee80211_register_hw(struct ieee802 ieee80211_led_init(local); + /* alloc wext scan information */ + i = 0; + local->int_scan_req.ssids = &local->scan_ssid; + local->scan_ssid.ssid = local->scan_ssid_value; + local->int_scan_req.n_ssids = 1; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!hw->wiphy->bands[band]) + continue; + for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) { + local->int_scan_req.channels[i] = &hw->wiphy->bands[band]->channels[j]; + i++; + } + } + return 0; fail_wep: @@ -950,6 +972,8 @@ fail_workqueue: free_netdev(local->mdev); fail_mdev_alloc: wiphy_unregister(local->hw.wiphy); +fail_wiphy_register: + kfree(local->int_scan_req.channels); return result; } EXPORT_SYMBOL(ieee80211_register_hw); @@ -995,6 +1019,7 @@ void ieee80211_unregister_hw(struct ieee ieee80211_wep_free(local); ieee80211_led_exit(local); free_netdev(local->mdev); + kfree(local->int_scan_req.channels); } EXPORT_SYMBOL(ieee80211_unregister_hw); --- everything.orig/net/wireless/nl80211.h 2008-09-19 03:09:58.000000000 +0200 +++ everything/net/wireless/nl80211.h 2008-09-19 03:16:42.000000000 +0200 @@ -7,6 +7,8 @@ extern int nl80211_init(void); extern void nl80211_exit(void); extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev); #else static inline int nl80211_init(void) { @@ -19,6 +21,10 @@ static inline void nl80211_notify_dev_re struct cfg80211_registered_device *rdev) { } +static inline void +nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{} #endif /* CONFIG_NL80211 */ #endif /* __NET_WIRELESS_NL80211_H */ --- everything.orig/net/wireless/core.c 2008-09-19 03:38:18.000000000 +0200 +++ everything/net/wireless/core.c 2008-09-19 04:27:15.000000000 +0200 @@ -244,6 +244,8 @@ struct wiphy *wiphy_new(struct cfg80211_ mutex_init(&drv->mtx); mutex_init(&drv->devlist_mtx); INIT_LIST_HEAD(&drv->netdev_list); + spin_lock_init(&drv->scan_lock); + INIT_LIST_HEAD(&drv->scan_list); device_initialize(&drv->wiphy.dev); drv->wiphy.dev.class = &ieee80211_class; @@ -361,8 +363,11 @@ EXPORT_SYMBOL(wiphy_unregister); void cfg80211_dev_free(struct cfg80211_registered_device *drv) { + struct cfg80211_scan_result *scan, *tmp; mutex_destroy(&drv->mtx); mutex_destroy(&drv->devlist_mtx); + list_for_each_entry_safe(scan, tmp, &drv->scan_list, list) + kfree(scan); kfree(drv); } --- everything.orig/net/wireless/core.h 2008-09-19 03:37:53.000000000 +0200 +++ everything/net/wireless/core.h 2008-09-19 04:26:22.000000000 +0200 @@ -28,6 +28,11 @@ struct cfg80211_registered_device { struct mutex devlist_mtx; struct list_head netdev_list; + /* scan results */ + spinlock_t scan_lock; + struct list_head scan_list; + u32 scan_generation; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));