aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/linux/nl80211.h25
-rw-r--r--include/net/cfg80211.h57
-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
7 files changed, 424 insertions, 1 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index de96783954a1..f8b5595ba4af 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -203,6 +203,26 @@
203 * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, 203 * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
204 * partial scan results may be available 204 * partial scan results may be available
205 * 205 *
206 * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan. Like with normal
207 * scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) are passed, they are used
208 * in the probe requests. For broadcast, a broadcast SSID must be
209 * passed (ie. an empty string). If no SSID is passed, no probe
210 * requests are sent and a passive scan is performed.
211 * %NL80211_ATTR_SCAN_FREQUENCIES, if passed, define which channels
212 * should be scanned; if not passed, all channels allowed for the
213 * current regulatory domain are used. Extra IEs can also be passed
214 * from the userspace by using the %NL80211_ATTR_IE attribute.
215 * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan
216 * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
217 * results available.
218 * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
219 * stopped. The driver may issue this event at any time during a
220 * scheduled scan. One reason for stopping the scan is if the hardware
221 * does not support starting an association or a normal scan while running
222 * a scheduled scan. This event is also sent when the
223 * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
224 * is brought down while a scheduled scan was running.
225 *
206 * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation 226 * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
207 * or noise level 227 * or noise level
208 * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to 228 * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
@@ -545,6 +565,11 @@ enum nl80211_commands {
545 NL80211_CMD_GET_WOWLAN, 565 NL80211_CMD_GET_WOWLAN,
546 NL80211_CMD_SET_WOWLAN, 566 NL80211_CMD_SET_WOWLAN,
547 567
568 NL80211_CMD_START_SCHED_SCAN,
569 NL80211_CMD_STOP_SCHED_SCAN,
570 NL80211_CMD_SCHED_SCAN_RESULTS,
571 NL80211_CMD_SCHED_SCAN_STOPPED,
572
548 /* add new commands above here */ 573 /* add new commands above here */
549 574
550 /* used to define NL80211_CMD_MAX below */ 575 /* used to define NL80211_CMD_MAX below */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 4b0d035be64f..e214c85b74d2 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -824,6 +824,33 @@ struct cfg80211_scan_request {
824}; 824};
825 825
826/** 826/**
827 * struct cfg80211_sched_scan_request - scheduled scan request description
828 *
829 * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
830 * @n_ssids: number of SSIDs
831 * @n_channels: total number of channels to scan
832 * @ie: optional information element(s) to add into Probe Request or %NULL
833 * @ie_len: length of ie in octets
834 * @wiphy: the wiphy this was for
835 * @dev: the interface
836 * @channels: channels to scan
837 */
838struct cfg80211_sched_scan_request {
839 struct cfg80211_ssid *ssids;
840 int n_ssids;
841 u32 n_channels;
842 const u8 *ie;
843 size_t ie_len;
844
845 /* internal */
846 struct wiphy *wiphy;
847 struct net_device *dev;
848
849 /* keep last */
850 struct ieee80211_channel *channels[0];
851};
852
853/**
827 * enum cfg80211_signal_type - signal type 854 * enum cfg80211_signal_type - signal type
828 * 855 *
829 * @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available 856 * @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available
@@ -1292,6 +1319,10 @@ struct cfg80211_wowlan {
1292 * @set_power_mgmt: Configure WLAN power management. A timeout value of -1 1319 * @set_power_mgmt: Configure WLAN power management. A timeout value of -1
1293 * allows the driver to adjust the dynamic ps timeout value. 1320 * allows the driver to adjust the dynamic ps timeout value.
1294 * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. 1321 * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
1322 * @sched_scan_start: Tell the driver to start a scheduled scan.
1323 * @sched_scan_stop: Tell the driver to stop an ongoing scheduled
1324 * scan. The driver_initiated flag specifies whether the driver
1325 * itself has informed that the scan has stopped.
1295 * 1326 *
1296 * @mgmt_frame_register: Notify driver that a management frame type was 1327 * @mgmt_frame_register: Notify driver that a management frame type was
1297 * registered. Note that this callback may not sleep, and cannot run 1328 * registered. Note that this callback may not sleep, and cannot run
@@ -1478,6 +1509,12 @@ struct cfg80211_ops {
1478 int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx); 1509 int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx);
1479 void (*get_ringparam)(struct wiphy *wiphy, 1510 void (*get_ringparam)(struct wiphy *wiphy,
1480 u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); 1511 u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
1512
1513 int (*sched_scan_start)(struct wiphy *wiphy,
1514 struct net_device *dev,
1515 struct cfg80211_sched_scan_request *request);
1516 int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev,
1517 bool driver_initiated);
1481}; 1518};
1482 1519
1483/* 1520/*
@@ -1522,6 +1559,7 @@ struct cfg80211_ops {
1522 * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN. 1559 * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN.
1523 * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing 1560 * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing
1524 * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH. 1561 * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH.
1562 * @WIPHY_FLAG_SCHED_SCAN: The device supports scheduled scans.
1525 */ 1563 */
1526enum wiphy_flags { 1564enum wiphy_flags {
1527 WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), 1565 WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
@@ -1534,6 +1572,7 @@ enum wiphy_flags {
1534 WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), 1572 WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7),
1535 WIPHY_FLAG_IBSS_RSN = BIT(8), 1573 WIPHY_FLAG_IBSS_RSN = BIT(8),
1536 WIPHY_FLAG_MESH_AUTH = BIT(10), 1574 WIPHY_FLAG_MESH_AUTH = BIT(10),
1575 WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11),
1537}; 1576};
1538 1577
1539struct mac_address { 1578struct mac_address {
@@ -2355,6 +2394,24 @@ int cfg80211_wext_siwpmksa(struct net_device *dev,
2355void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); 2394void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted);
2356 2395
2357/** 2396/**
2397 * cfg80211_sched_scan_results - notify that new scan results are available
2398 *
2399 * @wiphy: the wiphy which got scheduled scan results
2400 */
2401void cfg80211_sched_scan_results(struct wiphy *wiphy);
2402
2403/**
2404 * cfg80211_sched_scan_stopped - notify that the scheduled scan has stopped
2405 *
2406 * @wiphy: the wiphy on which the scheduled scan stopped
2407 *
2408 * The driver can call this function to inform cfg80211 that the
2409 * scheduled scan had to be stopped, for whatever reason. The driver
2410 * is then called back via the sched_scan_stop operation when done.
2411 */
2412void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
2413
2414/**
2358 * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame 2415 * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame
2359 * 2416 *
2360 * @wiphy: the wiphy reporting the BSS 2417 * @wiphy: the wiphy reporting the BSS
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;