| From: |
| Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org> |
| To: |
| linux-wireless <linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org> |
| Subject: |
| technology preview: scan with cfg80211 |
| Date: |
| Fri, 19 Sep 2008 05:18:48 +0200 |
| Message-ID: |
| <1221794329.10419.9.camel@johannes.berg> |
| Cc: |
| Dan Williams <dcbw-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> |
| Archive-link: |
| Article,
Thread
|
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 <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/nl80211.h>
+#include <linux/if_ether.h>
#include <net/genetlink.h>
/*
@@ -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 <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+#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)));
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html