aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorLuciano Coelho <coelho@ti.com>2011-05-11 10:09:35 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-05-11 15:12:26 -0400
commit807f8a8c300435d5483e8d78df9dcdbc27333166 (patch)
tree1537d40e149d7a8712fe63d17ea3b51093bf03a1 /net/wireless
parent6bdbdbf4a151a3a1333818cd17a7d7795e936041 (diff)
cfg80211/nl80211: add support for scheduled scans
Implement new functionality for scheduled scan offload. With this feature we can scan automatically at certain intervals. The idea is that the hardware can perform scan automatically and filter on desired results without waking up the host unnecessarily. Add NL80211_CMD_START_SCHED_SCAN and NL80211_CMD_STOP_SCHED_SCAN commands to the nl80211 interface. When results are available they are reported by NL80211_CMD_SCHED_SCAN_RESULTS events. The userspace is informed when the scheduled scan has stopped with a NL80211_CMD_SCHED_SCAN_STOPPED event, which can be triggered either by the driver or by a call to NL80211_CMD_STOP_SCHED_SCAN. Signed-off-by: Luciano Coelho <coelho@ti.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c12
-rw-r--r--net/wireless/core.h7
-rw-r--r--net/wireless/nl80211.c250
-rw-r--r--net/wireless/nl80211.h4
-rw-r--r--net/wireless/scan.c70
5 files changed, 342 insertions, 1 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index bea0d80710c8..f924a49b2425 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -370,7 +370,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
370 spin_lock_init(&rdev->bss_lock); 370 spin_lock_init(&rdev->bss_lock);
371 INIT_LIST_HEAD(&rdev->bss_list); 371 INIT_LIST_HEAD(&rdev->bss_list);
372 INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); 372 INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
373 373 INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
374 INIT_WORK(&rdev->sched_scan_stopped_wk, __cfg80211_sched_scan_stopped);
374#ifdef CONFIG_CFG80211_WEXT 375#ifdef CONFIG_CFG80211_WEXT
375 rdev->wiphy.wext = &cfg80211_wext_handler; 376 rdev->wiphy.wext = &cfg80211_wext_handler;
376#endif 377#endif
@@ -672,6 +673,11 @@ static void wdev_cleanup_work(struct work_struct *work)
672 ___cfg80211_scan_done(rdev, true); 673 ___cfg80211_scan_done(rdev, true);
673 } 674 }
674 675
676 if (WARN_ON(rdev->sched_scan_req &&
677 rdev->sched_scan_req->dev == wdev->netdev)) {
678 __cfg80211_stop_sched_scan(rdev, false);
679 }
680
675 cfg80211_unlock_rdev(rdev); 681 cfg80211_unlock_rdev(rdev);
676 682
677 mutex_lock(&rdev->devlist_mtx); 683 mutex_lock(&rdev->devlist_mtx);
@@ -759,6 +765,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
759 break; 765 break;
760 case NL80211_IFTYPE_P2P_CLIENT: 766 case NL80211_IFTYPE_P2P_CLIENT:
761 case NL80211_IFTYPE_STATION: 767 case NL80211_IFTYPE_STATION:
768 cfg80211_lock_rdev(rdev);
769 __cfg80211_stop_sched_scan(rdev, false);
770 cfg80211_unlock_rdev(rdev);
771
762 wdev_lock(wdev); 772 wdev_lock(wdev);
763#ifdef CONFIG_CFG80211_WEXT 773#ifdef CONFIG_CFG80211_WEXT
764 kfree(wdev->wext.ie); 774 kfree(wdev->wext.ie);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 7a18c10a7fb6..e3f7b1d995cd 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -60,8 +60,11 @@ struct cfg80211_registered_device {
60 struct rb_root bss_tree; 60 struct rb_root bss_tree;
61 u32 bss_generation; 61 u32 bss_generation;
62 struct cfg80211_scan_request *scan_req; /* protected by RTNL */ 62 struct cfg80211_scan_request *scan_req; /* protected by RTNL */
63 struct cfg80211_sched_scan_request *sched_scan_req;
63 unsigned long suspend_at; 64 unsigned long suspend_at;
64 struct work_struct scan_done_wk; 65 struct work_struct scan_done_wk;
66 struct work_struct sched_scan_results_wk;
67 struct work_struct sched_scan_stopped_wk;
65 68
66#ifdef CONFIG_NL80211_TESTMODE 69#ifdef CONFIG_NL80211_TESTMODE
67 struct genl_info *testmode_info; 70 struct genl_info *testmode_info;
@@ -411,6 +414,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
411void cfg80211_sme_disassoc(struct net_device *dev, int idx); 414void cfg80211_sme_disassoc(struct net_device *dev, int idx);
412void __cfg80211_scan_done(struct work_struct *wk); 415void __cfg80211_scan_done(struct work_struct *wk);
413void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); 416void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
417void __cfg80211_sched_scan_results(struct work_struct *wk);
418int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
419 bool driver_initiated);
420void __cfg80211_sched_scan_stopped(struct work_struct *wk);
414void cfg80211_upload_connect_keys(struct wireless_dev *wdev); 421void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
415int cfg80211_change_iface(struct cfg80211_registered_device *rdev, 422int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
416 struct net_device *dev, enum nl80211_iftype ntype, 423 struct net_device *dev, enum nl80211_iftype ntype,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 95dd5832e719..4fac370284c0 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -761,6 +761,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
761 } 761 }
762 CMD(set_channel, SET_CHANNEL); 762 CMD(set_channel, SET_CHANNEL);
763 CMD(set_wds_peer, SET_WDS_PEER); 763 CMD(set_wds_peer, SET_WDS_PEER);
764 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
765 CMD(sched_scan_start, START_SCHED_SCAN);
764 766
765#undef CMD 767#undef CMD
766 768
@@ -3357,6 +3359,179 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3357 return err; 3359 return err;
3358} 3360}
3359 3361
3362static int nl80211_start_sched_scan(struct sk_buff *skb,
3363 struct genl_info *info)
3364{
3365 struct cfg80211_sched_scan_request *request;
3366 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3367 struct net_device *dev = info->user_ptr[1];
3368 struct cfg80211_ssid *ssid;
3369 struct ieee80211_channel *channel;
3370 struct nlattr *attr;
3371 struct wiphy *wiphy;
3372 int err, tmp, n_ssids = 0, n_channels, i;
3373 enum ieee80211_band band;
3374 size_t ie_len;
3375
3376 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3377 !rdev->ops->sched_scan_start)
3378 return -EOPNOTSUPP;
3379
3380 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3381 return -EINVAL;
3382
3383 if (rdev->sched_scan_req)
3384 return -EINPROGRESS;
3385
3386 wiphy = &rdev->wiphy;
3387
3388 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3389 n_channels = validate_scan_freqs(
3390 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
3391 if (!n_channels)
3392 return -EINVAL;
3393 } else {
3394 n_channels = 0;
3395
3396 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3397 if (wiphy->bands[band])
3398 n_channels += wiphy->bands[band]->n_channels;
3399 }
3400
3401 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3402 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3403 tmp)
3404 n_ssids++;
3405
3406 if (n_ssids > wiphy->max_scan_ssids)
3407 return -EINVAL;
3408
3409 if (info->attrs[NL80211_ATTR_IE])
3410 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3411 else
3412 ie_len = 0;
3413
3414 if (ie_len > wiphy->max_scan_ie_len)
3415 return -EINVAL;
3416
3417 request = kzalloc(sizeof(*request)
3418 + sizeof(*ssid) * n_ssids
3419 + sizeof(channel) * n_channels
3420 + ie_len, GFP_KERNEL);
3421 if (!request)
3422 return -ENOMEM;
3423
3424 if (n_ssids)
3425 request->ssids = (void *)&request->channels[n_channels];
3426 request->n_ssids = n_ssids;
3427 if (ie_len) {
3428 if (request->ssids)
3429 request->ie = (void *)(request->ssids + n_ssids);
3430 else
3431 request->ie = (void *)(request->channels + n_channels);
3432 }
3433
3434 i = 0;
3435 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3436 /* user specified, bail out if channel not found */
3437 nla_for_each_nested(attr,
3438 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
3439 tmp) {
3440 struct ieee80211_channel *chan;
3441
3442 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3443
3444 if (!chan) {
3445 err = -EINVAL;
3446 goto out_free;
3447 }
3448
3449 /* ignore disabled channels */
3450 if (chan->flags & IEEE80211_CHAN_DISABLED)
3451 continue;
3452
3453 request->channels[i] = chan;
3454 i++;
3455 }
3456 } else {
3457 /* all channels */
3458 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3459 int j;
3460 if (!wiphy->bands[band])
3461 continue;
3462 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
3463 struct ieee80211_channel *chan;
3464
3465 chan = &wiphy->bands[band]->channels[j];
3466
3467 if (chan->flags & IEEE80211_CHAN_DISABLED)
3468 continue;
3469
3470 request->channels[i] = chan;
3471 i++;
3472 }
3473 }
3474 }
3475
3476 if (!i) {
3477 err = -EINVAL;
3478 goto out_free;
3479 }
3480
3481 request->n_channels = i;
3482
3483 i = 0;
3484 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3485 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3486 tmp) {
3487 if (request->ssids[i].ssid_len >
3488 IEEE80211_MAX_SSID_LEN) {
3489 err = -EINVAL;
3490 goto out_free;
3491 }
3492 memcpy(request->ssids[i].ssid, nla_data(attr),
3493 nla_len(attr));
3494 request->ssids[i].ssid_len = nla_len(attr);
3495 i++;
3496 }
3497 }
3498
3499 if (info->attrs[NL80211_ATTR_IE]) {
3500 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3501 memcpy((void *)request->ie,
3502 nla_data(info->attrs[NL80211_ATTR_IE]),
3503 request->ie_len);
3504 }
3505
3506 request->dev = dev;
3507 request->wiphy = &rdev->wiphy;
3508
3509 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
3510 if (!err) {
3511 rdev->sched_scan_req = request;
3512 nl80211_send_sched_scan(rdev, dev,
3513 NL80211_CMD_START_SCHED_SCAN);
3514 goto out;
3515 }
3516
3517out_free:
3518 kfree(request);
3519out:
3520 return err;
3521}
3522
3523static int nl80211_stop_sched_scan(struct sk_buff *skb,
3524 struct genl_info *info)
3525{
3526 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3527
3528 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3529 !rdev->ops->sched_scan_stop)
3530 return -EOPNOTSUPP;
3531
3532 return __cfg80211_stop_sched_scan(rdev, false);
3533}
3534
3360static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, 3535static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3361 struct cfg80211_registered_device *rdev, 3536 struct cfg80211_registered_device *rdev,
3362 struct wireless_dev *wdev, 3537 struct wireless_dev *wdev,
@@ -5327,6 +5502,22 @@ static struct genl_ops nl80211_ops[] = {
5327 .dumpit = nl80211_dump_scan, 5502 .dumpit = nl80211_dump_scan,
5328 }, 5503 },
5329 { 5504 {
5505 .cmd = NL80211_CMD_START_SCHED_SCAN,
5506 .doit = nl80211_start_sched_scan,
5507 .policy = nl80211_policy,
5508 .flags = GENL_ADMIN_PERM,
5509 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5510 NL80211_FLAG_NEED_RTNL,
5511 },
5512 {
5513 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
5514 .doit = nl80211_stop_sched_scan,
5515 .policy = nl80211_policy,
5516 .flags = GENL_ADMIN_PERM,
5517 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5518 NL80211_FLAG_NEED_RTNL,
5519 },
5520 {
5330 .cmd = NL80211_CMD_AUTHENTICATE, 5521 .cmd = NL80211_CMD_AUTHENTICATE,
5331 .doit = nl80211_authenticate, 5522 .doit = nl80211_authenticate,
5332 .policy = nl80211_policy, 5523 .policy = nl80211_policy,
@@ -5652,6 +5843,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,
5652 return -EMSGSIZE; 5843 return -EMSGSIZE;
5653} 5844}
5654 5845
5846static int
5847nl80211_send_sched_scan_msg(struct sk_buff *msg,
5848 struct cfg80211_registered_device *rdev,
5849 struct net_device *netdev,
5850 u32 pid, u32 seq, int flags, u32 cmd)
5851{
5852 void *hdr;
5853
5854 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
5855 if (!hdr)
5856 return -1;
5857
5858 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5859 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5860
5861 return genlmsg_end(msg, hdr);
5862
5863 nla_put_failure:
5864 genlmsg_cancel(msg, hdr);
5865 return -EMSGSIZE;
5866}
5867
5655void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, 5868void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
5656 struct net_device *netdev) 5869 struct net_device *netdev)
5657{ 5870{
@@ -5709,6 +5922,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
5709 nl80211_scan_mcgrp.id, GFP_KERNEL); 5922 nl80211_scan_mcgrp.id, GFP_KERNEL);
5710} 5923}
5711 5924
5925void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
5926 struct net_device *netdev)
5927{
5928 struct sk_buff *msg;
5929
5930 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5931 if (!msg)
5932 return;
5933
5934 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
5935 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
5936 nlmsg_free(msg);
5937 return;
5938 }
5939
5940 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5941 nl80211_scan_mcgrp.id, GFP_KERNEL);
5942}
5943
5944void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
5945 struct net_device *netdev, u32 cmd)
5946{
5947 struct sk_buff *msg;
5948
5949 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
5950 if (!msg)
5951 return;
5952
5953 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
5954 nlmsg_free(msg);
5955 return;
5956 }
5957
5958 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5959 nl80211_scan_mcgrp.id, GFP_KERNEL);
5960}
5961
5712/* 5962/*
5713 * This can happen on global regulatory changes or device specific settings 5963 * This can happen on global regulatory changes or device specific settings
5714 * based on custom world regulatory domains. 5964 * based on custom world regulatory domains.
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index f2af6955a665..2f1bfb87a651 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -12,6 +12,10 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
12 struct net_device *netdev); 12 struct net_device *netdev);
13void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, 13void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
14 struct net_device *netdev); 14 struct net_device *netdev);
15void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
16 struct net_device *netdev, u32 cmd);
17void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
18 struct net_device *netdev);
15void nl80211_send_reg_change_event(struct regulatory_request *request); 19void nl80211_send_reg_change_event(struct regulatory_request *request);
16void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, 20void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
17 struct net_device *netdev, 21 struct net_device *netdev,
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 62e542a2b192..65dfae3b9d41 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -93,6 +93,76 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
93} 93}
94EXPORT_SYMBOL(cfg80211_scan_done); 94EXPORT_SYMBOL(cfg80211_scan_done);
95 95
96void __cfg80211_sched_scan_results(struct work_struct *wk)
97{
98 struct cfg80211_registered_device *rdev;
99
100 rdev = container_of(wk, struct cfg80211_registered_device,
101 sched_scan_results_wk);
102
103 cfg80211_lock_rdev(rdev);
104
105 /* we don't have sched_scan_req anymore if the scan is stopping */
106 if (rdev->sched_scan_req)
107 nl80211_send_sched_scan_results(rdev,
108 rdev->sched_scan_req->dev);
109
110 cfg80211_unlock_rdev(rdev);
111}
112
113void cfg80211_sched_scan_results(struct wiphy *wiphy)
114{
115 /* ignore if we're not scanning */
116 if (wiphy_to_dev(wiphy)->sched_scan_req)
117 queue_work(cfg80211_wq,
118 &wiphy_to_dev(wiphy)->sched_scan_results_wk);
119}
120EXPORT_SYMBOL(cfg80211_sched_scan_results);
121
122void __cfg80211_sched_scan_stopped(struct work_struct *wk)
123{
124 struct cfg80211_registered_device *rdev;
125
126 rdev = container_of(wk, struct cfg80211_registered_device,
127 sched_scan_stopped_wk);
128
129 cfg80211_lock_rdev(rdev);
130 __cfg80211_stop_sched_scan(rdev, true);
131 cfg80211_unlock_rdev(rdev);
132}
133
134void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
135{
136 queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_stopped_wk);
137}
138EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
139
140int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
141 bool driver_initiated)
142{
143 int err;
144 struct net_device *dev;
145
146 ASSERT_RDEV_LOCK(rdev);
147
148 if (!rdev->sched_scan_req)
149 return 0;
150
151 dev = rdev->sched_scan_req->dev;
152
153 err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev,
154 driver_initiated);
155 if (err)
156 return err;
157
158 nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
159
160 kfree(rdev->sched_scan_req);
161 rdev->sched_scan_req = NULL;
162
163 return err;
164}
165
96static void bss_release(struct kref *ref) 166static void bss_release(struct kref *ref)
97{ 167{
98 struct cfg80211_internal_bss *bss; 168 struct cfg80211_internal_bss *bss;