diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-05-16 14:55:42 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-05-16 19:32:19 -0400 |
commit | e00cf3b9eb7839b952e434a75bff6b99e47337ac (patch) | |
tree | ef583ab8ac09bf703026650d4bc7777e6a3864d3 /net/wireless | |
parent | 1a8218e96271790a07dd7065a2ef173e0f67e328 (diff) | |
parent | 3b8ab88acaceb505aa06ef3bbf3a73b92470ae78 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
Conflicts:
drivers/net/wireless/iwlwifi/iwl-agn-tx.c
net/mac80211/sta_info.h
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 89 | ||||
-rw-r--r-- | net/wireless/core.h | 33 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_wep.c | 3 | ||||
-rw-r--r-- | net/wireless/mlme.c | 10 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 670 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 4 | ||||
-rw-r--r-- | net/wireless/reg.c | 2 | ||||
-rw-r--r-- | net/wireless/scan.c | 77 | ||||
-rw-r--r-- | net/wireless/sysfs.c | 2 | ||||
-rw-r--r-- | net/wireless/util.c | 126 |
10 files changed, 953 insertions, 63 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index bbf1fa11107a..c22ef3492ee6 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -370,7 +370,7 @@ 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 | #ifdef CONFIG_CFG80211_WEXT | 374 | #ifdef CONFIG_CFG80211_WEXT |
375 | rdev->wiphy.wext = &cfg80211_wext_handler; | 375 | rdev->wiphy.wext = &cfg80211_wext_handler; |
376 | #endif | 376 | #endif |
@@ -416,6 +416,67 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
416 | } | 416 | } |
417 | EXPORT_SYMBOL(wiphy_new); | 417 | EXPORT_SYMBOL(wiphy_new); |
418 | 418 | ||
419 | static int wiphy_verify_combinations(struct wiphy *wiphy) | ||
420 | { | ||
421 | const struct ieee80211_iface_combination *c; | ||
422 | int i, j; | ||
423 | |||
424 | /* If we have combinations enforce them */ | ||
425 | if (wiphy->n_iface_combinations) | ||
426 | wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; | ||
427 | |||
428 | for (i = 0; i < wiphy->n_iface_combinations; i++) { | ||
429 | u32 cnt = 0; | ||
430 | u16 all_iftypes = 0; | ||
431 | |||
432 | c = &wiphy->iface_combinations[i]; | ||
433 | |||
434 | /* Combinations with just one interface aren't real */ | ||
435 | if (WARN_ON(c->max_interfaces < 2)) | ||
436 | return -EINVAL; | ||
437 | |||
438 | /* Need at least one channel */ | ||
439 | if (WARN_ON(!c->num_different_channels)) | ||
440 | return -EINVAL; | ||
441 | |||
442 | if (WARN_ON(!c->n_limits)) | ||
443 | return -EINVAL; | ||
444 | |||
445 | for (j = 0; j < c->n_limits; j++) { | ||
446 | u16 types = c->limits[j].types; | ||
447 | |||
448 | /* | ||
449 | * interface types shouldn't overlap, this is | ||
450 | * used in cfg80211_can_change_interface() | ||
451 | */ | ||
452 | if (WARN_ON(types & all_iftypes)) | ||
453 | return -EINVAL; | ||
454 | all_iftypes |= types; | ||
455 | |||
456 | if (WARN_ON(!c->limits[j].max)) | ||
457 | return -EINVAL; | ||
458 | |||
459 | /* Shouldn't list software iftypes in combinations! */ | ||
460 | if (WARN_ON(wiphy->software_iftypes & types)) | ||
461 | return -EINVAL; | ||
462 | |||
463 | cnt += c->limits[j].max; | ||
464 | /* | ||
465 | * Don't advertise an unsupported type | ||
466 | * in a combination. | ||
467 | */ | ||
468 | if (WARN_ON((wiphy->interface_modes & types) != types)) | ||
469 | return -EINVAL; | ||
470 | } | ||
471 | |||
472 | /* You can't even choose that many! */ | ||
473 | if (WARN_ON(cnt < c->max_interfaces)) | ||
474 | return -EINVAL; | ||
475 | } | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
419 | int wiphy_register(struct wiphy *wiphy) | 480 | int wiphy_register(struct wiphy *wiphy) |
420 | { | 481 | { |
421 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 482 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
@@ -444,6 +505,10 @@ int wiphy_register(struct wiphy *wiphy) | |||
444 | if (WARN_ON(ifmodes != wiphy->interface_modes)) | 505 | if (WARN_ON(ifmodes != wiphy->interface_modes)) |
445 | wiphy->interface_modes = ifmodes; | 506 | wiphy->interface_modes = ifmodes; |
446 | 507 | ||
508 | res = wiphy_verify_combinations(wiphy); | ||
509 | if (res) | ||
510 | return res; | ||
511 | |||
447 | /* sanity check supported bands/channels */ | 512 | /* sanity check supported bands/channels */ |
448 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 513 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
449 | sband = wiphy->bands[band]; | 514 | sband = wiphy->bands[band]; |
@@ -493,6 +558,13 @@ int wiphy_register(struct wiphy *wiphy) | |||
493 | return -EINVAL; | 558 | return -EINVAL; |
494 | } | 559 | } |
495 | 560 | ||
561 | if (rdev->wiphy.wowlan.n_patterns) { | ||
562 | if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || | ||
563 | rdev->wiphy.wowlan.pattern_min_len > | ||
564 | rdev->wiphy.wowlan.pattern_max_len)) | ||
565 | return -EINVAL; | ||
566 | } | ||
567 | |||
496 | /* check and set up bitrates */ | 568 | /* check and set up bitrates */ |
497 | ieee80211_set_bitrate_flags(wiphy); | 569 | ieee80211_set_bitrate_flags(wiphy); |
498 | 570 | ||
@@ -631,6 +703,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev) | |||
631 | mutex_destroy(&rdev->devlist_mtx); | 703 | mutex_destroy(&rdev->devlist_mtx); |
632 | list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) | 704 | list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) |
633 | cfg80211_put_bss(&scan->pub); | 705 | cfg80211_put_bss(&scan->pub); |
706 | cfg80211_rdev_free_wowlan(rdev); | ||
634 | kfree(rdev); | 707 | kfree(rdev); |
635 | } | 708 | } |
636 | 709 | ||
@@ -664,6 +737,11 @@ static void wdev_cleanup_work(struct work_struct *work) | |||
664 | ___cfg80211_scan_done(rdev, true); | 737 | ___cfg80211_scan_done(rdev, true); |
665 | } | 738 | } |
666 | 739 | ||
740 | if (WARN_ON(rdev->sched_scan_req && | ||
741 | rdev->sched_scan_req->dev == wdev->netdev)) { | ||
742 | __cfg80211_stop_sched_scan(rdev, false); | ||
743 | } | ||
744 | |||
667 | cfg80211_unlock_rdev(rdev); | 745 | cfg80211_unlock_rdev(rdev); |
668 | 746 | ||
669 | mutex_lock(&rdev->devlist_mtx); | 747 | mutex_lock(&rdev->devlist_mtx); |
@@ -685,6 +763,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
685 | struct net_device *dev = ndev; | 763 | struct net_device *dev = ndev; |
686 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 764 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
687 | struct cfg80211_registered_device *rdev; | 765 | struct cfg80211_registered_device *rdev; |
766 | int ret; | ||
688 | 767 | ||
689 | if (!wdev) | 768 | if (!wdev) |
690 | return NOTIFY_DONE; | 769 | return NOTIFY_DONE; |
@@ -751,6 +830,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
751 | break; | 830 | break; |
752 | case NL80211_IFTYPE_P2P_CLIENT: | 831 | case NL80211_IFTYPE_P2P_CLIENT: |
753 | case NL80211_IFTYPE_STATION: | 832 | case NL80211_IFTYPE_STATION: |
833 | cfg80211_lock_rdev(rdev); | ||
834 | __cfg80211_stop_sched_scan(rdev, false); | ||
835 | cfg80211_unlock_rdev(rdev); | ||
836 | |||
754 | wdev_lock(wdev); | 837 | wdev_lock(wdev); |
755 | #ifdef CONFIG_CFG80211_WEXT | 838 | #ifdef CONFIG_CFG80211_WEXT |
756 | kfree(wdev->wext.ie); | 839 | kfree(wdev->wext.ie); |
@@ -769,6 +852,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
769 | default: | 852 | default: |
770 | break; | 853 | break; |
771 | } | 854 | } |
855 | wdev->beacon_interval = 0; | ||
772 | break; | 856 | break; |
773 | case NETDEV_DOWN: | 857 | case NETDEV_DOWN: |
774 | dev_hold(dev); | 858 | dev_hold(dev); |
@@ -875,6 +959,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
875 | return notifier_from_errno(-EOPNOTSUPP); | 959 | return notifier_from_errno(-EOPNOTSUPP); |
876 | if (rfkill_blocked(rdev->rfkill)) | 960 | if (rfkill_blocked(rdev->rfkill)) |
877 | return notifier_from_errno(-ERFKILL); | 961 | return notifier_from_errno(-ERFKILL); |
962 | ret = cfg80211_can_add_interface(rdev, wdev->iftype); | ||
963 | if (ret) | ||
964 | return notifier_from_errno(ret); | ||
878 | break; | 965 | break; |
879 | } | 966 | } |
880 | 967 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index 26a0a084e16b..bf0fb40e3c8b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -60,8 +60,10 @@ 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; | ||
65 | 67 | ||
66 | #ifdef CONFIG_NL80211_TESTMODE | 68 | #ifdef CONFIG_NL80211_TESTMODE |
67 | struct genl_info *testmode_info; | 69 | struct genl_info *testmode_info; |
@@ -70,6 +72,8 @@ struct cfg80211_registered_device { | |||
70 | struct work_struct conn_work; | 72 | struct work_struct conn_work; |
71 | struct work_struct event_work; | 73 | struct work_struct event_work; |
72 | 74 | ||
75 | struct cfg80211_wowlan *wowlan; | ||
76 | |||
73 | /* must be last because of the way we do wiphy_priv(), | 77 | /* must be last because of the way we do wiphy_priv(), |
74 | * and it should at least be aligned to NETDEV_ALIGN */ | 78 | * and it should at least be aligned to NETDEV_ALIGN */ |
75 | struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); | 79 | struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); |
@@ -89,6 +93,18 @@ bool wiphy_idx_valid(int wiphy_idx) | |||
89 | return wiphy_idx >= 0; | 93 | return wiphy_idx >= 0; |
90 | } | 94 | } |
91 | 95 | ||
96 | static inline void | ||
97 | cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) | ||
98 | { | ||
99 | int i; | ||
100 | |||
101 | if (!rdev->wowlan) | ||
102 | return; | ||
103 | for (i = 0; i < rdev->wowlan->n_patterns; i++) | ||
104 | kfree(rdev->wowlan->patterns[i].mask); | ||
105 | kfree(rdev->wowlan->patterns); | ||
106 | kfree(rdev->wowlan); | ||
107 | } | ||
92 | 108 | ||
93 | extern struct workqueue_struct *cfg80211_wq; | 109 | extern struct workqueue_struct *cfg80211_wq; |
94 | extern struct mutex cfg80211_mutex; | 110 | extern struct mutex cfg80211_mutex; |
@@ -397,12 +413,26 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); | |||
397 | void cfg80211_sme_disassoc(struct net_device *dev, int idx); | 413 | void cfg80211_sme_disassoc(struct net_device *dev, int idx); |
398 | void __cfg80211_scan_done(struct work_struct *wk); | 414 | void __cfg80211_scan_done(struct work_struct *wk); |
399 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); | 415 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); |
416 | void __cfg80211_sched_scan_results(struct work_struct *wk); | ||
417 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | ||
418 | bool driver_initiated); | ||
400 | void cfg80211_upload_connect_keys(struct wireless_dev *wdev); | 419 | void cfg80211_upload_connect_keys(struct wireless_dev *wdev); |
401 | int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | 420 | int cfg80211_change_iface(struct cfg80211_registered_device *rdev, |
402 | struct net_device *dev, enum nl80211_iftype ntype, | 421 | struct net_device *dev, enum nl80211_iftype ntype, |
403 | u32 *flags, struct vif_params *params); | 422 | u32 *flags, struct vif_params *params); |
404 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | 423 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); |
405 | 424 | ||
425 | int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | ||
426 | struct wireless_dev *wdev, | ||
427 | enum nl80211_iftype iftype); | ||
428 | |||
429 | static inline int | ||
430 | cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, | ||
431 | enum nl80211_iftype iftype) | ||
432 | { | ||
433 | return cfg80211_can_change_interface(rdev, NULL, iftype); | ||
434 | } | ||
435 | |||
406 | struct ieee80211_channel * | 436 | struct ieee80211_channel * |
407 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | 437 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, |
408 | int freq, enum nl80211_channel_type channel_type); | 438 | int freq, enum nl80211_channel_type channel_type); |
@@ -412,6 +442,9 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev, | |||
412 | 442 | ||
413 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); | 443 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); |
414 | 444 | ||
445 | int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | ||
446 | u32 beacon_int); | ||
447 | |||
415 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS | 448 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS |
416 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) | 449 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) |
417 | #else | 450 | #else |
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c index e2e88878ba35..2f265e033ae2 100644 --- a/net/wireless/lib80211_crypt_wep.c +++ b/net/wireless/lib80211_crypt_wep.c | |||
@@ -96,13 +96,12 @@ static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, | |||
96 | u8 *key, int keylen, void *priv) | 96 | u8 *key, int keylen, void *priv) |
97 | { | 97 | { |
98 | struct lib80211_wep_data *wep = priv; | 98 | struct lib80211_wep_data *wep = priv; |
99 | u32 klen, len; | 99 | u32 klen; |
100 | u8 *pos; | 100 | u8 *pos; |
101 | 101 | ||
102 | if (skb_headroom(skb) < 4 || skb->len < hdr_len) | 102 | if (skb_headroom(skb) < 4 || skb->len < hdr_len) |
103 | return -1; | 103 | return -1; |
104 | 104 | ||
105 | len = skb->len - hdr_len; | ||
106 | pos = skb_push(skb, 4); | 105 | pos = skb_push(skb, 4); |
107 | memmove(pos, pos + 4, hdr_len); | 106 | memmove(pos, pos + 4, hdr_len); |
108 | pos += hdr_len; | 107 | pos += hdr_len; |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 16881fea4ce6..493b939970cd 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -963,6 +963,16 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | |||
963 | if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) | 963 | if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) |
964 | err = -EINVAL; | 964 | err = -EINVAL; |
965 | break; | 965 | break; |
966 | case NL80211_IFTYPE_MESH_POINT: | ||
967 | if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) { | ||
968 | err = -EINVAL; | ||
969 | break; | ||
970 | } | ||
971 | /* | ||
972 | * check for mesh DA must be done by driver as | ||
973 | * cfg80211 doesn't track the stations | ||
974 | */ | ||
975 | break; | ||
966 | default: | 976 | default: |
967 | err = -EOPNOTSUPP; | 977 | err = -EOPNOTSUPP; |
968 | break; | 978 | break; |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0efa7fd01150..2222ce08ee91 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -173,6 +173,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
173 | [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, | 173 | [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, |
174 | [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, | 174 | [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, |
175 | [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, | 175 | [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, |
176 | [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, | ||
177 | [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, | ||
178 | [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, | ||
176 | }; | 179 | }; |
177 | 180 | ||
178 | /* policy for the key attributes */ | 181 | /* policy for the key attributes */ |
@@ -194,6 +197,15 @@ nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { | |||
194 | [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, | 197 | [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, |
195 | }; | 198 | }; |
196 | 199 | ||
200 | /* policy for WoWLAN attributes */ | ||
201 | static const struct nla_policy | ||
202 | nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { | ||
203 | [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, | ||
204 | [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, | ||
205 | [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, | ||
206 | [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, | ||
207 | }; | ||
208 | |||
197 | /* ifidx get helper */ | 209 | /* ifidx get helper */ |
198 | static int nl80211_get_ifidx(struct netlink_callback *cb) | 210 | static int nl80211_get_ifidx(struct netlink_callback *cb) |
199 | { | 211 | { |
@@ -534,6 +546,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) | |||
534 | case NL80211_IFTYPE_AP: | 546 | case NL80211_IFTYPE_AP: |
535 | case NL80211_IFTYPE_AP_VLAN: | 547 | case NL80211_IFTYPE_AP_VLAN: |
536 | case NL80211_IFTYPE_P2P_GO: | 548 | case NL80211_IFTYPE_P2P_GO: |
549 | case NL80211_IFTYPE_MESH_POINT: | ||
537 | break; | 550 | break; |
538 | case NL80211_IFTYPE_ADHOC: | 551 | case NL80211_IFTYPE_ADHOC: |
539 | if (!wdev->current_bss) | 552 | if (!wdev->current_bss) |
@@ -551,6 +564,88 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) | |||
551 | return 0; | 564 | return 0; |
552 | } | 565 | } |
553 | 566 | ||
567 | static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) | ||
568 | { | ||
569 | struct nlattr *nl_modes = nla_nest_start(msg, attr); | ||
570 | int i; | ||
571 | |||
572 | if (!nl_modes) | ||
573 | goto nla_put_failure; | ||
574 | |||
575 | i = 0; | ||
576 | while (ifmodes) { | ||
577 | if (ifmodes & 1) | ||
578 | NLA_PUT_FLAG(msg, i); | ||
579 | ifmodes >>= 1; | ||
580 | i++; | ||
581 | } | ||
582 | |||
583 | nla_nest_end(msg, nl_modes); | ||
584 | return 0; | ||
585 | |||
586 | nla_put_failure: | ||
587 | return -ENOBUFS; | ||
588 | } | ||
589 | |||
590 | static int nl80211_put_iface_combinations(struct wiphy *wiphy, | ||
591 | struct sk_buff *msg) | ||
592 | { | ||
593 | struct nlattr *nl_combis; | ||
594 | int i, j; | ||
595 | |||
596 | nl_combis = nla_nest_start(msg, | ||
597 | NL80211_ATTR_INTERFACE_COMBINATIONS); | ||
598 | if (!nl_combis) | ||
599 | goto nla_put_failure; | ||
600 | |||
601 | for (i = 0; i < wiphy->n_iface_combinations; i++) { | ||
602 | const struct ieee80211_iface_combination *c; | ||
603 | struct nlattr *nl_combi, *nl_limits; | ||
604 | |||
605 | c = &wiphy->iface_combinations[i]; | ||
606 | |||
607 | nl_combi = nla_nest_start(msg, i + 1); | ||
608 | if (!nl_combi) | ||
609 | goto nla_put_failure; | ||
610 | |||
611 | nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); | ||
612 | if (!nl_limits) | ||
613 | goto nla_put_failure; | ||
614 | |||
615 | for (j = 0; j < c->n_limits; j++) { | ||
616 | struct nlattr *nl_limit; | ||
617 | |||
618 | nl_limit = nla_nest_start(msg, j + 1); | ||
619 | if (!nl_limit) | ||
620 | goto nla_put_failure; | ||
621 | NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX, | ||
622 | c->limits[j].max); | ||
623 | if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, | ||
624 | c->limits[j].types)) | ||
625 | goto nla_put_failure; | ||
626 | nla_nest_end(msg, nl_limit); | ||
627 | } | ||
628 | |||
629 | nla_nest_end(msg, nl_limits); | ||
630 | |||
631 | if (c->beacon_int_infra_match) | ||
632 | NLA_PUT_FLAG(msg, | ||
633 | NL80211_IFACE_COMB_STA_AP_BI_MATCH); | ||
634 | NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, | ||
635 | c->num_different_channels); | ||
636 | NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM, | ||
637 | c->max_interfaces); | ||
638 | |||
639 | nla_nest_end(msg, nl_combi); | ||
640 | } | ||
641 | |||
642 | nla_nest_end(msg, nl_combis); | ||
643 | |||
644 | return 0; | ||
645 | nla_put_failure: | ||
646 | return -ENOBUFS; | ||
647 | } | ||
648 | |||
554 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | 649 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, |
555 | struct cfg80211_registered_device *dev) | 650 | struct cfg80211_registered_device *dev) |
556 | { | 651 | { |
@@ -558,13 +653,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
558 | struct nlattr *nl_bands, *nl_band; | 653 | struct nlattr *nl_bands, *nl_band; |
559 | struct nlattr *nl_freqs, *nl_freq; | 654 | struct nlattr *nl_freqs, *nl_freq; |
560 | struct nlattr *nl_rates, *nl_rate; | 655 | struct nlattr *nl_rates, *nl_rate; |
561 | struct nlattr *nl_modes; | ||
562 | struct nlattr *nl_cmds; | 656 | struct nlattr *nl_cmds; |
563 | enum ieee80211_band band; | 657 | enum ieee80211_band band; |
564 | struct ieee80211_channel *chan; | 658 | struct ieee80211_channel *chan; |
565 | struct ieee80211_rate *rate; | 659 | struct ieee80211_rate *rate; |
566 | int i; | 660 | int i; |
567 | u16 ifmodes = dev->wiphy.interface_modes; | ||
568 | const struct ieee80211_txrx_stypes *mgmt_stypes = | 661 | const struct ieee80211_txrx_stypes *mgmt_stypes = |
569 | dev->wiphy.mgmt_stypes; | 662 | dev->wiphy.mgmt_stypes; |
570 | 663 | ||
@@ -624,20 +717,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
624 | } | 717 | } |
625 | } | 718 | } |
626 | 719 | ||
627 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); | 720 | if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, |
628 | if (!nl_modes) | 721 | dev->wiphy.interface_modes)) |
629 | goto nla_put_failure; | 722 | goto nla_put_failure; |
630 | 723 | ||
631 | i = 0; | ||
632 | while (ifmodes) { | ||
633 | if (ifmodes & 1) | ||
634 | NLA_PUT_FLAG(msg, i); | ||
635 | ifmodes >>= 1; | ||
636 | i++; | ||
637 | } | ||
638 | |||
639 | nla_nest_end(msg, nl_modes); | ||
640 | |||
641 | nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); | 724 | nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); |
642 | if (!nl_bands) | 725 | if (!nl_bands) |
643 | goto nla_put_failure; | 726 | goto nla_put_failure; |
@@ -749,6 +832,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
749 | } | 832 | } |
750 | CMD(set_channel, SET_CHANNEL); | 833 | CMD(set_channel, SET_CHANNEL); |
751 | CMD(set_wds_peer, SET_WDS_PEER); | 834 | CMD(set_wds_peer, SET_WDS_PEER); |
835 | if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) | ||
836 | CMD(sched_scan_start, START_SCHED_SCAN); | ||
752 | 837 | ||
753 | #undef CMD | 838 | #undef CMD |
754 | 839 | ||
@@ -821,6 +906,42 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
821 | nla_nest_end(msg, nl_ifs); | 906 | nla_nest_end(msg, nl_ifs); |
822 | } | 907 | } |
823 | 908 | ||
909 | if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { | ||
910 | struct nlattr *nl_wowlan; | ||
911 | |||
912 | nl_wowlan = nla_nest_start(msg, | ||
913 | NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); | ||
914 | if (!nl_wowlan) | ||
915 | goto nla_put_failure; | ||
916 | |||
917 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) | ||
918 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); | ||
919 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) | ||
920 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); | ||
921 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) | ||
922 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); | ||
923 | if (dev->wiphy.wowlan.n_patterns) { | ||
924 | struct nl80211_wowlan_pattern_support pat = { | ||
925 | .max_patterns = dev->wiphy.wowlan.n_patterns, | ||
926 | .min_pattern_len = | ||
927 | dev->wiphy.wowlan.pattern_min_len, | ||
928 | .max_pattern_len = | ||
929 | dev->wiphy.wowlan.pattern_max_len, | ||
930 | }; | ||
931 | NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, | ||
932 | sizeof(pat), &pat); | ||
933 | } | ||
934 | |||
935 | nla_nest_end(msg, nl_wowlan); | ||
936 | } | ||
937 | |||
938 | if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, | ||
939 | dev->wiphy.software_iftypes)) | ||
940 | goto nla_put_failure; | ||
941 | |||
942 | if (nl80211_put_iface_combinations(&dev->wiphy, msg)) | ||
943 | goto nla_put_failure; | ||
944 | |||
824 | return genlmsg_end(msg, hdr); | 945 | return genlmsg_end(msg, hdr); |
825 | 946 | ||
826 | nla_put_failure: | 947 | nla_put_failure: |
@@ -1682,14 +1803,6 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
1682 | if (err) | 1803 | if (err) |
1683 | goto out; | 1804 | goto out; |
1684 | 1805 | ||
1685 | if (!(rdev->wiphy.flags & | ||
1686 | WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS)) { | ||
1687 | if (!key.def_uni || !key.def_multi) { | ||
1688 | err = -EOPNOTSUPP; | ||
1689 | goto out; | ||
1690 | } | ||
1691 | } | ||
1692 | |||
1693 | err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, | 1806 | err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, |
1694 | key.def_uni, key.def_multi); | 1807 | key.def_uni, key.def_multi); |
1695 | 1808 | ||
@@ -1840,8 +1953,9 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) | |||
1840 | struct beacon_parameters *info); | 1953 | struct beacon_parameters *info); |
1841 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 1954 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
1842 | struct net_device *dev = info->user_ptr[1]; | 1955 | struct net_device *dev = info->user_ptr[1]; |
1956 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1843 | struct beacon_parameters params; | 1957 | struct beacon_parameters params; |
1844 | int haveinfo = 0; | 1958 | int haveinfo = 0, err; |
1845 | 1959 | ||
1846 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL])) | 1960 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL])) |
1847 | return -EINVAL; | 1961 | return -EINVAL; |
@@ -1850,6 +1964,8 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) | |||
1850 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 1964 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
1851 | return -EOPNOTSUPP; | 1965 | return -EOPNOTSUPP; |
1852 | 1966 | ||
1967 | memset(¶ms, 0, sizeof(params)); | ||
1968 | |||
1853 | switch (info->genlhdr->cmd) { | 1969 | switch (info->genlhdr->cmd) { |
1854 | case NL80211_CMD_NEW_BEACON: | 1970 | case NL80211_CMD_NEW_BEACON: |
1855 | /* these are required for NEW_BEACON */ | 1971 | /* these are required for NEW_BEACON */ |
@@ -1858,6 +1974,15 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) | |||
1858 | !info->attrs[NL80211_ATTR_BEACON_HEAD]) | 1974 | !info->attrs[NL80211_ATTR_BEACON_HEAD]) |
1859 | return -EINVAL; | 1975 | return -EINVAL; |
1860 | 1976 | ||
1977 | params.interval = | ||
1978 | nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); | ||
1979 | params.dtim_period = | ||
1980 | nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); | ||
1981 | |||
1982 | err = cfg80211_validate_beacon_int(rdev, params.interval); | ||
1983 | if (err) | ||
1984 | return err; | ||
1985 | |||
1861 | call = rdev->ops->add_beacon; | 1986 | call = rdev->ops->add_beacon; |
1862 | break; | 1987 | break; |
1863 | case NL80211_CMD_SET_BEACON: | 1988 | case NL80211_CMD_SET_BEACON: |
@@ -1871,20 +1996,6 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) | |||
1871 | if (!call) | 1996 | if (!call) |
1872 | return -EOPNOTSUPP; | 1997 | return -EOPNOTSUPP; |
1873 | 1998 | ||
1874 | memset(¶ms, 0, sizeof(params)); | ||
1875 | |||
1876 | if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { | ||
1877 | params.interval = | ||
1878 | nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); | ||
1879 | haveinfo = 1; | ||
1880 | } | ||
1881 | |||
1882 | if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { | ||
1883 | params.dtim_period = | ||
1884 | nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); | ||
1885 | haveinfo = 1; | ||
1886 | } | ||
1887 | |||
1888 | if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { | 1999 | if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { |
1889 | params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); | 2000 | params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); |
1890 | params.head_len = | 2001 | params.head_len = |
@@ -1902,13 +2013,18 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) | |||
1902 | if (!haveinfo) | 2013 | if (!haveinfo) |
1903 | return -EINVAL; | 2014 | return -EINVAL; |
1904 | 2015 | ||
1905 | return call(&rdev->wiphy, dev, ¶ms); | 2016 | err = call(&rdev->wiphy, dev, ¶ms); |
2017 | if (!err && params.interval) | ||
2018 | wdev->beacon_interval = params.interval; | ||
2019 | return err; | ||
1906 | } | 2020 | } |
1907 | 2021 | ||
1908 | static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) | 2022 | static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) |
1909 | { | 2023 | { |
1910 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 2024 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
1911 | struct net_device *dev = info->user_ptr[1]; | 2025 | struct net_device *dev = info->user_ptr[1]; |
2026 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
2027 | int err; | ||
1912 | 2028 | ||
1913 | if (!rdev->ops->del_beacon) | 2029 | if (!rdev->ops->del_beacon) |
1914 | return -EOPNOTSUPP; | 2030 | return -EOPNOTSUPP; |
@@ -1917,7 +2033,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) | |||
1917 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 2033 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
1918 | return -EOPNOTSUPP; | 2034 | return -EOPNOTSUPP; |
1919 | 2035 | ||
1920 | return rdev->ops->del_beacon(&rdev->wiphy, dev); | 2036 | err = rdev->ops->del_beacon(&rdev->wiphy, dev); |
2037 | if (!err) | ||
2038 | wdev->beacon_interval = 0; | ||
2039 | return err; | ||
1921 | } | 2040 | } |
1922 | 2041 | ||
1923 | static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { | 2042 | static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { |
@@ -2216,6 +2335,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
2216 | memset(¶ms, 0, sizeof(params)); | 2335 | memset(¶ms, 0, sizeof(params)); |
2217 | 2336 | ||
2218 | params.listen_interval = -1; | 2337 | params.listen_interval = -1; |
2338 | params.plink_state = -1; | ||
2219 | 2339 | ||
2220 | if (info->attrs[NL80211_ATTR_STA_AID]) | 2340 | if (info->attrs[NL80211_ATTR_STA_AID]) |
2221 | return -EINVAL; | 2341 | return -EINVAL; |
@@ -2247,6 +2367,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
2247 | params.plink_action = | 2367 | params.plink_action = |
2248 | nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); | 2368 | nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); |
2249 | 2369 | ||
2370 | if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) | ||
2371 | params.plink_state = | ||
2372 | nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); | ||
2373 | |||
2250 | err = get_vlan(info, rdev, ¶ms.vlan); | 2374 | err = get_vlan(info, rdev, ¶ms.vlan); |
2251 | if (err) | 2375 | if (err) |
2252 | goto out; | 2376 | goto out; |
@@ -2286,10 +2410,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
2286 | err = -EINVAL; | 2410 | err = -EINVAL; |
2287 | if (params.listen_interval >= 0) | 2411 | if (params.listen_interval >= 0) |
2288 | err = -EINVAL; | 2412 | err = -EINVAL; |
2289 | if (params.supported_rates) | ||
2290 | err = -EINVAL; | ||
2291 | if (params.sta_flags_mask & | 2413 | if (params.sta_flags_mask & |
2292 | ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | | 2414 | ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | |
2415 | BIT(NL80211_STA_FLAG_MFP) | | ||
2293 | BIT(NL80211_STA_FLAG_AUTHORIZED))) | 2416 | BIT(NL80211_STA_FLAG_AUTHORIZED))) |
2294 | err = -EINVAL; | 2417 | err = -EINVAL; |
2295 | break; | 2418 | break; |
@@ -2840,6 +2963,7 @@ static const struct nla_policy | |||
2840 | [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, | 2963 | [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, |
2841 | [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, | 2964 | [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, |
2842 | .len = IEEE80211_MAX_DATA_LEN }, | 2965 | .len = IEEE80211_MAX_DATA_LEN }, |
2966 | [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, | ||
2843 | }; | 2967 | }; |
2844 | 2968 | ||
2845 | static int nl80211_parse_mesh_config(struct genl_info *info, | 2969 | static int nl80211_parse_mesh_config(struct genl_info *info, |
@@ -2949,7 +3073,8 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, | |||
2949 | setup->ie = nla_data(ieattr); | 3073 | setup->ie = nla_data(ieattr); |
2950 | setup->ie_len = nla_len(ieattr); | 3074 | setup->ie_len = nla_len(ieattr); |
2951 | } | 3075 | } |
2952 | setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); | 3076 | setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); |
3077 | setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); | ||
2953 | 3078 | ||
2954 | return 0; | 3079 | return 0; |
2955 | } | 3080 | } |
@@ -3318,6 +3443,188 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3318 | return err; | 3443 | return err; |
3319 | } | 3444 | } |
3320 | 3445 | ||
3446 | static int nl80211_start_sched_scan(struct sk_buff *skb, | ||
3447 | struct genl_info *info) | ||
3448 | { | ||
3449 | struct cfg80211_sched_scan_request *request; | ||
3450 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
3451 | struct net_device *dev = info->user_ptr[1]; | ||
3452 | struct cfg80211_ssid *ssid; | ||
3453 | struct ieee80211_channel *channel; | ||
3454 | struct nlattr *attr; | ||
3455 | struct wiphy *wiphy; | ||
3456 | int err, tmp, n_ssids = 0, n_channels, i; | ||
3457 | u32 interval; | ||
3458 | enum ieee80211_band band; | ||
3459 | size_t ie_len; | ||
3460 | |||
3461 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || | ||
3462 | !rdev->ops->sched_scan_start) | ||
3463 | return -EOPNOTSUPP; | ||
3464 | |||
3465 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | ||
3466 | return -EINVAL; | ||
3467 | |||
3468 | if (rdev->sched_scan_req) | ||
3469 | return -EINPROGRESS; | ||
3470 | |||
3471 | if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) | ||
3472 | return -EINVAL; | ||
3473 | |||
3474 | interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); | ||
3475 | if (interval == 0) | ||
3476 | return -EINVAL; | ||
3477 | |||
3478 | wiphy = &rdev->wiphy; | ||
3479 | |||
3480 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | ||
3481 | n_channels = validate_scan_freqs( | ||
3482 | info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); | ||
3483 | if (!n_channels) | ||
3484 | return -EINVAL; | ||
3485 | } else { | ||
3486 | n_channels = 0; | ||
3487 | |||
3488 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | ||
3489 | if (wiphy->bands[band]) | ||
3490 | n_channels += wiphy->bands[band]->n_channels; | ||
3491 | } | ||
3492 | |||
3493 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) | ||
3494 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], | ||
3495 | tmp) | ||
3496 | n_ssids++; | ||
3497 | |||
3498 | if (n_ssids > wiphy->max_scan_ssids) | ||
3499 | return -EINVAL; | ||
3500 | |||
3501 | if (info->attrs[NL80211_ATTR_IE]) | ||
3502 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
3503 | else | ||
3504 | ie_len = 0; | ||
3505 | |||
3506 | if (ie_len > wiphy->max_scan_ie_len) | ||
3507 | return -EINVAL; | ||
3508 | |||
3509 | request = kzalloc(sizeof(*request) | ||
3510 | + sizeof(*ssid) * n_ssids | ||
3511 | + sizeof(channel) * n_channels | ||
3512 | + ie_len, GFP_KERNEL); | ||
3513 | if (!request) | ||
3514 | return -ENOMEM; | ||
3515 | |||
3516 | if (n_ssids) | ||
3517 | request->ssids = (void *)&request->channels[n_channels]; | ||
3518 | request->n_ssids = n_ssids; | ||
3519 | if (ie_len) { | ||
3520 | if (request->ssids) | ||
3521 | request->ie = (void *)(request->ssids + n_ssids); | ||
3522 | else | ||
3523 | request->ie = (void *)(request->channels + n_channels); | ||
3524 | } | ||
3525 | |||
3526 | i = 0; | ||
3527 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | ||
3528 | /* user specified, bail out if channel not found */ | ||
3529 | nla_for_each_nested(attr, | ||
3530 | info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], | ||
3531 | tmp) { | ||
3532 | struct ieee80211_channel *chan; | ||
3533 | |||
3534 | chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | ||
3535 | |||
3536 | if (!chan) { | ||
3537 | err = -EINVAL; | ||
3538 | goto out_free; | ||
3539 | } | ||
3540 | |||
3541 | /* ignore disabled channels */ | ||
3542 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3543 | continue; | ||
3544 | |||
3545 | request->channels[i] = chan; | ||
3546 | i++; | ||
3547 | } | ||
3548 | } else { | ||
3549 | /* all channels */ | ||
3550 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
3551 | int j; | ||
3552 | if (!wiphy->bands[band]) | ||
3553 | continue; | ||
3554 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | ||
3555 | struct ieee80211_channel *chan; | ||
3556 | |||
3557 | chan = &wiphy->bands[band]->channels[j]; | ||
3558 | |||
3559 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3560 | continue; | ||
3561 | |||
3562 | request->channels[i] = chan; | ||
3563 | i++; | ||
3564 | } | ||
3565 | } | ||
3566 | } | ||
3567 | |||
3568 | if (!i) { | ||
3569 | err = -EINVAL; | ||
3570 | goto out_free; | ||
3571 | } | ||
3572 | |||
3573 | request->n_channels = i; | ||
3574 | |||
3575 | i = 0; | ||
3576 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { | ||
3577 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], | ||
3578 | tmp) { | ||
3579 | if (request->ssids[i].ssid_len > | ||
3580 | IEEE80211_MAX_SSID_LEN) { | ||
3581 | err = -EINVAL; | ||
3582 | goto out_free; | ||
3583 | } | ||
3584 | memcpy(request->ssids[i].ssid, nla_data(attr), | ||
3585 | nla_len(attr)); | ||
3586 | request->ssids[i].ssid_len = nla_len(attr); | ||
3587 | i++; | ||
3588 | } | ||
3589 | } | ||
3590 | |||
3591 | if (info->attrs[NL80211_ATTR_IE]) { | ||
3592 | request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
3593 | memcpy((void *)request->ie, | ||
3594 | nla_data(info->attrs[NL80211_ATTR_IE]), | ||
3595 | request->ie_len); | ||
3596 | } | ||
3597 | |||
3598 | request->dev = dev; | ||
3599 | request->wiphy = &rdev->wiphy; | ||
3600 | request->interval = interval; | ||
3601 | |||
3602 | err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); | ||
3603 | if (!err) { | ||
3604 | rdev->sched_scan_req = request; | ||
3605 | nl80211_send_sched_scan(rdev, dev, | ||
3606 | NL80211_CMD_START_SCHED_SCAN); | ||
3607 | goto out; | ||
3608 | } | ||
3609 | |||
3610 | out_free: | ||
3611 | kfree(request); | ||
3612 | out: | ||
3613 | return err; | ||
3614 | } | ||
3615 | |||
3616 | static int nl80211_stop_sched_scan(struct sk_buff *skb, | ||
3617 | struct genl_info *info) | ||
3618 | { | ||
3619 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
3620 | |||
3621 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || | ||
3622 | !rdev->ops->sched_scan_stop) | ||
3623 | return -EOPNOTSUPP; | ||
3624 | |||
3625 | return __cfg80211_stop_sched_scan(rdev, false); | ||
3626 | } | ||
3627 | |||
3321 | static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | 3628 | static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, |
3322 | struct cfg80211_registered_device *rdev, | 3629 | struct cfg80211_registered_device *rdev, |
3323 | struct wireless_dev *wdev, | 3630 | struct wireless_dev *wdev, |
@@ -4816,6 +5123,194 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) | |||
4816 | return cfg80211_leave_mesh(rdev, dev); | 5123 | return cfg80211_leave_mesh(rdev, dev); |
4817 | } | 5124 | } |
4818 | 5125 | ||
5126 | static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) | ||
5127 | { | ||
5128 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
5129 | struct sk_buff *msg; | ||
5130 | void *hdr; | ||
5131 | |||
5132 | if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) | ||
5133 | return -EOPNOTSUPP; | ||
5134 | |||
5135 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
5136 | if (!msg) | ||
5137 | return -ENOMEM; | ||
5138 | |||
5139 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
5140 | NL80211_CMD_GET_WOWLAN); | ||
5141 | if (!hdr) | ||
5142 | goto nla_put_failure; | ||
5143 | |||
5144 | if (rdev->wowlan) { | ||
5145 | struct nlattr *nl_wowlan; | ||
5146 | |||
5147 | nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); | ||
5148 | if (!nl_wowlan) | ||
5149 | goto nla_put_failure; | ||
5150 | |||
5151 | if (rdev->wowlan->any) | ||
5152 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); | ||
5153 | if (rdev->wowlan->disconnect) | ||
5154 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); | ||
5155 | if (rdev->wowlan->magic_pkt) | ||
5156 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); | ||
5157 | if (rdev->wowlan->n_patterns) { | ||
5158 | struct nlattr *nl_pats, *nl_pat; | ||
5159 | int i, pat_len; | ||
5160 | |||
5161 | nl_pats = nla_nest_start(msg, | ||
5162 | NL80211_WOWLAN_TRIG_PKT_PATTERN); | ||
5163 | if (!nl_pats) | ||
5164 | goto nla_put_failure; | ||
5165 | |||
5166 | for (i = 0; i < rdev->wowlan->n_patterns; i++) { | ||
5167 | nl_pat = nla_nest_start(msg, i + 1); | ||
5168 | if (!nl_pat) | ||
5169 | goto nla_put_failure; | ||
5170 | pat_len = rdev->wowlan->patterns[i].pattern_len; | ||
5171 | NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK, | ||
5172 | DIV_ROUND_UP(pat_len, 8), | ||
5173 | rdev->wowlan->patterns[i].mask); | ||
5174 | NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN, | ||
5175 | pat_len, | ||
5176 | rdev->wowlan->patterns[i].pattern); | ||
5177 | nla_nest_end(msg, nl_pat); | ||
5178 | } | ||
5179 | nla_nest_end(msg, nl_pats); | ||
5180 | } | ||
5181 | |||
5182 | nla_nest_end(msg, nl_wowlan); | ||
5183 | } | ||
5184 | |||
5185 | genlmsg_end(msg, hdr); | ||
5186 | return genlmsg_reply(msg, info); | ||
5187 | |||
5188 | nla_put_failure: | ||
5189 | nlmsg_free(msg); | ||
5190 | return -ENOBUFS; | ||
5191 | } | ||
5192 | |||
5193 | static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | ||
5194 | { | ||
5195 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
5196 | struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; | ||
5197 | struct cfg80211_wowlan no_triggers = {}; | ||
5198 | struct cfg80211_wowlan new_triggers = {}; | ||
5199 | struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; | ||
5200 | int err, i; | ||
5201 | |||
5202 | if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) | ||
5203 | return -EOPNOTSUPP; | ||
5204 | |||
5205 | if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) | ||
5206 | goto no_triggers; | ||
5207 | |||
5208 | err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, | ||
5209 | nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), | ||
5210 | nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), | ||
5211 | nl80211_wowlan_policy); | ||
5212 | if (err) | ||
5213 | return err; | ||
5214 | |||
5215 | if (tb[NL80211_WOWLAN_TRIG_ANY]) { | ||
5216 | if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) | ||
5217 | return -EINVAL; | ||
5218 | new_triggers.any = true; | ||
5219 | } | ||
5220 | |||
5221 | if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { | ||
5222 | if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) | ||
5223 | return -EINVAL; | ||
5224 | new_triggers.disconnect = true; | ||
5225 | } | ||
5226 | |||
5227 | if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { | ||
5228 | if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) | ||
5229 | return -EINVAL; | ||
5230 | new_triggers.magic_pkt = true; | ||
5231 | } | ||
5232 | |||
5233 | if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { | ||
5234 | struct nlattr *pat; | ||
5235 | int n_patterns = 0; | ||
5236 | int rem, pat_len, mask_len; | ||
5237 | struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; | ||
5238 | |||
5239 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], | ||
5240 | rem) | ||
5241 | n_patterns++; | ||
5242 | if (n_patterns > wowlan->n_patterns) | ||
5243 | return -EINVAL; | ||
5244 | |||
5245 | new_triggers.patterns = kcalloc(n_patterns, | ||
5246 | sizeof(new_triggers.patterns[0]), | ||
5247 | GFP_KERNEL); | ||
5248 | if (!new_triggers.patterns) | ||
5249 | return -ENOMEM; | ||
5250 | |||
5251 | new_triggers.n_patterns = n_patterns; | ||
5252 | i = 0; | ||
5253 | |||
5254 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], | ||
5255 | rem) { | ||
5256 | nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, | ||
5257 | nla_data(pat), nla_len(pat), NULL); | ||
5258 | err = -EINVAL; | ||
5259 | if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || | ||
5260 | !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) | ||
5261 | goto error; | ||
5262 | pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); | ||
5263 | mask_len = DIV_ROUND_UP(pat_len, 8); | ||
5264 | if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != | ||
5265 | mask_len) | ||
5266 | goto error; | ||
5267 | if (pat_len > wowlan->pattern_max_len || | ||
5268 | pat_len < wowlan->pattern_min_len) | ||
5269 | goto error; | ||
5270 | |||
5271 | new_triggers.patterns[i].mask = | ||
5272 | kmalloc(mask_len + pat_len, GFP_KERNEL); | ||
5273 | if (!new_triggers.patterns[i].mask) { | ||
5274 | err = -ENOMEM; | ||
5275 | goto error; | ||
5276 | } | ||
5277 | new_triggers.patterns[i].pattern = | ||
5278 | new_triggers.patterns[i].mask + mask_len; | ||
5279 | memcpy(new_triggers.patterns[i].mask, | ||
5280 | nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), | ||
5281 | mask_len); | ||
5282 | new_triggers.patterns[i].pattern_len = pat_len; | ||
5283 | memcpy(new_triggers.patterns[i].pattern, | ||
5284 | nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), | ||
5285 | pat_len); | ||
5286 | i++; | ||
5287 | } | ||
5288 | } | ||
5289 | |||
5290 | if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { | ||
5291 | struct cfg80211_wowlan *ntrig; | ||
5292 | ntrig = kmemdup(&new_triggers, sizeof(new_triggers), | ||
5293 | GFP_KERNEL); | ||
5294 | if (!ntrig) { | ||
5295 | err = -ENOMEM; | ||
5296 | goto error; | ||
5297 | } | ||
5298 | cfg80211_rdev_free_wowlan(rdev); | ||
5299 | rdev->wowlan = ntrig; | ||
5300 | } else { | ||
5301 | no_triggers: | ||
5302 | cfg80211_rdev_free_wowlan(rdev); | ||
5303 | rdev->wowlan = NULL; | ||
5304 | } | ||
5305 | |||
5306 | return 0; | ||
5307 | error: | ||
5308 | for (i = 0; i < new_triggers.n_patterns; i++) | ||
5309 | kfree(new_triggers.patterns[i].mask); | ||
5310 | kfree(new_triggers.patterns); | ||
5311 | return err; | ||
5312 | } | ||
5313 | |||
4819 | #define NL80211_FLAG_NEED_WIPHY 0x01 | 5314 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
4820 | #define NL80211_FLAG_NEED_NETDEV 0x02 | 5315 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
4821 | #define NL80211_FLAG_NEED_RTNL 0x04 | 5316 | #define NL80211_FLAG_NEED_RTNL 0x04 |
@@ -5100,6 +5595,22 @@ static struct genl_ops nl80211_ops[] = { | |||
5100 | .dumpit = nl80211_dump_scan, | 5595 | .dumpit = nl80211_dump_scan, |
5101 | }, | 5596 | }, |
5102 | { | 5597 | { |
5598 | .cmd = NL80211_CMD_START_SCHED_SCAN, | ||
5599 | .doit = nl80211_start_sched_scan, | ||
5600 | .policy = nl80211_policy, | ||
5601 | .flags = GENL_ADMIN_PERM, | ||
5602 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
5603 | NL80211_FLAG_NEED_RTNL, | ||
5604 | }, | ||
5605 | { | ||
5606 | .cmd = NL80211_CMD_STOP_SCHED_SCAN, | ||
5607 | .doit = nl80211_stop_sched_scan, | ||
5608 | .policy = nl80211_policy, | ||
5609 | .flags = GENL_ADMIN_PERM, | ||
5610 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
5611 | NL80211_FLAG_NEED_RTNL, | ||
5612 | }, | ||
5613 | { | ||
5103 | .cmd = NL80211_CMD_AUTHENTICATE, | 5614 | .cmd = NL80211_CMD_AUTHENTICATE, |
5104 | .doit = nl80211_authenticate, | 5615 | .doit = nl80211_authenticate, |
5105 | .policy = nl80211_policy, | 5616 | .policy = nl80211_policy, |
@@ -5314,6 +5825,22 @@ static struct genl_ops nl80211_ops[] = { | |||
5314 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 5825 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
5315 | NL80211_FLAG_NEED_RTNL, | 5826 | NL80211_FLAG_NEED_RTNL, |
5316 | }, | 5827 | }, |
5828 | { | ||
5829 | .cmd = NL80211_CMD_GET_WOWLAN, | ||
5830 | .doit = nl80211_get_wowlan, | ||
5831 | .policy = nl80211_policy, | ||
5832 | /* can be retrieved by unprivileged users */ | ||
5833 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | ||
5834 | NL80211_FLAG_NEED_RTNL, | ||
5835 | }, | ||
5836 | { | ||
5837 | .cmd = NL80211_CMD_SET_WOWLAN, | ||
5838 | .doit = nl80211_set_wowlan, | ||
5839 | .policy = nl80211_policy, | ||
5840 | .flags = GENL_ADMIN_PERM, | ||
5841 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | ||
5842 | NL80211_FLAG_NEED_RTNL, | ||
5843 | }, | ||
5317 | }; | 5844 | }; |
5318 | 5845 | ||
5319 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5846 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
@@ -5409,6 +5936,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg, | |||
5409 | return -EMSGSIZE; | 5936 | return -EMSGSIZE; |
5410 | } | 5937 | } |
5411 | 5938 | ||
5939 | static int | ||
5940 | nl80211_send_sched_scan_msg(struct sk_buff *msg, | ||
5941 | struct cfg80211_registered_device *rdev, | ||
5942 | struct net_device *netdev, | ||
5943 | u32 pid, u32 seq, int flags, u32 cmd) | ||
5944 | { | ||
5945 | void *hdr; | ||
5946 | |||
5947 | hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); | ||
5948 | if (!hdr) | ||
5949 | return -1; | ||
5950 | |||
5951 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5952 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5953 | |||
5954 | return genlmsg_end(msg, hdr); | ||
5955 | |||
5956 | nla_put_failure: | ||
5957 | genlmsg_cancel(msg, hdr); | ||
5958 | return -EMSGSIZE; | ||
5959 | } | ||
5960 | |||
5412 | void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, | 5961 | void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, |
5413 | struct net_device *netdev) | 5962 | struct net_device *netdev) |
5414 | { | 5963 | { |
@@ -5466,6 +6015,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, | |||
5466 | nl80211_scan_mcgrp.id, GFP_KERNEL); | 6015 | nl80211_scan_mcgrp.id, GFP_KERNEL); |
5467 | } | 6016 | } |
5468 | 6017 | ||
6018 | void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, | ||
6019 | struct net_device *netdev) | ||
6020 | { | ||
6021 | struct sk_buff *msg; | ||
6022 | |||
6023 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
6024 | if (!msg) | ||
6025 | return; | ||
6026 | |||
6027 | if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, | ||
6028 | NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { | ||
6029 | nlmsg_free(msg); | ||
6030 | return; | ||
6031 | } | ||
6032 | |||
6033 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
6034 | nl80211_scan_mcgrp.id, GFP_KERNEL); | ||
6035 | } | ||
6036 | |||
6037 | void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, | ||
6038 | struct net_device *netdev, u32 cmd) | ||
6039 | { | ||
6040 | struct sk_buff *msg; | ||
6041 | |||
6042 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
6043 | if (!msg) | ||
6044 | return; | ||
6045 | |||
6046 | if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { | ||
6047 | nlmsg_free(msg); | ||
6048 | return; | ||
6049 | } | ||
6050 | |||
6051 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
6052 | nl80211_scan_mcgrp.id, GFP_KERNEL); | ||
6053 | } | ||
6054 | |||
5469 | /* | 6055 | /* |
5470 | * This can happen on global regulatory changes or device specific settings | 6056 | * This can happen on global regulatory changes or device specific settings |
5471 | * based on custom world regulatory domains. | 6057 | * 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); |
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/reg.c b/net/wireless/reg.c index 1613080a96b9..1ad0f39fe091 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -672,11 +672,9 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
672 | for (i = 0; i < regd->n_reg_rules; i++) { | 672 | for (i = 0; i < regd->n_reg_rules; i++) { |
673 | const struct ieee80211_reg_rule *rr; | 673 | const struct ieee80211_reg_rule *rr; |
674 | const struct ieee80211_freq_range *fr = NULL; | 674 | const struct ieee80211_freq_range *fr = NULL; |
675 | const struct ieee80211_power_rule *pr = NULL; | ||
676 | 675 | ||
677 | rr = ®d->reg_rules[i]; | 676 | rr = ®d->reg_rules[i]; |
678 | fr = &rr->freq_range; | 677 | fr = &rr->freq_range; |
679 | pr = &rr->power_rule; | ||
680 | 678 | ||
681 | /* | 679 | /* |
682 | * We only need to know if one frequency rule was | 680 | * We only need to know if one frequency rule was |
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index fbf6f33ae4d0..73a441d237b5 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -93,6 +93,69 @@ 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 wiphy *wiphy) | ||
123 | { | ||
124 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
125 | |||
126 | cfg80211_lock_rdev(rdev); | ||
127 | __cfg80211_stop_sched_scan(rdev, true); | ||
128 | cfg80211_unlock_rdev(rdev); | ||
129 | } | ||
130 | EXPORT_SYMBOL(cfg80211_sched_scan_stopped); | ||
131 | |||
132 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | ||
133 | bool driver_initiated) | ||
134 | { | ||
135 | int err; | ||
136 | struct net_device *dev; | ||
137 | |||
138 | ASSERT_RDEV_LOCK(rdev); | ||
139 | |||
140 | if (!rdev->sched_scan_req) | ||
141 | return 0; | ||
142 | |||
143 | dev = rdev->sched_scan_req->dev; | ||
144 | |||
145 | if (!driver_initiated) { | ||
146 | err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); | ||
147 | if (err) | ||
148 | return err; | ||
149 | } | ||
150 | |||
151 | nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); | ||
152 | |||
153 | kfree(rdev->sched_scan_req); | ||
154 | rdev->sched_scan_req = NULL; | ||
155 | |||
156 | return err; | ||
157 | } | ||
158 | |||
96 | static void bss_release(struct kref *ref) | 159 | static void bss_release(struct kref *ref) |
97 | { | 160 | { |
98 | struct cfg80211_internal_bss *bss; | 161 | struct cfg80211_internal_bss *bss; |
@@ -210,7 +273,7 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
210 | { | 273 | { |
211 | const u8 *ie; | 274 | const u8 *ie; |
212 | 275 | ||
213 | if (!is_zero_ether_addr(a->bssid)) | 276 | if (!WLAN_CAPABILITY_IS_MBSS(a->capability)) |
214 | return false; | 277 | return false; |
215 | 278 | ||
216 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, | 279 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, |
@@ -248,11 +311,7 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
248 | if (a->channel != b->channel) | 311 | if (a->channel != b->channel) |
249 | return b->channel->center_freq - a->channel->center_freq; | 312 | return b->channel->center_freq - a->channel->center_freq; |
250 | 313 | ||
251 | r = memcmp(a->bssid, b->bssid, ETH_ALEN); | 314 | if (WLAN_CAPABILITY_IS_MBSS(a->capability | b->capability)) { |
252 | if (r) | ||
253 | return r; | ||
254 | |||
255 | if (is_zero_ether_addr(a->bssid)) { | ||
256 | r = cmp_ies(WLAN_EID_MESH_ID, | 315 | r = cmp_ies(WLAN_EID_MESH_ID, |
257 | a->information_elements, | 316 | a->information_elements, |
258 | a->len_information_elements, | 317 | a->len_information_elements, |
@@ -267,6 +326,10 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
267 | b->len_information_elements); | 326 | b->len_information_elements); |
268 | } | 327 | } |
269 | 328 | ||
329 | r = memcmp(a->bssid, b->bssid, ETH_ALEN); | ||
330 | if (r) | ||
331 | return r; | ||
332 | |||
270 | return cmp_ies(WLAN_EID_SSID, | 333 | return cmp_ies(WLAN_EID_SSID, |
271 | a->information_elements, | 334 | a->information_elements, |
272 | a->len_information_elements, | 335 | a->len_information_elements, |
@@ -407,7 +470,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
407 | 470 | ||
408 | res->ts = jiffies; | 471 | res->ts = jiffies; |
409 | 472 | ||
410 | if (is_zero_ether_addr(res->pub.bssid)) { | 473 | if (WLAN_CAPABILITY_IS_MBSS(res->pub.capability)) { |
411 | /* must be mesh, verify */ | 474 | /* must be mesh, verify */ |
412 | meshid = cfg80211_find_ie(WLAN_EID_MESH_ID, | 475 | meshid = cfg80211_find_ie(WLAN_EID_MESH_ID, |
413 | res->pub.information_elements, | 476 | res->pub.information_elements, |
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 4294fa22bb2d..c6e4ca6a7d2e 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c | |||
@@ -93,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state) | |||
93 | 93 | ||
94 | if (rdev->ops->suspend) { | 94 | if (rdev->ops->suspend) { |
95 | rtnl_lock(); | 95 | rtnl_lock(); |
96 | ret = rdev->ops->suspend(&rdev->wiphy); | 96 | ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); |
97 | rtnl_unlock(); | 97 | rtnl_unlock(); |
98 | } | 98 | } |
99 | 99 | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c index 6a750bc6bcfe..f0536d44d43c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -544,7 +544,8 @@ EXPORT_SYMBOL(ieee80211_data_from_8023); | |||
544 | 544 | ||
545 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | 545 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, |
546 | const u8 *addr, enum nl80211_iftype iftype, | 546 | const u8 *addr, enum nl80211_iftype iftype, |
547 | const unsigned int extra_headroom) | 547 | const unsigned int extra_headroom, |
548 | bool has_80211_header) | ||
548 | { | 549 | { |
549 | struct sk_buff *frame = NULL; | 550 | struct sk_buff *frame = NULL; |
550 | u16 ethertype; | 551 | u16 ethertype; |
@@ -553,14 +554,18 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | |||
553 | int remaining, err; | 554 | int remaining, err; |
554 | u8 dst[ETH_ALEN], src[ETH_ALEN]; | 555 | u8 dst[ETH_ALEN], src[ETH_ALEN]; |
555 | 556 | ||
556 | err = ieee80211_data_to_8023(skb, addr, iftype); | 557 | if (has_80211_header) { |
557 | if (err) | 558 | err = ieee80211_data_to_8023(skb, addr, iftype); |
558 | goto out; | 559 | if (err) |
560 | goto out; | ||
559 | 561 | ||
560 | /* skip the wrapping header */ | 562 | /* skip the wrapping header */ |
561 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); | 563 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); |
562 | if (!eth) | 564 | if (!eth) |
563 | goto out; | 565 | goto out; |
566 | } else { | ||
567 | eth = (struct ethhdr *) skb->data; | ||
568 | } | ||
564 | 569 | ||
565 | while (skb != frame) { | 570 | while (skb != frame) { |
566 | u8 padding; | 571 | u8 padding; |
@@ -803,6 +808,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
803 | return -EBUSY; | 808 | return -EBUSY; |
804 | 809 | ||
805 | if (ntype != otype) { | 810 | if (ntype != otype) { |
811 | err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, | ||
812 | ntype); | ||
813 | if (err) | ||
814 | return err; | ||
815 | |||
806 | dev->ieee80211_ptr->use_4addr = false; | 816 | dev->ieee80211_ptr->use_4addr = false; |
807 | dev->ieee80211_ptr->mesh_id_up_len = 0; | 817 | dev->ieee80211_ptr->mesh_id_up_len = 0; |
808 | 818 | ||
@@ -896,3 +906,103 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate) | |||
896 | /* do NOT round down here */ | 906 | /* do NOT round down here */ |
897 | return (bitrate + 50000) / 100000; | 907 | return (bitrate + 50000) / 100000; |
898 | } | 908 | } |
909 | |||
910 | int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | ||
911 | u32 beacon_int) | ||
912 | { | ||
913 | struct wireless_dev *wdev; | ||
914 | int res = 0; | ||
915 | |||
916 | if (!beacon_int) | ||
917 | return -EINVAL; | ||
918 | |||
919 | mutex_lock(&rdev->devlist_mtx); | ||
920 | |||
921 | list_for_each_entry(wdev, &rdev->netdev_list, list) { | ||
922 | if (!wdev->beacon_interval) | ||
923 | continue; | ||
924 | if (wdev->beacon_interval != beacon_int) { | ||
925 | res = -EINVAL; | ||
926 | break; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | mutex_unlock(&rdev->devlist_mtx); | ||
931 | |||
932 | return res; | ||
933 | } | ||
934 | |||
935 | int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | ||
936 | struct wireless_dev *wdev, | ||
937 | enum nl80211_iftype iftype) | ||
938 | { | ||
939 | struct wireless_dev *wdev_iter; | ||
940 | int num[NUM_NL80211_IFTYPES]; | ||
941 | int total = 1; | ||
942 | int i, j; | ||
943 | |||
944 | ASSERT_RTNL(); | ||
945 | |||
946 | /* Always allow software iftypes */ | ||
947 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | ||
948 | return 0; | ||
949 | |||
950 | /* | ||
951 | * Drivers will gradually all set this flag, until all | ||
952 | * have it we only enforce for those that set it. | ||
953 | */ | ||
954 | if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) | ||
955 | return 0; | ||
956 | |||
957 | memset(num, 0, sizeof(num)); | ||
958 | |||
959 | num[iftype] = 1; | ||
960 | |||
961 | mutex_lock(&rdev->devlist_mtx); | ||
962 | list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { | ||
963 | if (wdev_iter == wdev) | ||
964 | continue; | ||
965 | if (!netif_running(wdev_iter->netdev)) | ||
966 | continue; | ||
967 | |||
968 | if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) | ||
969 | continue; | ||
970 | |||
971 | num[wdev_iter->iftype]++; | ||
972 | total++; | ||
973 | } | ||
974 | mutex_unlock(&rdev->devlist_mtx); | ||
975 | |||
976 | for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { | ||
977 | const struct ieee80211_iface_combination *c; | ||
978 | struct ieee80211_iface_limit *limits; | ||
979 | |||
980 | c = &rdev->wiphy.iface_combinations[i]; | ||
981 | |||
982 | limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, | ||
983 | GFP_KERNEL); | ||
984 | if (!limits) | ||
985 | return -ENOMEM; | ||
986 | if (total > c->max_interfaces) | ||
987 | goto cont; | ||
988 | |||
989 | for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { | ||
990 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | ||
991 | continue; | ||
992 | for (j = 0; j < c->n_limits; j++) { | ||
993 | if (!(limits[j].types & iftype)) | ||
994 | continue; | ||
995 | if (limits[j].max < num[iftype]) | ||
996 | goto cont; | ||
997 | limits[j].max -= num[iftype]; | ||
998 | } | ||
999 | } | ||
1000 | /* yay, it fits */ | ||
1001 | kfree(limits); | ||
1002 | return 0; | ||
1003 | cont: | ||
1004 | kfree(limits); | ||
1005 | } | ||
1006 | |||
1007 | return -EBUSY; | ||
1008 | } | ||