diff options
-rw-r--r-- | include/linux/nl80211.h | 25 | ||||
-rw-r--r-- | include/net/cfg80211.h | 57 | ||||
-rw-r--r-- | net/wireless/core.c | 12 | ||||
-rw-r--r-- | net/wireless/core.h | 7 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 250 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 4 | ||||
-rw-r--r-- | net/wireless/scan.c | 70 |
7 files changed, 424 insertions, 1 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index de96783954a..f8b5595ba4a 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 4b0d035be64..e214c85b74d 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 | */ | ||
838 | struct 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 | */ |
1526 | enum wiphy_flags { | 1564 | enum 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 | ||
1539 | struct mac_address { | 1578 | struct mac_address { |
@@ -2355,6 +2394,24 @@ int cfg80211_wext_siwpmksa(struct net_device *dev, | |||
2355 | void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); | 2394 | void 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 | */ | ||
2401 | void 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 | */ | ||
2412 | void 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 bea0d80710c..f924a49b242 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 7a18c10a7fb..e3f7b1d995c 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); | |||
411 | void cfg80211_sme_disassoc(struct net_device *dev, int idx); | 414 | void cfg80211_sme_disassoc(struct net_device *dev, int idx); |
412 | void __cfg80211_scan_done(struct work_struct *wk); | 415 | void __cfg80211_scan_done(struct work_struct *wk); |
413 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); | 416 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); |
417 | void __cfg80211_sched_scan_results(struct work_struct *wk); | ||
418 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | ||
419 | bool driver_initiated); | ||
420 | void __cfg80211_sched_scan_stopped(struct work_struct *wk); | ||
414 | void cfg80211_upload_connect_keys(struct wireless_dev *wdev); | 421 | void cfg80211_upload_connect_keys(struct wireless_dev *wdev); |
415 | int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | 422 | int 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 95dd5832e71..4fac370284c 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 | ||
3362 | static 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 | |||
3517 | out_free: | ||
3518 | kfree(request); | ||
3519 | out: | ||
3520 | return err; | ||
3521 | } | ||
3522 | |||
3523 | static 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 | |||
3360 | static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | 3535 | static 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 | ||
5846 | static int | ||
5847 | nl80211_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 | |||
5655 | void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, | 5868 | void 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 | ||
5925 | void 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 | |||
5944 | void 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 f2af6955a66..2f1bfb87a65 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); |
13 | void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, | 13 | void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, |
14 | struct net_device *netdev); | 14 | struct net_device *netdev); |
15 | void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, | ||
16 | struct net_device *netdev, u32 cmd); | ||
17 | void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, | ||
18 | struct net_device *netdev); | ||
15 | void nl80211_send_reg_change_event(struct regulatory_request *request); | 19 | void nl80211_send_reg_change_event(struct regulatory_request *request); |
16 | void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, | 20 | void 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 62e542a2b19..65dfae3b9d4 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 | } |
94 | EXPORT_SYMBOL(cfg80211_scan_done); | 94 | EXPORT_SYMBOL(cfg80211_scan_done); |
95 | 95 | ||
96 | void __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 | |||
113 | void 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 | } | ||
120 | EXPORT_SYMBOL(cfg80211_sched_scan_results); | ||
121 | |||
122 | void __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 | |||
134 | void cfg80211_sched_scan_stopped(struct wiphy *wiphy) | ||
135 | { | ||
136 | queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_stopped_wk); | ||
137 | } | ||
138 | EXPORT_SYMBOL(cfg80211_sched_scan_stopped); | ||
139 | |||
140 | int __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 | |||
96 | static void bss_release(struct kref *ref) | 166 | static void bss_release(struct kref *ref) |
97 | { | 167 | { |
98 | struct cfg80211_internal_bss *bss; | 168 | struct cfg80211_internal_bss *bss; |