diff options
Diffstat (limited to 'net/wireless')
| -rw-r--r-- | net/wireless/.gitignore | 1 | ||||
| -rw-r--r-- | net/wireless/Kconfig | 13 | ||||
| -rw-r--r-- | net/wireless/Makefile | 6 | ||||
| -rw-r--r-- | net/wireless/chan.c | 41 | ||||
| -rw-r--r-- | net/wireless/core.c | 59 | ||||
| -rw-r--r-- | net/wireless/core.h | 20 | ||||
| -rw-r--r-- | net/wireless/db.txt | 17 | ||||
| -rw-r--r-- | net/wireless/genregdb.awk | 118 | ||||
| -rw-r--r-- | net/wireless/lib80211_crypt_ccmp.c | 2 | ||||
| -rw-r--r-- | net/wireless/lib80211_crypt_tkip.c | 23 | ||||
| -rw-r--r-- | net/wireless/mlme.c | 214 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 866 | ||||
| -rw-r--r-- | net/wireless/nl80211.h | 23 | ||||
| -rw-r--r-- | net/wireless/radiotap.c | 305 | ||||
| -rw-r--r-- | net/wireless/reg.c | 687 | ||||
| -rw-r--r-- | net/wireless/reg.h | 29 | ||||
| -rw-r--r-- | net/wireless/regdb.h | 7 | ||||
| -rw-r--r-- | net/wireless/scan.c | 158 | ||||
| -rw-r--r-- | net/wireless/sme.c | 41 | ||||
| -rw-r--r-- | net/wireless/sysfs.c | 20 | ||||
| -rw-r--r-- | net/wireless/util.c | 137 | ||||
| -rw-r--r-- | net/wireless/wext-compat.c | 49 | ||||
| -rw-r--r-- | net/wireless/wext-proc.c | 4 |
23 files changed, 2380 insertions, 460 deletions
diff --git a/net/wireless/.gitignore b/net/wireless/.gitignore new file mode 100644 index 000000000000..c33451b896d9 --- /dev/null +++ b/net/wireless/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| regdb.c | |||
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 90e93a5701aa..d0ee29063e5d 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
| @@ -94,20 +94,21 @@ config CFG80211_DEBUGFS | |||
| 94 | 94 | ||
| 95 | If unsure, say N. | 95 | If unsure, say N. |
| 96 | 96 | ||
| 97 | config WIRELESS_OLD_REGULATORY | 97 | config CFG80211_INTERNAL_REGDB |
| 98 | bool "Old wireless static regulatory definitions" | 98 | bool "use statically compiled regulatory rules database" if EMBEDDED |
| 99 | default n | 99 | default n |
| 100 | depends on CFG80211 | 100 | depends on CFG80211 |
| 101 | ---help--- | 101 | ---help--- |
| 102 | This option enables the old static regulatory information | 102 | This option generates an internal data structure representing |
| 103 | and uses it within the new framework. This option is available | 103 | the wireless regulatory rules described in net/wireless/db.txt |
| 104 | for historical reasons and it is advised to leave it off. | 104 | and includes code to query that database. This is an alternative |
| 105 | to using CRDA for defining regulatory rules for the kernel. | ||
| 105 | 106 | ||
| 106 | For details see: | 107 | For details see: |
| 107 | 108 | ||
| 108 | http://wireless.kernel.org/en/developers/Regulatory | 109 | http://wireless.kernel.org/en/developers/Regulatory |
| 109 | 110 | ||
| 110 | Say N and if you say Y, please tell us why. The default is N. | 111 | Most distributions have a CRDA package. So if unsure, say N. |
| 111 | 112 | ||
| 112 | config CFG80211_WEXT | 113 | config CFG80211_WEXT |
| 113 | bool "cfg80211 wireless extensions compatibility" | 114 | bool "cfg80211 wireless extensions compatibility" |
diff --git a/net/wireless/Makefile b/net/wireless/Makefile index f07c8dc7aab2..e77e508126fa 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile | |||
| @@ -13,5 +13,11 @@ cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o | |||
| 13 | cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o | 13 | cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o |
| 14 | cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o | 14 | cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o |
| 15 | cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o | 15 | cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o |
| 16 | cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o | ||
| 16 | 17 | ||
| 17 | ccflags-y += -D__CHECK_ENDIAN__ | 18 | ccflags-y += -D__CHECK_ENDIAN__ |
| 19 | |||
| 20 | $(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk | ||
| 21 | @$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@ | ||
| 22 | |||
| 23 | clean-files := regdb.c | ||
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index a46ac6c9b365..bf1737fc9a7e 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
| @@ -41,44 +41,57 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev, | |||
| 41 | return result; | 41 | return result; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 44 | struct ieee80211_channel * |
| 45 | struct wireless_dev *for_wdev, | 45 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, |
| 46 | int freq, enum nl80211_channel_type channel_type) | 46 | int freq, enum nl80211_channel_type channel_type) |
| 47 | { | 47 | { |
| 48 | struct ieee80211_channel *chan; | 48 | struct ieee80211_channel *chan; |
| 49 | struct ieee80211_sta_ht_cap *ht_cap; | 49 | struct ieee80211_sta_ht_cap *ht_cap; |
| 50 | int result; | ||
| 51 | |||
| 52 | if (rdev_fixed_channel(rdev, for_wdev)) | ||
| 53 | return -EBUSY; | ||
| 54 | |||
| 55 | if (!rdev->ops->set_channel) | ||
| 56 | return -EOPNOTSUPP; | ||
| 57 | 50 | ||
| 58 | chan = ieee80211_get_channel(&rdev->wiphy, freq); | 51 | chan = ieee80211_get_channel(&rdev->wiphy, freq); |
| 59 | 52 | ||
| 60 | /* Primary channel not allowed */ | 53 | /* Primary channel not allowed */ |
| 61 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | 54 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) |
| 62 | return -EINVAL; | 55 | return NULL; |
| 63 | 56 | ||
| 64 | if (channel_type == NL80211_CHAN_HT40MINUS && | 57 | if (channel_type == NL80211_CHAN_HT40MINUS && |
| 65 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | 58 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) |
| 66 | return -EINVAL; | 59 | return NULL; |
| 67 | else if (channel_type == NL80211_CHAN_HT40PLUS && | 60 | else if (channel_type == NL80211_CHAN_HT40PLUS && |
| 68 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | 61 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) |
| 69 | return -EINVAL; | 62 | return NULL; |
| 70 | 63 | ||
| 71 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; | 64 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; |
| 72 | 65 | ||
| 73 | if (channel_type != NL80211_CHAN_NO_HT) { | 66 | if (channel_type != NL80211_CHAN_NO_HT) { |
| 74 | if (!ht_cap->ht_supported) | 67 | if (!ht_cap->ht_supported) |
| 75 | return -EINVAL; | 68 | return NULL; |
| 76 | 69 | ||
| 77 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | 70 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || |
| 78 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) | 71 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) |
| 79 | return -EINVAL; | 72 | return NULL; |
| 80 | } | 73 | } |
| 81 | 74 | ||
| 75 | return chan; | ||
| 76 | } | ||
| 77 | |||
| 78 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | ||
| 79 | struct wireless_dev *for_wdev, | ||
| 80 | int freq, enum nl80211_channel_type channel_type) | ||
| 81 | { | ||
| 82 | struct ieee80211_channel *chan; | ||
| 83 | int result; | ||
| 84 | |||
| 85 | if (rdev_fixed_channel(rdev, for_wdev)) | ||
| 86 | return -EBUSY; | ||
| 87 | |||
| 88 | if (!rdev->ops->set_channel) | ||
| 89 | return -EOPNOTSUPP; | ||
| 90 | |||
| 91 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
| 92 | if (!chan) | ||
| 93 | return -EINVAL; | ||
| 94 | |||
| 82 | result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); | 95 | result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); |
| 83 | if (result) | 96 | if (result) |
| 84 | return result; | 97 | return result; |
diff --git a/net/wireless/core.c b/net/wireless/core.c index 92b812442488..7fdb9409ad2a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * This is the linux wireless configuration interface. | 2 | * This is the linux wireless configuration interface. |
| 3 | * | 3 | * |
| 4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| 7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
| @@ -31,15 +31,10 @@ MODULE_AUTHOR("Johannes Berg"); | |||
| 31 | MODULE_LICENSE("GPL"); | 31 | MODULE_LICENSE("GPL"); |
| 32 | MODULE_DESCRIPTION("wireless configuration support"); | 32 | MODULE_DESCRIPTION("wireless configuration support"); |
| 33 | 33 | ||
| 34 | /* RCU might be appropriate here since we usually | 34 | /* RCU-protected (and cfg80211_mutex for writers) */ |
| 35 | * only read the list, and that can happen quite | ||
| 36 | * often because we need to do it for each command */ | ||
| 37 | LIST_HEAD(cfg80211_rdev_list); | 35 | LIST_HEAD(cfg80211_rdev_list); |
| 38 | int cfg80211_rdev_list_generation; | 36 | int cfg80211_rdev_list_generation; |
| 39 | 37 | ||
| 40 | /* | ||
| 41 | * This is used to protect the cfg80211_rdev_list | ||
| 42 | */ | ||
| 43 | DEFINE_MUTEX(cfg80211_mutex); | 38 | DEFINE_MUTEX(cfg80211_mutex); |
| 44 | 39 | ||
| 45 | /* for debugfs */ | 40 | /* for debugfs */ |
| @@ -402,6 +397,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
| 402 | rdev->wiphy.retry_long = 4; | 397 | rdev->wiphy.retry_long = 4; |
| 403 | rdev->wiphy.frag_threshold = (u32) -1; | 398 | rdev->wiphy.frag_threshold = (u32) -1; |
| 404 | rdev->wiphy.rts_threshold = (u32) -1; | 399 | rdev->wiphy.rts_threshold = (u32) -1; |
| 400 | rdev->wiphy.coverage_class = 0; | ||
| 405 | 401 | ||
| 406 | return &rdev->wiphy; | 402 | return &rdev->wiphy; |
| 407 | } | 403 | } |
| @@ -417,6 +413,18 @@ int wiphy_register(struct wiphy *wiphy) | |||
| 417 | int i; | 413 | int i; |
| 418 | u16 ifmodes = wiphy->interface_modes; | 414 | u16 ifmodes = wiphy->interface_modes; |
| 419 | 415 | ||
| 416 | if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) | ||
| 417 | return -EINVAL; | ||
| 418 | |||
| 419 | if (WARN_ON(wiphy->addresses && | ||
| 420 | !is_zero_ether_addr(wiphy->perm_addr) && | ||
| 421 | memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, | ||
| 422 | ETH_ALEN))) | ||
| 423 | return -EINVAL; | ||
| 424 | |||
| 425 | if (wiphy->addresses) | ||
| 426 | memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); | ||
| 427 | |||
| 420 | /* sanity check ifmodes */ | 428 | /* sanity check ifmodes */ |
| 421 | WARN_ON(!ifmodes); | 429 | WARN_ON(!ifmodes); |
| 422 | ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; | 430 | ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; |
| @@ -476,7 +484,7 @@ int wiphy_register(struct wiphy *wiphy) | |||
| 476 | /* set up regulatory info */ | 484 | /* set up regulatory info */ |
| 477 | wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); | 485 | wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); |
| 478 | 486 | ||
| 479 | list_add(&rdev->list, &cfg80211_rdev_list); | 487 | list_add_rcu(&rdev->list, &cfg80211_rdev_list); |
| 480 | cfg80211_rdev_list_generation++; | 488 | cfg80211_rdev_list_generation++; |
| 481 | 489 | ||
| 482 | mutex_unlock(&cfg80211_mutex); | 490 | mutex_unlock(&cfg80211_mutex); |
| @@ -553,7 +561,8 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
| 553 | * it impossible to find from userspace. | 561 | * it impossible to find from userspace. |
| 554 | */ | 562 | */ |
| 555 | debugfs_remove_recursive(rdev->wiphy.debugfsdir); | 563 | debugfs_remove_recursive(rdev->wiphy.debugfsdir); |
| 556 | list_del(&rdev->list); | 564 | list_del_rcu(&rdev->list); |
| 565 | synchronize_rcu(); | ||
| 557 | 566 | ||
| 558 | /* | 567 | /* |
| 559 | * Try to grab rdev->mtx. If a command is still in progress, | 568 | * Try to grab rdev->mtx. If a command is still in progress, |
| @@ -668,8 +677,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
| 668 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); | 677 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); |
| 669 | INIT_LIST_HEAD(&wdev->event_list); | 678 | INIT_LIST_HEAD(&wdev->event_list); |
| 670 | spin_lock_init(&wdev->event_lock); | 679 | spin_lock_init(&wdev->event_lock); |
| 680 | INIT_LIST_HEAD(&wdev->action_registrations); | ||
| 681 | spin_lock_init(&wdev->action_registrations_lock); | ||
| 682 | |||
| 671 | mutex_lock(&rdev->devlist_mtx); | 683 | mutex_lock(&rdev->devlist_mtx); |
| 672 | list_add(&wdev->list, &rdev->netdev_list); | 684 | list_add_rcu(&wdev->list, &rdev->netdev_list); |
| 673 | rdev->devlist_generation++; | 685 | rdev->devlist_generation++; |
| 674 | /* can only change netns with wiphy */ | 686 | /* can only change netns with wiphy */ |
| 675 | dev->features |= NETIF_F_NETNS_LOCAL; | 687 | dev->features |= NETIF_F_NETNS_LOCAL; |
| @@ -686,19 +698,21 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
| 686 | wdev->wext.default_key = -1; | 698 | wdev->wext.default_key = -1; |
| 687 | wdev->wext.default_mgmt_key = -1; | 699 | wdev->wext.default_mgmt_key = -1; |
| 688 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 700 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; |
| 701 | #endif | ||
| 702 | |||
| 689 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) | 703 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) |
| 690 | wdev->wext.ps = true; | 704 | wdev->ps = true; |
| 691 | else | 705 | else |
| 692 | wdev->wext.ps = false; | 706 | wdev->ps = false; |
| 693 | wdev->wext.ps_timeout = 100; | 707 | wdev->ps_timeout = 100; |
| 694 | if (rdev->ops->set_power_mgmt) | 708 | if (rdev->ops->set_power_mgmt) |
| 695 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, | 709 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, |
| 696 | wdev->wext.ps, | 710 | wdev->ps, |
| 697 | wdev->wext.ps_timeout)) { | 711 | wdev->ps_timeout)) { |
| 698 | /* assume this means it's off */ | 712 | /* assume this means it's off */ |
| 699 | wdev->wext.ps = false; | 713 | wdev->ps = false; |
| 700 | } | 714 | } |
| 701 | #endif | 715 | |
| 702 | if (!dev->ethtool_ops) | 716 | if (!dev->ethtool_ops) |
| 703 | dev->ethtool_ops = &cfg80211_ethtool_ops; | 717 | dev->ethtool_ops = &cfg80211_ethtool_ops; |
| 704 | 718 | ||
| @@ -781,13 +795,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
| 781 | */ | 795 | */ |
| 782 | if (!list_empty(&wdev->list)) { | 796 | if (!list_empty(&wdev->list)) { |
| 783 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); | 797 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); |
| 784 | list_del_init(&wdev->list); | 798 | list_del_rcu(&wdev->list); |
| 785 | rdev->devlist_generation++; | 799 | rdev->devlist_generation++; |
| 800 | cfg80211_mlme_purge_actions(wdev); | ||
| 786 | #ifdef CONFIG_CFG80211_WEXT | 801 | #ifdef CONFIG_CFG80211_WEXT |
| 787 | kfree(wdev->wext.keys); | 802 | kfree(wdev->wext.keys); |
| 788 | #endif | 803 | #endif |
| 789 | } | 804 | } |
| 790 | mutex_unlock(&rdev->devlist_mtx); | 805 | mutex_unlock(&rdev->devlist_mtx); |
| 806 | /* | ||
| 807 | * synchronise (so that we won't find this netdev | ||
| 808 | * from other code any more) and then clear the list | ||
| 809 | * head so that the above code can safely check for | ||
| 810 | * !list_empty() to avoid double-cleanup. | ||
| 811 | */ | ||
| 812 | synchronize_rcu(); | ||
| 813 | INIT_LIST_HEAD(&wdev->list); | ||
| 791 | break; | 814 | break; |
| 792 | case NETDEV_PRE_UP: | 815 | case NETDEV_PRE_UP: |
| 793 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) | 816 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 4ef3efc94106..d52da913145a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Wireless configuration interface internals. | 2 | * Wireless configuration interface internals. |
| 3 | * | 3 | * |
| 4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
| 5 | */ | 5 | */ |
| 6 | #ifndef __NET_WIRELESS_CORE_H | 6 | #ifndef __NET_WIRELESS_CORE_H |
| 7 | #define __NET_WIRELESS_CORE_H | 7 | #define __NET_WIRELESS_CORE_H |
| @@ -48,6 +48,7 @@ struct cfg80211_registered_device { | |||
| 48 | 48 | ||
| 49 | /* associate netdev list */ | 49 | /* associate netdev list */ |
| 50 | struct mutex devlist_mtx; | 50 | struct mutex devlist_mtx; |
| 51 | /* protected by devlist_mtx or RCU */ | ||
| 51 | struct list_head netdev_list; | 52 | struct list_head netdev_list; |
| 52 | int devlist_generation; | 53 | int devlist_generation; |
| 53 | int opencount; /* also protected by devlist_mtx */ | 54 | int opencount; /* also protected by devlist_mtx */ |
| @@ -111,7 +112,8 @@ struct cfg80211_internal_bss { | |||
| 111 | unsigned long ts; | 112 | unsigned long ts; |
| 112 | struct kref ref; | 113 | struct kref ref; |
| 113 | atomic_t hold; | 114 | atomic_t hold; |
| 114 | bool ies_allocated; | 115 | bool beacon_ies_allocated; |
| 116 | bool proberesp_ies_allocated; | ||
| 115 | 117 | ||
| 116 | /* must be last because of priv member */ | 118 | /* must be last because of priv member */ |
| 117 | struct cfg80211_bss pub; | 119 | struct cfg80211_bss pub; |
| @@ -327,6 +329,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
| 327 | const u8 *resp_ie, size_t resp_ie_len, | 329 | const u8 *resp_ie, size_t resp_ie_len, |
| 328 | u16 status, bool wextev, | 330 | u16 status, bool wextev, |
| 329 | struct cfg80211_bss *bss); | 331 | struct cfg80211_bss *bss); |
| 332 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
| 333 | const u8 *match_data, int match_len); | ||
| 334 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid); | ||
| 335 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev); | ||
| 336 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
| 337 | struct net_device *dev, | ||
| 338 | struct ieee80211_channel *chan, | ||
| 339 | enum nl80211_channel_type channel_type, | ||
| 340 | const u8 *buf, size_t len, u64 *cookie); | ||
| 330 | 341 | ||
| 331 | /* SME */ | 342 | /* SME */ |
| 332 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, | 343 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, |
| @@ -374,10 +385,15 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | |||
| 374 | struct ieee80211_channel * | 385 | struct ieee80211_channel * |
| 375 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, | 386 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, |
| 376 | struct wireless_dev *for_wdev); | 387 | struct wireless_dev *for_wdev); |
| 388 | struct ieee80211_channel * | ||
| 389 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | ||
| 390 | int freq, enum nl80211_channel_type channel_type); | ||
| 377 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 391 | int rdev_set_freq(struct cfg80211_registered_device *rdev, |
| 378 | struct wireless_dev *for_wdev, | 392 | struct wireless_dev *for_wdev, |
| 379 | int freq, enum nl80211_channel_type channel_type); | 393 | int freq, enum nl80211_channel_type channel_type); |
| 380 | 394 | ||
| 395 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); | ||
| 396 | |||
| 381 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS | 397 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS |
| 382 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) | 398 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) |
| 383 | #else | 399 | #else |
diff --git a/net/wireless/db.txt b/net/wireless/db.txt new file mode 100644 index 000000000000..a2fc3a09ccdc --- /dev/null +++ b/net/wireless/db.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # | ||
| 2 | # This file is a placeholder to prevent accidental build breakage if someone | ||
| 3 | # enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to | ||
| 4 | # enable that build option. | ||
| 5 | # | ||
| 6 | # You should be using CRDA instead. It is even better if you use the CRDA | ||
| 7 | # package provided by your distribution, since they will probably keep it | ||
| 8 | # up-to-date on your behalf. | ||
| 9 | # | ||
| 10 | # If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will | ||
| 11 | # need to replace this file with one containing appropriately formatted | ||
| 12 | # regulatory rules that cover the regulatory domains you will be using. Your | ||
| 13 | # best option is to extract the db.txt file from the wireless-regdb git | ||
| 14 | # repository: | ||
| 15 | # | ||
| 16 | # git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git | ||
| 17 | # | ||
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk new file mode 100644 index 000000000000..3cc9e69880a8 --- /dev/null +++ b/net/wireless/genregdb.awk | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | #!/usr/bin/awk -f | ||
| 2 | # | ||
| 3 | # genregdb.awk -- generate regdb.c from db.txt | ||
| 4 | # | ||
| 5 | # Actually, it reads from stdin (presumed to be db.txt) and writes | ||
| 6 | # to stdout (presumed to be regdb.c), but close enough... | ||
| 7 | # | ||
| 8 | # Copyright 2009 John W. Linville <linville@tuxdriver.com> | ||
| 9 | # | ||
| 10 | # This program is free software; you can redistribute it and/or modify | ||
| 11 | # it under the terms of the GNU General Public License version 2 as | ||
| 12 | # published by the Free Software Foundation. | ||
| 13 | # | ||
| 14 | |||
| 15 | BEGIN { | ||
| 16 | active = 0 | ||
| 17 | rules = 0; | ||
| 18 | print "/*" | ||
| 19 | print " * DO NOT EDIT -- file generated from data in db.txt" | ||
| 20 | print " */" | ||
| 21 | print "" | ||
| 22 | print "#include <linux/nl80211.h>" | ||
| 23 | print "#include <net/cfg80211.h>" | ||
| 24 | print "" | ||
| 25 | regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" | ||
| 26 | } | ||
| 27 | |||
| 28 | /^[ \t]*#/ { | ||
| 29 | # Ignore | ||
| 30 | } | ||
| 31 | |||
| 32 | !active && /^[ \t]*$/ { | ||
| 33 | # Ignore | ||
| 34 | } | ||
| 35 | |||
| 36 | !active && /country/ { | ||
| 37 | country=$2 | ||
| 38 | sub(/:/, "", country) | ||
| 39 | printf "static const struct ieee80211_regdomain regdom_%s = {\n", country | ||
| 40 | printf "\t.alpha2 = \"%s\",\n", country | ||
| 41 | printf "\t.reg_rules = {\n" | ||
| 42 | active = 1 | ||
| 43 | regdb = regdb "\t®dom_" country ",\n" | ||
| 44 | } | ||
| 45 | |||
| 46 | active && /^[ \t]*\(/ { | ||
| 47 | start = $1 | ||
| 48 | sub(/\(/, "", start) | ||
| 49 | end = $3 | ||
| 50 | bw = $5 | ||
| 51 | sub(/\),/, "", bw) | ||
| 52 | gain = $6 | ||
| 53 | sub(/\(/, "", gain) | ||
| 54 | sub(/,/, "", gain) | ||
| 55 | power = $7 | ||
| 56 | sub(/\)/, "", power) | ||
| 57 | sub(/,/, "", power) | ||
| 58 | # power might be in mW... | ||
| 59 | units = $8 | ||
| 60 | sub(/\)/, "", units) | ||
| 61 | sub(/,/, "", units) | ||
| 62 | if (units == "mW") { | ||
| 63 | if (power == 100) { | ||
| 64 | power = 20 | ||
| 65 | } else if (power == 200) { | ||
| 66 | power = 23 | ||
| 67 | } else if (power == 500) { | ||
| 68 | power = 27 | ||
| 69 | } else if (power == 1000) { | ||
| 70 | power = 30 | ||
| 71 | } else { | ||
| 72 | print "Unknown power value in database!" | ||
| 73 | } | ||
| 74 | } | ||
| 75 | flagstr = "" | ||
| 76 | for (i=8; i<=NF; i++) | ||
| 77 | flagstr = flagstr $i | ||
| 78 | split(flagstr, flagarray, ",") | ||
| 79 | flags = "" | ||
| 80 | for (arg in flagarray) { | ||
| 81 | if (flagarray[arg] == "NO-OFDM") { | ||
| 82 | flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | " | ||
| 83 | } else if (flagarray[arg] == "NO-CCK") { | ||
| 84 | flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | " | ||
| 85 | } else if (flagarray[arg] == "NO-INDOOR") { | ||
| 86 | flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | " | ||
| 87 | } else if (flagarray[arg] == "NO-OUTDOOR") { | ||
| 88 | flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | " | ||
| 89 | } else if (flagarray[arg] == "DFS") { | ||
| 90 | flags = flags "\n\t\t\tNL80211_RRF_DFS | " | ||
| 91 | } else if (flagarray[arg] == "PTP-ONLY") { | ||
| 92 | flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | " | ||
| 93 | } else if (flagarray[arg] == "PTMP-ONLY") { | ||
| 94 | flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " | ||
| 95 | } else if (flagarray[arg] == "PASSIVE-SCAN") { | ||
| 96 | flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " | ||
| 97 | } else if (flagarray[arg] == "NO-IBSS") { | ||
| 98 | flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " | ||
| 99 | } | ||
| 100 | } | ||
| 101 | flags = flags "0" | ||
| 102 | printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags | ||
| 103 | rules++ | ||
| 104 | } | ||
| 105 | |||
| 106 | active && /^[ \t]*$/ { | ||
| 107 | active = 0 | ||
| 108 | printf "\t},\n" | ||
| 109 | printf "\t.n_reg_rules = %d\n", rules | ||
| 110 | printf "};\n\n" | ||
| 111 | rules = 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | END { | ||
| 115 | print regdb "};" | ||
| 116 | print "" | ||
| 117 | print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" | ||
| 118 | } | ||
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c index 2301dc1edc4c..b7fa31d5fd13 100644 --- a/net/wireless/lib80211_crypt_ccmp.c +++ b/net/wireless/lib80211_crypt_ccmp.c | |||
| @@ -237,7 +237,6 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
| 237 | return -1; | 237 | return -1; |
| 238 | 238 | ||
| 239 | pos = skb->data + hdr_len + CCMP_HDR_LEN; | 239 | pos = skb->data + hdr_len + CCMP_HDR_LEN; |
| 240 | mic = skb_put(skb, CCMP_MIC_LEN); | ||
| 241 | hdr = (struct ieee80211_hdr *)skb->data; | 240 | hdr = (struct ieee80211_hdr *)skb->data; |
| 242 | ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); | 241 | ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); |
| 243 | 242 | ||
| @@ -257,6 +256,7 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
| 257 | pos += len; | 256 | pos += len; |
| 258 | } | 257 | } |
| 259 | 258 | ||
| 259 | mic = skb_put(skb, CCMP_MIC_LEN); | ||
| 260 | for (i = 0; i < CCMP_MIC_LEN; i++) | 260 | for (i = 0; i < CCMP_MIC_LEN; i++) |
| 261 | mic[i] = b[i] ^ s0[i]; | 261 | mic[i] = b[i] ^ s0[i]; |
| 262 | 262 | ||
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index c36287399d7e..8cbdb32ff316 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c | |||
| @@ -36,6 +36,8 @@ MODULE_AUTHOR("Jouni Malinen"); | |||
| 36 | MODULE_DESCRIPTION("lib80211 crypt: TKIP"); | 36 | MODULE_DESCRIPTION("lib80211 crypt: TKIP"); |
| 37 | MODULE_LICENSE("GPL"); | 37 | MODULE_LICENSE("GPL"); |
| 38 | 38 | ||
| 39 | #define TKIP_HDR_LEN 8 | ||
| 40 | |||
| 39 | struct lib80211_tkip_data { | 41 | struct lib80211_tkip_data { |
| 40 | #define TKIP_KEY_LEN 32 | 42 | #define TKIP_KEY_LEN 32 |
| 41 | u8 key[TKIP_KEY_LEN]; | 43 | u8 key[TKIP_KEY_LEN]; |
| @@ -314,13 +316,12 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, | |||
| 314 | u8 * rc4key, int keylen, void *priv) | 316 | u8 * rc4key, int keylen, void *priv) |
| 315 | { | 317 | { |
| 316 | struct lib80211_tkip_data *tkey = priv; | 318 | struct lib80211_tkip_data *tkey = priv; |
| 317 | int len; | ||
| 318 | u8 *pos; | 319 | u8 *pos; |
| 319 | struct ieee80211_hdr *hdr; | 320 | struct ieee80211_hdr *hdr; |
| 320 | 321 | ||
| 321 | hdr = (struct ieee80211_hdr *)skb->data; | 322 | hdr = (struct ieee80211_hdr *)skb->data; |
| 322 | 323 | ||
| 323 | if (skb_headroom(skb) < 8 || skb->len < hdr_len) | 324 | if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len) |
| 324 | return -1; | 325 | return -1; |
| 325 | 326 | ||
| 326 | if (rc4key == NULL || keylen < 16) | 327 | if (rc4key == NULL || keylen < 16) |
| @@ -333,9 +334,8 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, | |||
| 333 | } | 334 | } |
| 334 | tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); | 335 | tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); |
| 335 | 336 | ||
| 336 | len = skb->len - hdr_len; | 337 | pos = skb_push(skb, TKIP_HDR_LEN); |
| 337 | pos = skb_push(skb, 8); | 338 | memmove(pos, pos + TKIP_HDR_LEN, hdr_len); |
| 338 | memmove(pos, pos + 8, hdr_len); | ||
| 339 | pos += hdr_len; | 339 | pos += hdr_len; |
| 340 | 340 | ||
| 341 | *pos++ = *rc4key; | 341 | *pos++ = *rc4key; |
| @@ -353,7 +353,7 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, | |||
| 353 | tkey->tx_iv32++; | 353 | tkey->tx_iv32++; |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | return 8; | 356 | return TKIP_HDR_LEN; |
| 357 | } | 357 | } |
| 358 | 358 | ||
| 359 | static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | 359 | static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) |
| @@ -384,9 +384,8 @@ static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
| 384 | if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) | 384 | if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) |
| 385 | return -1; | 385 | return -1; |
| 386 | 386 | ||
| 387 | icv = skb_put(skb, 4); | ||
| 388 | |||
| 389 | crc = ~crc32_le(~0, pos, len); | 387 | crc = ~crc32_le(~0, pos, len); |
| 388 | icv = skb_put(skb, 4); | ||
| 390 | icv[0] = crc; | 389 | icv[0] = crc; |
| 391 | icv[1] = crc >> 8; | 390 | icv[1] = crc >> 8; |
| 392 | icv[2] = crc >> 16; | 391 | icv[2] = crc >> 16; |
| @@ -434,7 +433,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
| 434 | return -1; | 433 | return -1; |
| 435 | } | 434 | } |
| 436 | 435 | ||
| 437 | if (skb->len < hdr_len + 8 + 4) | 436 | if (skb->len < hdr_len + TKIP_HDR_LEN + 4) |
| 438 | return -1; | 437 | return -1; |
| 439 | 438 | ||
| 440 | pos = skb->data + hdr_len; | 439 | pos = skb->data + hdr_len; |
| @@ -462,7 +461,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
| 462 | } | 461 | } |
| 463 | iv16 = (pos[0] << 8) | pos[2]; | 462 | iv16 = (pos[0] << 8) | pos[2]; |
| 464 | iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); | 463 | iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); |
| 465 | pos += 8; | 464 | pos += TKIP_HDR_LEN; |
| 466 | 465 | ||
| 467 | if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { | 466 | if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { |
| 468 | #ifdef CONFIG_LIB80211_DEBUG | 467 | #ifdef CONFIG_LIB80211_DEBUG |
| @@ -523,8 +522,8 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
| 523 | tkey->rx_iv16_new = iv16; | 522 | tkey->rx_iv16_new = iv16; |
| 524 | 523 | ||
| 525 | /* Remove IV and ICV */ | 524 | /* Remove IV and ICV */ |
| 526 | memmove(skb->data + 8, skb->data, hdr_len); | 525 | memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len); |
| 527 | skb_pull(skb, 8); | 526 | skb_pull(skb, TKIP_HDR_LEN); |
| 528 | skb_trim(skb, skb->len - 4); | 527 | skb_trim(skb, skb->len - 4); |
| 529 | 528 | ||
| 530 | return keyidx; | 529 | return keyidx; |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 82e6002c8d67..62bc8855e123 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
| @@ -148,22 +148,23 @@ void __cfg80211_send_deauth(struct net_device *dev, | |||
| 148 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | 148 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
| 149 | const u8 *bssid = mgmt->bssid; | 149 | const u8 *bssid = mgmt->bssid; |
| 150 | int i; | 150 | int i; |
| 151 | bool found = false; | ||
| 151 | 152 | ||
| 152 | ASSERT_WDEV_LOCK(wdev); | 153 | ASSERT_WDEV_LOCK(wdev); |
| 153 | 154 | ||
| 154 | nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); | ||
| 155 | |||
| 156 | if (wdev->current_bss && | 155 | if (wdev->current_bss && |
| 157 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { | 156 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { |
| 158 | cfg80211_unhold_bss(wdev->current_bss); | 157 | cfg80211_unhold_bss(wdev->current_bss); |
| 159 | cfg80211_put_bss(&wdev->current_bss->pub); | 158 | cfg80211_put_bss(&wdev->current_bss->pub); |
| 160 | wdev->current_bss = NULL; | 159 | wdev->current_bss = NULL; |
| 160 | found = true; | ||
| 161 | } else for (i = 0; i < MAX_AUTH_BSSES; i++) { | 161 | } else for (i = 0; i < MAX_AUTH_BSSES; i++) { |
| 162 | if (wdev->auth_bsses[i] && | 162 | if (wdev->auth_bsses[i] && |
| 163 | memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { | 163 | memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { |
| 164 | cfg80211_unhold_bss(wdev->auth_bsses[i]); | 164 | cfg80211_unhold_bss(wdev->auth_bsses[i]); |
| 165 | cfg80211_put_bss(&wdev->auth_bsses[i]->pub); | 165 | cfg80211_put_bss(&wdev->auth_bsses[i]->pub); |
| 166 | wdev->auth_bsses[i] = NULL; | 166 | wdev->auth_bsses[i] = NULL; |
| 167 | found = true; | ||
| 167 | break; | 168 | break; |
| 168 | } | 169 | } |
| 169 | if (wdev->authtry_bsses[i] && | 170 | if (wdev->authtry_bsses[i] && |
| @@ -171,10 +172,16 @@ void __cfg80211_send_deauth(struct net_device *dev, | |||
| 171 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); | 172 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); |
| 172 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); | 173 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); |
| 173 | wdev->authtry_bsses[i] = NULL; | 174 | wdev->authtry_bsses[i] = NULL; |
| 175 | found = true; | ||
| 174 | break; | 176 | break; |
| 175 | } | 177 | } |
| 176 | } | 178 | } |
| 177 | 179 | ||
| 180 | if (!found) | ||
| 181 | return; | ||
| 182 | |||
| 183 | nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); | ||
| 184 | |||
| 178 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { | 185 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { |
| 179 | u16 reason_code; | 186 | u16 reason_code; |
| 180 | bool from_ap; | 187 | bool from_ap; |
| @@ -684,3 +691,206 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | |||
| 684 | } | 691 | } |
| 685 | } | 692 | } |
| 686 | } | 693 | } |
| 694 | |||
| 695 | void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, | ||
| 696 | struct ieee80211_channel *chan, | ||
| 697 | enum nl80211_channel_type channel_type, | ||
| 698 | unsigned int duration, gfp_t gfp) | ||
| 699 | { | ||
| 700 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
| 701 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
| 702 | |||
| 703 | nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, | ||
| 704 | duration, gfp); | ||
| 705 | } | ||
| 706 | EXPORT_SYMBOL(cfg80211_ready_on_channel); | ||
| 707 | |||
| 708 | void cfg80211_remain_on_channel_expired(struct net_device *dev, | ||
| 709 | u64 cookie, | ||
| 710 | struct ieee80211_channel *chan, | ||
| 711 | enum nl80211_channel_type channel_type, | ||
| 712 | gfp_t gfp) | ||
| 713 | { | ||
| 714 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
| 715 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
| 716 | |||
| 717 | nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, | ||
| 718 | channel_type, gfp); | ||
| 719 | } | ||
| 720 | EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); | ||
| 721 | |||
| 722 | void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, | ||
| 723 | struct station_info *sinfo, gfp_t gfp) | ||
| 724 | { | ||
| 725 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
| 726 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
| 727 | |||
| 728 | nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); | ||
| 729 | } | ||
| 730 | EXPORT_SYMBOL(cfg80211_new_sta); | ||
| 731 | |||
| 732 | struct cfg80211_action_registration { | ||
| 733 | struct list_head list; | ||
| 734 | |||
| 735 | u32 nlpid; | ||
| 736 | |||
| 737 | int match_len; | ||
| 738 | |||
| 739 | u8 match[]; | ||
| 740 | }; | ||
| 741 | |||
| 742 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
| 743 | const u8 *match_data, int match_len) | ||
| 744 | { | ||
| 745 | struct cfg80211_action_registration *reg, *nreg; | ||
| 746 | int err = 0; | ||
| 747 | |||
| 748 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); | ||
| 749 | if (!nreg) | ||
| 750 | return -ENOMEM; | ||
| 751 | |||
| 752 | spin_lock_bh(&wdev->action_registrations_lock); | ||
| 753 | |||
| 754 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
| 755 | int mlen = min(match_len, reg->match_len); | ||
| 756 | |||
| 757 | if (memcmp(reg->match, match_data, mlen) == 0) { | ||
| 758 | err = -EALREADY; | ||
| 759 | break; | ||
| 760 | } | ||
| 761 | } | ||
| 762 | |||
| 763 | if (err) { | ||
| 764 | kfree(nreg); | ||
| 765 | goto out; | ||
| 766 | } | ||
| 767 | |||
| 768 | memcpy(nreg->match, match_data, match_len); | ||
| 769 | nreg->match_len = match_len; | ||
| 770 | nreg->nlpid = snd_pid; | ||
| 771 | list_add(&nreg->list, &wdev->action_registrations); | ||
| 772 | |||
| 773 | out: | ||
| 774 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
| 775 | return err; | ||
| 776 | } | ||
| 777 | |||
| 778 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid) | ||
| 779 | { | ||
| 780 | struct cfg80211_action_registration *reg, *tmp; | ||
| 781 | |||
| 782 | spin_lock_bh(&wdev->action_registrations_lock); | ||
| 783 | |||
| 784 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
| 785 | if (reg->nlpid == nlpid) { | ||
| 786 | list_del(®->list); | ||
| 787 | kfree(reg); | ||
| 788 | } | ||
| 789 | } | ||
| 790 | |||
| 791 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
| 792 | } | ||
| 793 | |||
| 794 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev) | ||
| 795 | { | ||
| 796 | struct cfg80211_action_registration *reg, *tmp; | ||
| 797 | |||
| 798 | spin_lock_bh(&wdev->action_registrations_lock); | ||
| 799 | |||
| 800 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
| 801 | list_del(®->list); | ||
| 802 | kfree(reg); | ||
| 803 | } | ||
| 804 | |||
| 805 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
| 806 | } | ||
| 807 | |||
| 808 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
| 809 | struct net_device *dev, | ||
| 810 | struct ieee80211_channel *chan, | ||
| 811 | enum nl80211_channel_type channel_type, | ||
| 812 | const u8 *buf, size_t len, u64 *cookie) | ||
| 813 | { | ||
| 814 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
| 815 | const struct ieee80211_mgmt *mgmt; | ||
| 816 | |||
| 817 | if (rdev->ops->action == NULL) | ||
| 818 | return -EOPNOTSUPP; | ||
| 819 | if (len < 24 + 1) | ||
| 820 | return -EINVAL; | ||
| 821 | |||
| 822 | mgmt = (const struct ieee80211_mgmt *) buf; | ||
| 823 | if (!ieee80211_is_action(mgmt->frame_control)) | ||
| 824 | return -EINVAL; | ||
| 825 | if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { | ||
| 826 | /* Verify that we are associated with the destination AP */ | ||
| 827 | if (!wdev->current_bss || | ||
| 828 | memcmp(wdev->current_bss->pub.bssid, mgmt->bssid, | ||
| 829 | ETH_ALEN) != 0 || | ||
| 830 | memcmp(wdev->current_bss->pub.bssid, mgmt->da, | ||
| 831 | ETH_ALEN) != 0) | ||
| 832 | return -ENOTCONN; | ||
| 833 | } | ||
| 834 | |||
| 835 | if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) | ||
| 836 | return -EINVAL; | ||
| 837 | |||
| 838 | /* Transmit the Action frame as requested by user space */ | ||
| 839 | return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type, | ||
| 840 | buf, len, cookie); | ||
| 841 | } | ||
| 842 | |||
| 843 | bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, | ||
| 844 | size_t len, gfp_t gfp) | ||
| 845 | { | ||
| 846 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
| 847 | struct wiphy *wiphy = wdev->wiphy; | ||
| 848 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
| 849 | struct cfg80211_action_registration *reg; | ||
| 850 | const u8 *action_data; | ||
| 851 | int action_data_len; | ||
| 852 | bool result = false; | ||
| 853 | |||
| 854 | /* frame length - min size excluding category */ | ||
| 855 | action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1); | ||
| 856 | |||
| 857 | /* action data starts with category */ | ||
| 858 | action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1; | ||
| 859 | |||
| 860 | spin_lock_bh(&wdev->action_registrations_lock); | ||
| 861 | |||
| 862 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
| 863 | if (reg->match_len > action_data_len) | ||
| 864 | continue; | ||
| 865 | |||
| 866 | if (memcmp(reg->match, action_data, reg->match_len)) | ||
| 867 | continue; | ||
| 868 | |||
| 869 | /* found match! */ | ||
| 870 | |||
| 871 | /* Indicate the received Action frame to user space */ | ||
| 872 | if (nl80211_send_action(rdev, dev, reg->nlpid, freq, | ||
| 873 | buf, len, gfp)) | ||
| 874 | continue; | ||
| 875 | |||
| 876 | result = true; | ||
| 877 | break; | ||
| 878 | } | ||
| 879 | |||
| 880 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
| 881 | |||
| 882 | return result; | ||
| 883 | } | ||
| 884 | EXPORT_SYMBOL(cfg80211_rx_action); | ||
| 885 | |||
| 886 | void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, | ||
| 887 | const u8 *buf, size_t len, bool ack, gfp_t gfp) | ||
| 888 | { | ||
| 889 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
| 890 | struct wiphy *wiphy = wdev->wiphy; | ||
| 891 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
| 892 | |||
| 893 | /* Indicate TX status of the Action frame to user space */ | ||
| 894 | nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); | ||
| 895 | } | ||
| 896 | EXPORT_SYMBOL(cfg80211_action_tx_status); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a6028433e3a0..e447db04cf76 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * This is the new netlink-based wireless configuration interface. | 2 | * This is the new netlink-based wireless configuration interface. |
| 3 | * | 3 | * |
| 4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| 7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
| @@ -58,7 +58,7 @@ static int get_rdev_dev_by_info_ifindex(struct genl_info *info, | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | /* policy for the attributes */ | 60 | /* policy for the attributes */ |
| 61 | static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | 61 | static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { |
| 62 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 62 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
| 63 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 63 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
| 64 | .len = 20-1 }, | 64 | .len = 20-1 }, |
| @@ -69,6 +69,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
| 69 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, | 69 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, |
| 70 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, | 70 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, |
| 71 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, | 71 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, |
| 72 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, | ||
| 72 | 73 | ||
| 73 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 74 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
| 74 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 75 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
| @@ -141,11 +142,17 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
| 141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | 142 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, |
| 142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | 143 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, |
| 143 | .len = WLAN_PMKID_LEN }, | 144 | .len = WLAN_PMKID_LEN }, |
| 145 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | ||
| 146 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | ||
| 147 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, | ||
| 148 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | ||
| 149 | .len = IEEE80211_MAX_DATA_LEN }, | ||
| 150 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | ||
| 151 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | ||
| 144 | }; | 152 | }; |
| 145 | 153 | ||
| 146 | /* policy for the attributes */ | 154 | /* policy for the attributes */ |
| 147 | static struct nla_policy | 155 | static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { |
| 148 | nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | ||
| 149 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, | 156 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, |
| 150 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, | 157 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, |
| 151 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, | 158 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, |
| @@ -442,6 +449,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
| 442 | dev->wiphy.frag_threshold); | 449 | dev->wiphy.frag_threshold); |
| 443 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, | 450 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, |
| 444 | dev->wiphy.rts_threshold); | 451 | dev->wiphy.rts_threshold); |
| 452 | NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, | ||
| 453 | dev->wiphy.coverage_class); | ||
| 445 | 454 | ||
| 446 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, | 455 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, |
| 447 | dev->wiphy.max_scan_ssids); | 456 | dev->wiphy.max_scan_ssids); |
| @@ -569,6 +578,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
| 569 | CMD(set_pmksa, SET_PMKSA); | 578 | CMD(set_pmksa, SET_PMKSA); |
| 570 | CMD(del_pmksa, DEL_PMKSA); | 579 | CMD(del_pmksa, DEL_PMKSA); |
| 571 | CMD(flush_pmksa, FLUSH_PMKSA); | 580 | CMD(flush_pmksa, FLUSH_PMKSA); |
| 581 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | ||
| 582 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | ||
| 583 | CMD(action, ACTION); | ||
| 572 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 584 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
| 573 | i++; | 585 | i++; |
| 574 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 586 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
| @@ -681,6 +693,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 681 | u32 changed; | 693 | u32 changed; |
| 682 | u8 retry_short = 0, retry_long = 0; | 694 | u8 retry_short = 0, retry_long = 0; |
| 683 | u32 frag_threshold = 0, rts_threshold = 0; | 695 | u32 frag_threshold = 0, rts_threshold = 0; |
| 696 | u8 coverage_class = 0; | ||
| 684 | 697 | ||
| 685 | rtnl_lock(); | 698 | rtnl_lock(); |
| 686 | 699 | ||
| @@ -803,9 +816,16 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 803 | changed |= WIPHY_PARAM_RTS_THRESHOLD; | 816 | changed |= WIPHY_PARAM_RTS_THRESHOLD; |
| 804 | } | 817 | } |
| 805 | 818 | ||
| 819 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { | ||
| 820 | coverage_class = nla_get_u8( | ||
| 821 | info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); | ||
| 822 | changed |= WIPHY_PARAM_COVERAGE_CLASS; | ||
| 823 | } | ||
| 824 | |||
| 806 | if (changed) { | 825 | if (changed) { |
| 807 | u8 old_retry_short, old_retry_long; | 826 | u8 old_retry_short, old_retry_long; |
| 808 | u32 old_frag_threshold, old_rts_threshold; | 827 | u32 old_frag_threshold, old_rts_threshold; |
| 828 | u8 old_coverage_class; | ||
| 809 | 829 | ||
| 810 | if (!rdev->ops->set_wiphy_params) { | 830 | if (!rdev->ops->set_wiphy_params) { |
| 811 | result = -EOPNOTSUPP; | 831 | result = -EOPNOTSUPP; |
| @@ -816,6 +836,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 816 | old_retry_long = rdev->wiphy.retry_long; | 836 | old_retry_long = rdev->wiphy.retry_long; |
| 817 | old_frag_threshold = rdev->wiphy.frag_threshold; | 837 | old_frag_threshold = rdev->wiphy.frag_threshold; |
| 818 | old_rts_threshold = rdev->wiphy.rts_threshold; | 838 | old_rts_threshold = rdev->wiphy.rts_threshold; |
| 839 | old_coverage_class = rdev->wiphy.coverage_class; | ||
| 819 | 840 | ||
| 820 | if (changed & WIPHY_PARAM_RETRY_SHORT) | 841 | if (changed & WIPHY_PARAM_RETRY_SHORT) |
| 821 | rdev->wiphy.retry_short = retry_short; | 842 | rdev->wiphy.retry_short = retry_short; |
| @@ -825,6 +846,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 825 | rdev->wiphy.frag_threshold = frag_threshold; | 846 | rdev->wiphy.frag_threshold = frag_threshold; |
| 826 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) | 847 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) |
| 827 | rdev->wiphy.rts_threshold = rts_threshold; | 848 | rdev->wiphy.rts_threshold = rts_threshold; |
| 849 | if (changed & WIPHY_PARAM_COVERAGE_CLASS) | ||
| 850 | rdev->wiphy.coverage_class = coverage_class; | ||
| 828 | 851 | ||
| 829 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); | 852 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); |
| 830 | if (result) { | 853 | if (result) { |
| @@ -832,6 +855,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 832 | rdev->wiphy.retry_long = old_retry_long; | 855 | rdev->wiphy.retry_long = old_retry_long; |
| 833 | rdev->wiphy.frag_threshold = old_frag_threshold; | 856 | rdev->wiphy.frag_threshold = old_frag_threshold; |
| 834 | rdev->wiphy.rts_threshold = old_rts_threshold; | 857 | rdev->wiphy.rts_threshold = old_rts_threshold; |
| 858 | rdev->wiphy.coverage_class = old_coverage_class; | ||
| 835 | } | 859 | } |
| 836 | } | 860 | } |
| 837 | 861 | ||
| @@ -1637,42 +1661,9 @@ static int parse_station_flags(struct genl_info *info, | |||
| 1637 | return 0; | 1661 | return 0; |
| 1638 | } | 1662 | } |
| 1639 | 1663 | ||
| 1640 | static u16 nl80211_calculate_bitrate(struct rate_info *rate) | ||
| 1641 | { | ||
| 1642 | int modulation, streams, bitrate; | ||
| 1643 | |||
| 1644 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
| 1645 | return rate->legacy; | ||
| 1646 | |||
| 1647 | /* the formula below does only work for MCS values smaller than 32 */ | ||
| 1648 | if (rate->mcs >= 32) | ||
| 1649 | return 0; | ||
| 1650 | |||
| 1651 | modulation = rate->mcs & 7; | ||
| 1652 | streams = (rate->mcs >> 3) + 1; | ||
| 1653 | |||
| 1654 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
| 1655 | 13500000 : 6500000; | ||
| 1656 | |||
| 1657 | if (modulation < 4) | ||
| 1658 | bitrate *= (modulation + 1); | ||
| 1659 | else if (modulation == 4) | ||
| 1660 | bitrate *= (modulation + 2); | ||
| 1661 | else | ||
| 1662 | bitrate *= (modulation + 3); | ||
| 1663 | |||
| 1664 | bitrate *= streams; | ||
| 1665 | |||
| 1666 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
| 1667 | bitrate = (bitrate / 9) * 10; | ||
| 1668 | |||
| 1669 | /* do NOT round down here */ | ||
| 1670 | return (bitrate + 50000) / 100000; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 1664 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
| 1674 | int flags, struct net_device *dev, | 1665 | int flags, struct net_device *dev, |
| 1675 | u8 *mac_addr, struct station_info *sinfo) | 1666 | const u8 *mac_addr, struct station_info *sinfo) |
| 1676 | { | 1667 | { |
| 1677 | void *hdr; | 1668 | void *hdr; |
| 1678 | struct nlattr *sinfoattr, *txrate; | 1669 | struct nlattr *sinfoattr, *txrate; |
| @@ -1716,8 +1707,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
| 1716 | if (!txrate) | 1707 | if (!txrate) |
| 1717 | goto nla_put_failure; | 1708 | goto nla_put_failure; |
| 1718 | 1709 | ||
| 1719 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | 1710 | /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
| 1720 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | 1711 | bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); |
| 1721 | if (bitrate > 0) | 1712 | if (bitrate > 0) |
| 1722 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | 1713 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); |
| 1723 | 1714 | ||
| @@ -2023,6 +2014,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2023 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 2014 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
| 2024 | return -EINVAL; | 2015 | return -EINVAL; |
| 2025 | 2016 | ||
| 2017 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
| 2018 | return -EINVAL; | ||
| 2019 | |||
| 2026 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 2020 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
| 2027 | params.supported_rates = | 2021 | params.supported_rates = |
| 2028 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 2022 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
| @@ -2031,11 +2025,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2031 | params.listen_interval = | 2025 | params.listen_interval = |
| 2032 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 2026 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
| 2033 | 2027 | ||
| 2034 | if (info->attrs[NL80211_ATTR_STA_AID]) { | 2028 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); |
| 2035 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | 2029 | if (!params.aid || params.aid > IEEE80211_MAX_AID) |
| 2036 | if (!params.aid || params.aid > IEEE80211_MAX_AID) | 2030 | return -EINVAL; |
| 2037 | return -EINVAL; | ||
| 2038 | } | ||
| 2039 | 2031 | ||
| 2040 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 2032 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
| 2041 | params.ht_capa = | 2033 | params.ht_capa = |
| @@ -2050,6 +2042,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2050 | if (err) | 2042 | if (err) |
| 2051 | goto out_rtnl; | 2043 | goto out_rtnl; |
| 2052 | 2044 | ||
| 2045 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
| 2046 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | ||
| 2047 | err = -EINVAL; | ||
| 2048 | goto out; | ||
| 2049 | } | ||
| 2050 | |||
| 2053 | err = get_vlan(info, rdev, ¶ms.vlan); | 2051 | err = get_vlan(info, rdev, ¶ms.vlan); |
| 2054 | if (err) | 2052 | if (err) |
| 2055 | goto out; | 2053 | goto out; |
| @@ -2057,35 +2055,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2057 | /* validate settings */ | 2055 | /* validate settings */ |
| 2058 | err = 0; | 2056 | err = 0; |
| 2059 | 2057 | ||
| 2060 | switch (dev->ieee80211_ptr->iftype) { | ||
| 2061 | case NL80211_IFTYPE_AP: | ||
| 2062 | case NL80211_IFTYPE_AP_VLAN: | ||
| 2063 | /* all ok but must have AID */ | ||
| 2064 | if (!params.aid) | ||
| 2065 | err = -EINVAL; | ||
| 2066 | break; | ||
| 2067 | case NL80211_IFTYPE_MESH_POINT: | ||
| 2068 | /* disallow things mesh doesn't support */ | ||
| 2069 | if (params.vlan) | ||
| 2070 | err = -EINVAL; | ||
| 2071 | if (params.aid) | ||
| 2072 | err = -EINVAL; | ||
| 2073 | if (params.ht_capa) | ||
| 2074 | err = -EINVAL; | ||
| 2075 | if (params.listen_interval >= 0) | ||
| 2076 | err = -EINVAL; | ||
| 2077 | if (params.supported_rates) | ||
| 2078 | err = -EINVAL; | ||
| 2079 | if (params.sta_flags_mask) | ||
| 2080 | err = -EINVAL; | ||
| 2081 | break; | ||
| 2082 | default: | ||
| 2083 | err = -EINVAL; | ||
| 2084 | } | ||
| 2085 | |||
| 2086 | if (err) | ||
| 2087 | goto out; | ||
| 2088 | |||
| 2089 | if (!rdev->ops->add_station) { | 2058 | if (!rdev->ops->add_station) { |
| 2090 | err = -EOPNOTSUPP; | 2059 | err = -EOPNOTSUPP; |
| 2091 | goto out; | 2060 | goto out; |
| @@ -2126,8 +2095,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2126 | goto out_rtnl; | 2095 | goto out_rtnl; |
| 2127 | 2096 | ||
| 2128 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2097 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
| 2129 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | 2098 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { |
| 2130 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
| 2131 | err = -EINVAL; | 2099 | err = -EINVAL; |
| 2132 | goto out; | 2100 | goto out; |
| 2133 | } | 2101 | } |
| @@ -2514,8 +2482,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
| 2514 | return err; | 2482 | return err; |
| 2515 | } | 2483 | } |
| 2516 | 2484 | ||
| 2517 | static const struct nla_policy | 2485 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { |
| 2518 | reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | ||
| 2519 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, | 2486 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, |
| 2520 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, | 2487 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, |
| 2521 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, | 2488 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, |
| @@ -2583,12 +2550,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
| 2583 | 2550 | ||
| 2584 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | 2551 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); |
| 2585 | 2552 | ||
| 2586 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
| 2587 | /* We ignore world regdom requests with the old regdom setup */ | ||
| 2588 | if (is_world_regdom(data)) | ||
| 2589 | return -EINVAL; | ||
| 2590 | #endif | ||
| 2591 | |||
| 2592 | r = regulatory_hint_user(data); | 2553 | r = regulatory_hint_user(data); |
| 2593 | 2554 | ||
| 2594 | return r; | 2555 | return r; |
| @@ -2690,8 +2651,7 @@ do {\ | |||
| 2690 | } \ | 2651 | } \ |
| 2691 | } while (0);\ | 2652 | } while (0);\ |
| 2692 | 2653 | ||
| 2693 | static struct nla_policy | 2654 | static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { |
| 2694 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = { | ||
| 2695 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, | 2655 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, |
| 2696 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, | 2656 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, |
| 2697 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, | 2657 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, |
| @@ -3182,6 +3142,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
| 3182 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, | 3142 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, |
| 3183 | res->len_information_elements, | 3143 | res->len_information_elements, |
| 3184 | res->information_elements); | 3144 | res->information_elements); |
| 3145 | if (res->beacon_ies && res->len_beacon_ies && | ||
| 3146 | res->beacon_ies != res->information_elements) | ||
| 3147 | NLA_PUT(msg, NL80211_BSS_BEACON_IES, | ||
| 3148 | res->len_beacon_ies, res->beacon_ies); | ||
| 3185 | if (res->tsf) | 3149 | if (res->tsf) |
| 3186 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); | 3150 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); |
| 3187 | if (res->beacon_interval) | 3151 | if (res->beacon_interval) |
| @@ -3586,6 +3550,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
| 3586 | { | 3550 | { |
| 3587 | struct cfg80211_registered_device *rdev; | 3551 | struct cfg80211_registered_device *rdev; |
| 3588 | struct net_device *dev; | 3552 | struct net_device *dev; |
| 3553 | struct wireless_dev *wdev; | ||
| 3589 | struct cfg80211_crypto_settings crypto; | 3554 | struct cfg80211_crypto_settings crypto; |
| 3590 | struct ieee80211_channel *chan, *fixedchan; | 3555 | struct ieee80211_channel *chan, *fixedchan; |
| 3591 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; | 3556 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; |
| @@ -3631,7 +3596,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
| 3631 | } | 3596 | } |
| 3632 | 3597 | ||
| 3633 | mutex_lock(&rdev->devlist_mtx); | 3598 | mutex_lock(&rdev->devlist_mtx); |
| 3634 | fixedchan = rdev_fixed_channel(rdev, NULL); | 3599 | wdev = dev->ieee80211_ptr; |
| 3600 | fixedchan = rdev_fixed_channel(rdev, wdev); | ||
| 3635 | if (fixedchan && chan != fixedchan) { | 3601 | if (fixedchan && chan != fixedchan) { |
| 3636 | err = -EBUSY; | 3602 | err = -EBUSY; |
| 3637 | mutex_unlock(&rdev->devlist_mtx); | 3603 | mutex_unlock(&rdev->devlist_mtx); |
| @@ -4322,6 +4288,496 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | |||
| 4322 | 4288 | ||
| 4323 | } | 4289 | } |
| 4324 | 4290 | ||
| 4291 | static int nl80211_remain_on_channel(struct sk_buff *skb, | ||
| 4292 | struct genl_info *info) | ||
| 4293 | { | ||
| 4294 | struct cfg80211_registered_device *rdev; | ||
| 4295 | struct net_device *dev; | ||
| 4296 | struct ieee80211_channel *chan; | ||
| 4297 | struct sk_buff *msg; | ||
| 4298 | void *hdr; | ||
| 4299 | u64 cookie; | ||
| 4300 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
| 4301 | u32 freq, duration; | ||
| 4302 | int err; | ||
| 4303 | |||
| 4304 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
| 4305 | !info->attrs[NL80211_ATTR_DURATION]) | ||
| 4306 | return -EINVAL; | ||
| 4307 | |||
| 4308 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
| 4309 | |||
| 4310 | /* | ||
| 4311 | * We should be on that channel for at least one jiffie, | ||
| 4312 | * and more than 5 seconds seems excessive. | ||
| 4313 | */ | ||
| 4314 | if (!duration || !msecs_to_jiffies(duration) || duration > 5000) | ||
| 4315 | return -EINVAL; | ||
| 4316 | |||
| 4317 | rtnl_lock(); | ||
| 4318 | |||
| 4319 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4320 | if (err) | ||
| 4321 | goto unlock_rtnl; | ||
| 4322 | |||
| 4323 | if (!rdev->ops->remain_on_channel) { | ||
| 4324 | err = -EOPNOTSUPP; | ||
| 4325 | goto out; | ||
| 4326 | } | ||
| 4327 | |||
| 4328 | if (!netif_running(dev)) { | ||
| 4329 | err = -ENETDOWN; | ||
| 4330 | goto out; | ||
| 4331 | } | ||
| 4332 | |||
| 4333 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
| 4334 | channel_type = nla_get_u32( | ||
| 4335 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
| 4336 | if (channel_type != NL80211_CHAN_NO_HT && | ||
| 4337 | channel_type != NL80211_CHAN_HT20 && | ||
| 4338 | channel_type != NL80211_CHAN_HT40PLUS && | ||
| 4339 | channel_type != NL80211_CHAN_HT40MINUS) | ||
| 4340 | err = -EINVAL; | ||
| 4341 | goto out; | ||
| 4342 | } | ||
| 4343 | |||
| 4344 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
| 4345 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
| 4346 | if (chan == NULL) { | ||
| 4347 | err = -EINVAL; | ||
| 4348 | goto out; | ||
| 4349 | } | ||
| 4350 | |||
| 4351 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 4352 | if (!msg) { | ||
| 4353 | err = -ENOMEM; | ||
| 4354 | goto out; | ||
| 4355 | } | ||
| 4356 | |||
| 4357 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
| 4358 | NL80211_CMD_REMAIN_ON_CHANNEL); | ||
| 4359 | |||
| 4360 | if (IS_ERR(hdr)) { | ||
| 4361 | err = PTR_ERR(hdr); | ||
| 4362 | goto free_msg; | ||
| 4363 | } | ||
| 4364 | |||
| 4365 | err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, | ||
| 4366 | channel_type, duration, &cookie); | ||
| 4367 | |||
| 4368 | if (err) | ||
| 4369 | goto free_msg; | ||
| 4370 | |||
| 4371 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 4372 | |||
| 4373 | genlmsg_end(msg, hdr); | ||
| 4374 | err = genlmsg_reply(msg, info); | ||
| 4375 | goto out; | ||
| 4376 | |||
| 4377 | nla_put_failure: | ||
| 4378 | err = -ENOBUFS; | ||
| 4379 | free_msg: | ||
| 4380 | nlmsg_free(msg); | ||
| 4381 | out: | ||
| 4382 | cfg80211_unlock_rdev(rdev); | ||
| 4383 | dev_put(dev); | ||
| 4384 | unlock_rtnl: | ||
| 4385 | rtnl_unlock(); | ||
| 4386 | return err; | ||
| 4387 | } | ||
| 4388 | |||
| 4389 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | ||
| 4390 | struct genl_info *info) | ||
| 4391 | { | ||
| 4392 | struct cfg80211_registered_device *rdev; | ||
| 4393 | struct net_device *dev; | ||
| 4394 | u64 cookie; | ||
| 4395 | int err; | ||
| 4396 | |||
| 4397 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
| 4398 | return -EINVAL; | ||
| 4399 | |||
| 4400 | rtnl_lock(); | ||
| 4401 | |||
| 4402 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4403 | if (err) | ||
| 4404 | goto unlock_rtnl; | ||
| 4405 | |||
| 4406 | if (!rdev->ops->cancel_remain_on_channel) { | ||
| 4407 | err = -EOPNOTSUPP; | ||
| 4408 | goto out; | ||
| 4409 | } | ||
| 4410 | |||
| 4411 | if (!netif_running(dev)) { | ||
| 4412 | err = -ENETDOWN; | ||
| 4413 | goto out; | ||
| 4414 | } | ||
| 4415 | |||
| 4416 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
| 4417 | |||
| 4418 | err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); | ||
| 4419 | |||
| 4420 | out: | ||
| 4421 | cfg80211_unlock_rdev(rdev); | ||
| 4422 | dev_put(dev); | ||
| 4423 | unlock_rtnl: | ||
| 4424 | rtnl_unlock(); | ||
| 4425 | return err; | ||
| 4426 | } | ||
| 4427 | |||
| 4428 | static u32 rateset_to_mask(struct ieee80211_supported_band *sband, | ||
| 4429 | u8 *rates, u8 rates_len) | ||
| 4430 | { | ||
| 4431 | u8 i; | ||
| 4432 | u32 mask = 0; | ||
| 4433 | |||
| 4434 | for (i = 0; i < rates_len; i++) { | ||
| 4435 | int rate = (rates[i] & 0x7f) * 5; | ||
| 4436 | int ridx; | ||
| 4437 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { | ||
| 4438 | struct ieee80211_rate *srate = | ||
| 4439 | &sband->bitrates[ridx]; | ||
| 4440 | if (rate == srate->bitrate) { | ||
| 4441 | mask |= 1 << ridx; | ||
| 4442 | break; | ||
| 4443 | } | ||
| 4444 | } | ||
| 4445 | if (ridx == sband->n_bitrates) | ||
| 4446 | return 0; /* rate not found */ | ||
| 4447 | } | ||
| 4448 | |||
| 4449 | return mask; | ||
| 4450 | } | ||
| 4451 | |||
| 4452 | static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { | ||
| 4453 | [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, | ||
| 4454 | .len = NL80211_MAX_SUPP_RATES }, | ||
| 4455 | }; | ||
| 4456 | |||
| 4457 | static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, | ||
| 4458 | struct genl_info *info) | ||
| 4459 | { | ||
| 4460 | struct nlattr *tb[NL80211_TXRATE_MAX + 1]; | ||
| 4461 | struct cfg80211_registered_device *rdev; | ||
| 4462 | struct cfg80211_bitrate_mask mask; | ||
| 4463 | int err, rem, i; | ||
| 4464 | struct net_device *dev; | ||
| 4465 | struct nlattr *tx_rates; | ||
| 4466 | struct ieee80211_supported_band *sband; | ||
| 4467 | |||
| 4468 | if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) | ||
| 4469 | return -EINVAL; | ||
| 4470 | |||
| 4471 | rtnl_lock(); | ||
| 4472 | |||
| 4473 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4474 | if (err) | ||
| 4475 | goto unlock_rtnl; | ||
| 4476 | |||
| 4477 | if (!rdev->ops->set_bitrate_mask) { | ||
| 4478 | err = -EOPNOTSUPP; | ||
| 4479 | goto unlock; | ||
| 4480 | } | ||
| 4481 | |||
| 4482 | memset(&mask, 0, sizeof(mask)); | ||
| 4483 | /* Default to all rates enabled */ | ||
| 4484 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { | ||
| 4485 | sband = rdev->wiphy.bands[i]; | ||
| 4486 | mask.control[i].legacy = | ||
| 4487 | sband ? (1 << sband->n_bitrates) - 1 : 0; | ||
| 4488 | } | ||
| 4489 | |||
| 4490 | /* | ||
| 4491 | * The nested attribute uses enum nl80211_band as the index. This maps | ||
| 4492 | * directly to the enum ieee80211_band values used in cfg80211. | ||
| 4493 | */ | ||
| 4494 | nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) | ||
| 4495 | { | ||
| 4496 | enum ieee80211_band band = nla_type(tx_rates); | ||
| 4497 | if (band < 0 || band >= IEEE80211_NUM_BANDS) { | ||
| 4498 | err = -EINVAL; | ||
| 4499 | goto unlock; | ||
| 4500 | } | ||
| 4501 | sband = rdev->wiphy.bands[band]; | ||
| 4502 | if (sband == NULL) { | ||
| 4503 | err = -EINVAL; | ||
| 4504 | goto unlock; | ||
| 4505 | } | ||
| 4506 | nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), | ||
| 4507 | nla_len(tx_rates), nl80211_txattr_policy); | ||
| 4508 | if (tb[NL80211_TXRATE_LEGACY]) { | ||
| 4509 | mask.control[band].legacy = rateset_to_mask( | ||
| 4510 | sband, | ||
| 4511 | nla_data(tb[NL80211_TXRATE_LEGACY]), | ||
| 4512 | nla_len(tb[NL80211_TXRATE_LEGACY])); | ||
| 4513 | if (mask.control[band].legacy == 0) { | ||
| 4514 | err = -EINVAL; | ||
| 4515 | goto unlock; | ||
| 4516 | } | ||
| 4517 | } | ||
| 4518 | } | ||
| 4519 | |||
| 4520 | err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); | ||
| 4521 | |||
| 4522 | unlock: | ||
| 4523 | dev_put(dev); | ||
| 4524 | cfg80211_unlock_rdev(rdev); | ||
| 4525 | unlock_rtnl: | ||
| 4526 | rtnl_unlock(); | ||
| 4527 | return err; | ||
| 4528 | } | ||
| 4529 | |||
| 4530 | static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) | ||
| 4531 | { | ||
| 4532 | struct cfg80211_registered_device *rdev; | ||
| 4533 | struct net_device *dev; | ||
| 4534 | int err; | ||
| 4535 | |||
| 4536 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) | ||
| 4537 | return -EINVAL; | ||
| 4538 | |||
| 4539 | if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1) | ||
| 4540 | return -EINVAL; | ||
| 4541 | |||
| 4542 | rtnl_lock(); | ||
| 4543 | |||
| 4544 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4545 | if (err) | ||
| 4546 | goto unlock_rtnl; | ||
| 4547 | |||
| 4548 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
| 4549 | err = -EOPNOTSUPP; | ||
| 4550 | goto out; | ||
| 4551 | } | ||
| 4552 | |||
| 4553 | /* not much point in registering if we can't reply */ | ||
| 4554 | if (!rdev->ops->action) { | ||
| 4555 | err = -EOPNOTSUPP; | ||
| 4556 | goto out; | ||
| 4557 | } | ||
| 4558 | |||
| 4559 | err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid, | ||
| 4560 | nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), | ||
| 4561 | nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); | ||
| 4562 | out: | ||
| 4563 | cfg80211_unlock_rdev(rdev); | ||
| 4564 | dev_put(dev); | ||
| 4565 | unlock_rtnl: | ||
| 4566 | rtnl_unlock(); | ||
| 4567 | return err; | ||
| 4568 | } | ||
| 4569 | |||
| 4570 | static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | ||
| 4571 | { | ||
| 4572 | struct cfg80211_registered_device *rdev; | ||
| 4573 | struct net_device *dev; | ||
| 4574 | struct ieee80211_channel *chan; | ||
| 4575 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
| 4576 | u32 freq; | ||
| 4577 | int err; | ||
| 4578 | void *hdr; | ||
| 4579 | u64 cookie; | ||
| 4580 | struct sk_buff *msg; | ||
| 4581 | |||
| 4582 | if (!info->attrs[NL80211_ATTR_FRAME] || | ||
| 4583 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
| 4584 | return -EINVAL; | ||
| 4585 | |||
| 4586 | rtnl_lock(); | ||
| 4587 | |||
| 4588 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4589 | if (err) | ||
| 4590 | goto unlock_rtnl; | ||
| 4591 | |||
| 4592 | if (!rdev->ops->action) { | ||
| 4593 | err = -EOPNOTSUPP; | ||
| 4594 | goto out; | ||
| 4595 | } | ||
| 4596 | |||
| 4597 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
| 4598 | err = -EOPNOTSUPP; | ||
| 4599 | goto out; | ||
| 4600 | } | ||
| 4601 | |||
| 4602 | if (!netif_running(dev)) { | ||
| 4603 | err = -ENETDOWN; | ||
| 4604 | goto out; | ||
| 4605 | } | ||
| 4606 | |||
| 4607 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
| 4608 | channel_type = nla_get_u32( | ||
| 4609 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
| 4610 | if (channel_type != NL80211_CHAN_NO_HT && | ||
| 4611 | channel_type != NL80211_CHAN_HT20 && | ||
| 4612 | channel_type != NL80211_CHAN_HT40PLUS && | ||
| 4613 | channel_type != NL80211_CHAN_HT40MINUS) | ||
| 4614 | err = -EINVAL; | ||
| 4615 | goto out; | ||
| 4616 | } | ||
| 4617 | |||
| 4618 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
| 4619 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
| 4620 | if (chan == NULL) { | ||
| 4621 | err = -EINVAL; | ||
| 4622 | goto out; | ||
| 4623 | } | ||
| 4624 | |||
| 4625 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 4626 | if (!msg) { | ||
| 4627 | err = -ENOMEM; | ||
| 4628 | goto out; | ||
| 4629 | } | ||
| 4630 | |||
| 4631 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
| 4632 | NL80211_CMD_ACTION); | ||
| 4633 | |||
| 4634 | if (IS_ERR(hdr)) { | ||
| 4635 | err = PTR_ERR(hdr); | ||
| 4636 | goto free_msg; | ||
| 4637 | } | ||
| 4638 | err = cfg80211_mlme_action(rdev, dev, chan, channel_type, | ||
| 4639 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | ||
| 4640 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | ||
| 4641 | &cookie); | ||
| 4642 | if (err) | ||
| 4643 | goto free_msg; | ||
| 4644 | |||
| 4645 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 4646 | |||
| 4647 | genlmsg_end(msg, hdr); | ||
| 4648 | err = genlmsg_reply(msg, info); | ||
| 4649 | goto out; | ||
| 4650 | |||
| 4651 | nla_put_failure: | ||
| 4652 | err = -ENOBUFS; | ||
| 4653 | free_msg: | ||
| 4654 | nlmsg_free(msg); | ||
| 4655 | out: | ||
| 4656 | cfg80211_unlock_rdev(rdev); | ||
| 4657 | dev_put(dev); | ||
| 4658 | unlock_rtnl: | ||
| 4659 | rtnl_unlock(); | ||
| 4660 | return err; | ||
| 4661 | } | ||
| 4662 | |||
| 4663 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | ||
| 4664 | { | ||
| 4665 | struct cfg80211_registered_device *rdev; | ||
| 4666 | struct wireless_dev *wdev; | ||
| 4667 | struct net_device *dev; | ||
| 4668 | u8 ps_state; | ||
| 4669 | bool state; | ||
| 4670 | int err; | ||
| 4671 | |||
| 4672 | if (!info->attrs[NL80211_ATTR_PS_STATE]) { | ||
| 4673 | err = -EINVAL; | ||
| 4674 | goto out; | ||
| 4675 | } | ||
| 4676 | |||
| 4677 | ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); | ||
| 4678 | |||
| 4679 | if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) { | ||
| 4680 | err = -EINVAL; | ||
| 4681 | goto out; | ||
| 4682 | } | ||
| 4683 | |||
| 4684 | rtnl_lock(); | ||
| 4685 | |||
| 4686 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4687 | if (err) | ||
| 4688 | goto unlock_rdev; | ||
| 4689 | |||
| 4690 | wdev = dev->ieee80211_ptr; | ||
| 4691 | |||
| 4692 | if (!rdev->ops->set_power_mgmt) { | ||
| 4693 | err = -EOPNOTSUPP; | ||
| 4694 | goto unlock_rdev; | ||
| 4695 | } | ||
| 4696 | |||
| 4697 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | ||
| 4698 | |||
| 4699 | if (state == wdev->ps) | ||
| 4700 | goto unlock_rdev; | ||
| 4701 | |||
| 4702 | wdev->ps = state; | ||
| 4703 | |||
| 4704 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, | ||
| 4705 | wdev->ps_timeout)) | ||
| 4706 | /* assume this means it's off */ | ||
| 4707 | wdev->ps = false; | ||
| 4708 | |||
| 4709 | unlock_rdev: | ||
| 4710 | cfg80211_unlock_rdev(rdev); | ||
| 4711 | dev_put(dev); | ||
| 4712 | rtnl_unlock(); | ||
| 4713 | |||
| 4714 | out: | ||
| 4715 | return err; | ||
| 4716 | } | ||
| 4717 | |||
| 4718 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | ||
| 4719 | { | ||
| 4720 | struct cfg80211_registered_device *rdev; | ||
| 4721 | enum nl80211_ps_state ps_state; | ||
| 4722 | struct wireless_dev *wdev; | ||
| 4723 | struct net_device *dev; | ||
| 4724 | struct sk_buff *msg; | ||
| 4725 | void *hdr; | ||
| 4726 | int err; | ||
| 4727 | |||
| 4728 | rtnl_lock(); | ||
| 4729 | |||
| 4730 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4731 | if (err) | ||
| 4732 | goto unlock_rtnl; | ||
| 4733 | |||
| 4734 | wdev = dev->ieee80211_ptr; | ||
| 4735 | |||
| 4736 | if (!rdev->ops->set_power_mgmt) { | ||
| 4737 | err = -EOPNOTSUPP; | ||
| 4738 | goto out; | ||
| 4739 | } | ||
| 4740 | |||
| 4741 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 4742 | if (!msg) { | ||
| 4743 | err = -ENOMEM; | ||
| 4744 | goto out; | ||
| 4745 | } | ||
| 4746 | |||
| 4747 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
| 4748 | NL80211_CMD_GET_POWER_SAVE); | ||
| 4749 | if (!hdr) { | ||
| 4750 | err = -ENOMEM; | ||
| 4751 | goto free_msg; | ||
| 4752 | } | ||
| 4753 | |||
| 4754 | if (wdev->ps) | ||
| 4755 | ps_state = NL80211_PS_ENABLED; | ||
| 4756 | else | ||
| 4757 | ps_state = NL80211_PS_DISABLED; | ||
| 4758 | |||
| 4759 | NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); | ||
| 4760 | |||
| 4761 | genlmsg_end(msg, hdr); | ||
| 4762 | err = genlmsg_reply(msg, info); | ||
| 4763 | goto out; | ||
| 4764 | |||
| 4765 | nla_put_failure: | ||
| 4766 | err = -ENOBUFS; | ||
| 4767 | |||
| 4768 | free_msg: | ||
| 4769 | nlmsg_free(msg); | ||
| 4770 | |||
| 4771 | out: | ||
| 4772 | cfg80211_unlock_rdev(rdev); | ||
| 4773 | dev_put(dev); | ||
| 4774 | |||
| 4775 | unlock_rtnl: | ||
| 4776 | rtnl_unlock(); | ||
| 4777 | |||
| 4778 | return err; | ||
| 4779 | } | ||
| 4780 | |||
| 4325 | static struct genl_ops nl80211_ops[] = { | 4781 | static struct genl_ops nl80211_ops[] = { |
| 4326 | { | 4782 | { |
| 4327 | .cmd = NL80211_CMD_GET_WIPHY, | 4783 | .cmd = NL80211_CMD_GET_WIPHY, |
| @@ -4584,8 +5040,50 @@ static struct genl_ops nl80211_ops[] = { | |||
| 4584 | .policy = nl80211_policy, | 5040 | .policy = nl80211_policy, |
| 4585 | .flags = GENL_ADMIN_PERM, | 5041 | .flags = GENL_ADMIN_PERM, |
| 4586 | }, | 5042 | }, |
| 4587 | 5043 | { | |
| 5044 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | ||
| 5045 | .doit = nl80211_remain_on_channel, | ||
| 5046 | .policy = nl80211_policy, | ||
| 5047 | .flags = GENL_ADMIN_PERM, | ||
| 5048 | }, | ||
| 5049 | { | ||
| 5050 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
| 5051 | .doit = nl80211_cancel_remain_on_channel, | ||
| 5052 | .policy = nl80211_policy, | ||
| 5053 | .flags = GENL_ADMIN_PERM, | ||
| 5054 | }, | ||
| 5055 | { | ||
| 5056 | .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, | ||
| 5057 | .doit = nl80211_set_tx_bitrate_mask, | ||
| 5058 | .policy = nl80211_policy, | ||
| 5059 | .flags = GENL_ADMIN_PERM, | ||
| 5060 | }, | ||
| 5061 | { | ||
| 5062 | .cmd = NL80211_CMD_REGISTER_ACTION, | ||
| 5063 | .doit = nl80211_register_action, | ||
| 5064 | .policy = nl80211_policy, | ||
| 5065 | .flags = GENL_ADMIN_PERM, | ||
| 5066 | }, | ||
| 5067 | { | ||
| 5068 | .cmd = NL80211_CMD_ACTION, | ||
| 5069 | .doit = nl80211_action, | ||
| 5070 | .policy = nl80211_policy, | ||
| 5071 | .flags = GENL_ADMIN_PERM, | ||
| 5072 | }, | ||
| 5073 | { | ||
| 5074 | .cmd = NL80211_CMD_SET_POWER_SAVE, | ||
| 5075 | .doit = nl80211_set_power_save, | ||
| 5076 | .policy = nl80211_policy, | ||
| 5077 | .flags = GENL_ADMIN_PERM, | ||
| 5078 | }, | ||
| 5079 | { | ||
| 5080 | .cmd = NL80211_CMD_GET_POWER_SAVE, | ||
| 5081 | .doit = nl80211_get_power_save, | ||
| 5082 | .policy = nl80211_policy, | ||
| 5083 | /* can be retrieved by unprivileged users */ | ||
| 5084 | }, | ||
| 4588 | }; | 5085 | }; |
| 5086 | |||
| 4589 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5087 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
| 4590 | .name = "mlme", | 5088 | .name = "mlme", |
| 4591 | }; | 5089 | }; |
| @@ -5173,6 +5671,193 @@ nla_put_failure: | |||
| 5173 | nlmsg_free(msg); | 5671 | nlmsg_free(msg); |
| 5174 | } | 5672 | } |
| 5175 | 5673 | ||
| 5674 | static void nl80211_send_remain_on_chan_event( | ||
| 5675 | int cmd, struct cfg80211_registered_device *rdev, | ||
| 5676 | struct net_device *netdev, u64 cookie, | ||
| 5677 | struct ieee80211_channel *chan, | ||
| 5678 | enum nl80211_channel_type channel_type, | ||
| 5679 | unsigned int duration, gfp_t gfp) | ||
| 5680 | { | ||
| 5681 | struct sk_buff *msg; | ||
| 5682 | void *hdr; | ||
| 5683 | |||
| 5684 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
| 5685 | if (!msg) | ||
| 5686 | return; | ||
| 5687 | |||
| 5688 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); | ||
| 5689 | if (!hdr) { | ||
| 5690 | nlmsg_free(msg); | ||
| 5691 | return; | ||
| 5692 | } | ||
| 5693 | |||
| 5694 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
| 5695 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
| 5696 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); | ||
| 5697 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); | ||
| 5698 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 5699 | |||
| 5700 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) | ||
| 5701 | NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); | ||
| 5702 | |||
| 5703 | if (genlmsg_end(msg, hdr) < 0) { | ||
| 5704 | nlmsg_free(msg); | ||
| 5705 | return; | ||
| 5706 | } | ||
| 5707 | |||
| 5708 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
| 5709 | nl80211_mlme_mcgrp.id, gfp); | ||
| 5710 | return; | ||
| 5711 | |||
| 5712 | nla_put_failure: | ||
| 5713 | genlmsg_cancel(msg, hdr); | ||
| 5714 | nlmsg_free(msg); | ||
| 5715 | } | ||
| 5716 | |||
| 5717 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
| 5718 | struct net_device *netdev, u64 cookie, | ||
| 5719 | struct ieee80211_channel *chan, | ||
| 5720 | enum nl80211_channel_type channel_type, | ||
| 5721 | unsigned int duration, gfp_t gfp) | ||
| 5722 | { | ||
| 5723 | nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, | ||
| 5724 | rdev, netdev, cookie, chan, | ||
| 5725 | channel_type, duration, gfp); | ||
| 5726 | } | ||
| 5727 | |||
| 5728 | void nl80211_send_remain_on_channel_cancel( | ||
| 5729 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
| 5730 | u64 cookie, struct ieee80211_channel *chan, | ||
| 5731 | enum nl80211_channel_type channel_type, gfp_t gfp) | ||
| 5732 | { | ||
| 5733 | nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
| 5734 | rdev, netdev, cookie, chan, | ||
| 5735 | channel_type, 0, gfp); | ||
| 5736 | } | ||
| 5737 | |||
| 5738 | void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | ||
| 5739 | struct net_device *dev, const u8 *mac_addr, | ||
| 5740 | struct station_info *sinfo, gfp_t gfp) | ||
| 5741 | { | ||
| 5742 | struct sk_buff *msg; | ||
| 5743 | |||
| 5744 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
| 5745 | if (!msg) | ||
| 5746 | return; | ||
| 5747 | |||
| 5748 | if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { | ||
| 5749 | nlmsg_free(msg); | ||
| 5750 | return; | ||
| 5751 | } | ||
| 5752 | |||
| 5753 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
| 5754 | nl80211_mlme_mcgrp.id, gfp); | ||
| 5755 | } | ||
| 5756 | |||
| 5757 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
| 5758 | struct net_device *netdev, u32 nlpid, | ||
| 5759 | int freq, const u8 *buf, size_t len, gfp_t gfp) | ||
| 5760 | { | ||
| 5761 | struct sk_buff *msg; | ||
| 5762 | void *hdr; | ||
| 5763 | int err; | ||
| 5764 | |||
| 5765 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
| 5766 | if (!msg) | ||
| 5767 | return -ENOMEM; | ||
| 5768 | |||
| 5769 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION); | ||
| 5770 | if (!hdr) { | ||
| 5771 | nlmsg_free(msg); | ||
| 5772 | return -ENOMEM; | ||
| 5773 | } | ||
| 5774 | |||
| 5775 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
| 5776 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
| 5777 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); | ||
| 5778 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
| 5779 | |||
| 5780 | err = genlmsg_end(msg, hdr); | ||
| 5781 | if (err < 0) { | ||
| 5782 | nlmsg_free(msg); | ||
| 5783 | return err; | ||
| 5784 | } | ||
| 5785 | |||
| 5786 | err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); | ||
| 5787 | if (err < 0) | ||
| 5788 | return err; | ||
| 5789 | return 0; | ||
| 5790 | |||
| 5791 | nla_put_failure: | ||
| 5792 | genlmsg_cancel(msg, hdr); | ||
| 5793 | nlmsg_free(msg); | ||
| 5794 | return -ENOBUFS; | ||
| 5795 | } | ||
| 5796 | |||
| 5797 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
| 5798 | struct net_device *netdev, u64 cookie, | ||
| 5799 | const u8 *buf, size_t len, bool ack, | ||
| 5800 | gfp_t gfp) | ||
| 5801 | { | ||
| 5802 | struct sk_buff *msg; | ||
| 5803 | void *hdr; | ||
| 5804 | |||
| 5805 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
| 5806 | if (!msg) | ||
| 5807 | return; | ||
| 5808 | |||
| 5809 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS); | ||
| 5810 | if (!hdr) { | ||
| 5811 | nlmsg_free(msg); | ||
| 5812 | return; | ||
| 5813 | } | ||
| 5814 | |||
| 5815 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
| 5816 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
| 5817 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
| 5818 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 5819 | if (ack) | ||
| 5820 | NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); | ||
| 5821 | |||
| 5822 | if (genlmsg_end(msg, hdr) < 0) { | ||
| 5823 | nlmsg_free(msg); | ||
| 5824 | return; | ||
| 5825 | } | ||
| 5826 | |||
| 5827 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
| 5828 | return; | ||
| 5829 | |||
| 5830 | nla_put_failure: | ||
| 5831 | genlmsg_cancel(msg, hdr); | ||
| 5832 | nlmsg_free(msg); | ||
| 5833 | } | ||
| 5834 | |||
| 5835 | static int nl80211_netlink_notify(struct notifier_block * nb, | ||
| 5836 | unsigned long state, | ||
| 5837 | void *_notify) | ||
| 5838 | { | ||
| 5839 | struct netlink_notify *notify = _notify; | ||
| 5840 | struct cfg80211_registered_device *rdev; | ||
| 5841 | struct wireless_dev *wdev; | ||
| 5842 | |||
| 5843 | if (state != NETLINK_URELEASE) | ||
| 5844 | return NOTIFY_DONE; | ||
| 5845 | |||
| 5846 | rcu_read_lock(); | ||
| 5847 | |||
| 5848 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) | ||
| 5849 | list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) | ||
| 5850 | cfg80211_mlme_unregister_actions(wdev, notify->pid); | ||
| 5851 | |||
| 5852 | rcu_read_unlock(); | ||
| 5853 | |||
| 5854 | return NOTIFY_DONE; | ||
| 5855 | } | ||
| 5856 | |||
| 5857 | static struct notifier_block nl80211_netlink_notifier = { | ||
| 5858 | .notifier_call = nl80211_netlink_notify, | ||
| 5859 | }; | ||
| 5860 | |||
| 5176 | /* initialisation/exit functions */ | 5861 | /* initialisation/exit functions */ |
| 5177 | 5862 | ||
| 5178 | int nl80211_init(void) | 5863 | int nl80211_init(void) |
| @@ -5206,6 +5891,10 @@ int nl80211_init(void) | |||
| 5206 | goto err_out; | 5891 | goto err_out; |
| 5207 | #endif | 5892 | #endif |
| 5208 | 5893 | ||
| 5894 | err = netlink_register_notifier(&nl80211_netlink_notifier); | ||
| 5895 | if (err) | ||
| 5896 | goto err_out; | ||
| 5897 | |||
| 5209 | return 0; | 5898 | return 0; |
| 5210 | err_out: | 5899 | err_out: |
| 5211 | genl_unregister_family(&nl80211_fam); | 5900 | genl_unregister_family(&nl80211_fam); |
| @@ -5214,5 +5903,6 @@ int nl80211_init(void) | |||
| 5214 | 5903 | ||
| 5215 | void nl80211_exit(void) | 5904 | void nl80211_exit(void) |
| 5216 | { | 5905 | { |
| 5906 | netlink_unregister_notifier(&nl80211_netlink_notifier); | ||
| 5217 | genl_unregister_family(&nl80211_fam); | 5907 | genl_unregister_family(&nl80211_fam); |
| 5218 | } | 5908 | } |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 44cc2a76a1b0..4ca511102c6c 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
| @@ -59,4 +59,27 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, | |||
| 59 | struct net_device *netdev, const u8 *bssid, | 59 | struct net_device *netdev, const u8 *bssid, |
| 60 | gfp_t gfp); | 60 | gfp_t gfp); |
| 61 | 61 | ||
| 62 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
| 63 | struct net_device *netdev, | ||
| 64 | u64 cookie, | ||
| 65 | struct ieee80211_channel *chan, | ||
| 66 | enum nl80211_channel_type channel_type, | ||
| 67 | unsigned int duration, gfp_t gfp); | ||
| 68 | void nl80211_send_remain_on_channel_cancel( | ||
| 69 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
| 70 | u64 cookie, struct ieee80211_channel *chan, | ||
| 71 | enum nl80211_channel_type channel_type, gfp_t gfp); | ||
| 72 | |||
| 73 | void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | ||
| 74 | struct net_device *dev, const u8 *mac_addr, | ||
| 75 | struct station_info *sinfo, gfp_t gfp); | ||
| 76 | |||
| 77 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
| 78 | struct net_device *netdev, u32 nlpid, int freq, | ||
| 79 | const u8 *buf, size_t len, gfp_t gfp); | ||
| 80 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
| 81 | struct net_device *netdev, u64 cookie, | ||
| 82 | const u8 *buf, size_t len, bool ack, | ||
| 83 | gfp_t gfp); | ||
| 84 | |||
| 62 | #endif /* __NET_WIRELESS_NL80211_H */ | 85 | #endif /* __NET_WIRELESS_NL80211_H */ |
diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c index f591871a7b4f..1332c445d1c7 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c | |||
| @@ -2,6 +2,16 @@ | |||
| 2 | * Radiotap parser | 2 | * Radiotap parser |
| 3 | * | 3 | * |
| 4 | * Copyright 2007 Andy Green <andy@warmcat.com> | 4 | * Copyright 2007 Andy Green <andy@warmcat.com> |
| 5 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * Alternatively, this software may be distributed under the terms of BSD | ||
| 12 | * license. | ||
| 13 | * | ||
| 14 | * See COPYING for more details. | ||
| 5 | */ | 15 | */ |
| 6 | 16 | ||
| 7 | #include <net/cfg80211.h> | 17 | #include <net/cfg80211.h> |
| @@ -10,6 +20,35 @@ | |||
| 10 | 20 | ||
| 11 | /* function prototypes and related defs are in include/net/cfg80211.h */ | 21 | /* function prototypes and related defs are in include/net/cfg80211.h */ |
| 12 | 22 | ||
| 23 | static const struct radiotap_align_size rtap_namespace_sizes[] = { | ||
| 24 | [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, | ||
| 25 | [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, | ||
| 26 | [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, | ||
| 27 | [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, | ||
| 28 | [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, | ||
| 29 | [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, | ||
| 30 | [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, | ||
| 31 | [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, | ||
| 32 | [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, | ||
| 33 | [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, | ||
| 34 | [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, | ||
| 35 | [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, | ||
| 36 | [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, | ||
| 37 | [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, | ||
| 38 | [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, | ||
| 39 | [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, | ||
| 40 | [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, | ||
| 41 | [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, | ||
| 42 | /* | ||
| 43 | * add more here as they are defined in radiotap.h | ||
| 44 | */ | ||
| 45 | }; | ||
| 46 | |||
| 47 | static const struct ieee80211_radiotap_namespace radiotap_ns = { | ||
| 48 | .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]), | ||
| 49 | .align_size = rtap_namespace_sizes, | ||
| 50 | }; | ||
| 51 | |||
| 13 | /** | 52 | /** |
| 14 | * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization | 53 | * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization |
| 15 | * @iterator: radiotap_iterator to initialize | 54 | * @iterator: radiotap_iterator to initialize |
| @@ -50,9 +89,9 @@ | |||
| 50 | */ | 89 | */ |
| 51 | 90 | ||
| 52 | int ieee80211_radiotap_iterator_init( | 91 | int ieee80211_radiotap_iterator_init( |
| 53 | struct ieee80211_radiotap_iterator *iterator, | 92 | struct ieee80211_radiotap_iterator *iterator, |
| 54 | struct ieee80211_radiotap_header *radiotap_header, | 93 | struct ieee80211_radiotap_header *radiotap_header, |
| 55 | int max_length) | 94 | int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) |
| 56 | { | 95 | { |
| 57 | /* Linux only supports version 0 radiotap format */ | 96 | /* Linux only supports version 0 radiotap format */ |
| 58 | if (radiotap_header->it_version) | 97 | if (radiotap_header->it_version) |
| @@ -62,19 +101,24 @@ int ieee80211_radiotap_iterator_init( | |||
| 62 | if (max_length < get_unaligned_le16(&radiotap_header->it_len)) | 101 | if (max_length < get_unaligned_le16(&radiotap_header->it_len)) |
| 63 | return -EINVAL; | 102 | return -EINVAL; |
| 64 | 103 | ||
| 65 | iterator->rtheader = radiotap_header; | 104 | iterator->_rtheader = radiotap_header; |
| 66 | iterator->max_length = get_unaligned_le16(&radiotap_header->it_len); | 105 | iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); |
| 67 | iterator->arg_index = 0; | 106 | iterator->_arg_index = 0; |
| 68 | iterator->bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); | 107 | iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); |
| 69 | iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); | 108 | iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); |
| 70 | iterator->this_arg = NULL; | 109 | iterator->_reset_on_ext = 0; |
| 110 | iterator->_next_bitmap = &radiotap_header->it_present; | ||
| 111 | iterator->_next_bitmap++; | ||
| 112 | iterator->_vns = vns; | ||
| 113 | iterator->current_namespace = &radiotap_ns; | ||
| 114 | iterator->is_radiotap_ns = 1; | ||
| 71 | 115 | ||
| 72 | /* find payload start allowing for extended bitmap(s) */ | 116 | /* find payload start allowing for extended bitmap(s) */ |
| 73 | 117 | ||
| 74 | if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { | 118 | if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { |
| 75 | while (get_unaligned_le32(iterator->arg) & | 119 | while (get_unaligned_le32(iterator->_arg) & |
| 76 | (1 << IEEE80211_RADIOTAP_EXT)) { | 120 | (1 << IEEE80211_RADIOTAP_EXT)) { |
| 77 | iterator->arg += sizeof(u32); | 121 | iterator->_arg += sizeof(uint32_t); |
| 78 | 122 | ||
| 79 | /* | 123 | /* |
| 80 | * check for insanity where the present bitmaps | 124 | * check for insanity where the present bitmaps |
| @@ -82,12 +126,13 @@ int ieee80211_radiotap_iterator_init( | |||
| 82 | * stated radiotap header length | 126 | * stated radiotap header length |
| 83 | */ | 127 | */ |
| 84 | 128 | ||
| 85 | if (((ulong)iterator->arg - | 129 | if ((unsigned long)iterator->_arg - |
| 86 | (ulong)iterator->rtheader) > iterator->max_length) | 130 | (unsigned long)iterator->_rtheader > |
| 131 | (unsigned long)iterator->_max_length) | ||
| 87 | return -EINVAL; | 132 | return -EINVAL; |
| 88 | } | 133 | } |
| 89 | 134 | ||
| 90 | iterator->arg += sizeof(u32); | 135 | iterator->_arg += sizeof(uint32_t); |
| 91 | 136 | ||
| 92 | /* | 137 | /* |
| 93 | * no need to check again for blowing past stated radiotap | 138 | * no need to check again for blowing past stated radiotap |
| @@ -96,12 +141,36 @@ int ieee80211_radiotap_iterator_init( | |||
| 96 | */ | 141 | */ |
| 97 | } | 142 | } |
| 98 | 143 | ||
| 144 | iterator->this_arg = iterator->_arg; | ||
| 145 | |||
| 99 | /* we are all initialized happily */ | 146 | /* we are all initialized happily */ |
| 100 | 147 | ||
| 101 | return 0; | 148 | return 0; |
| 102 | } | 149 | } |
| 103 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); | 150 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); |
| 104 | 151 | ||
| 152 | static void find_ns(struct ieee80211_radiotap_iterator *iterator, | ||
| 153 | uint32_t oui, uint8_t subns) | ||
| 154 | { | ||
| 155 | int i; | ||
| 156 | |||
| 157 | iterator->current_namespace = NULL; | ||
| 158 | |||
| 159 | if (!iterator->_vns) | ||
| 160 | return; | ||
| 161 | |||
| 162 | for (i = 0; i < iterator->_vns->n_ns; i++) { | ||
| 163 | if (iterator->_vns->ns[i].oui != oui) | ||
| 164 | continue; | ||
| 165 | if (iterator->_vns->ns[i].subns != subns) | ||
| 166 | continue; | ||
| 167 | |||
| 168 | iterator->current_namespace = &iterator->_vns->ns[i]; | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | |||
| 105 | 174 | ||
| 106 | /** | 175 | /** |
| 107 | * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg | 176 | * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg |
| @@ -127,99 +196,80 @@ EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); | |||
| 127 | */ | 196 | */ |
| 128 | 197 | ||
| 129 | int ieee80211_radiotap_iterator_next( | 198 | int ieee80211_radiotap_iterator_next( |
| 130 | struct ieee80211_radiotap_iterator *iterator) | 199 | struct ieee80211_radiotap_iterator *iterator) |
| 131 | { | 200 | { |
| 132 | 201 | while (1) { | |
| 133 | /* | ||
| 134 | * small length lookup table for all radiotap types we heard of | ||
| 135 | * starting from b0 in the bitmap, so we can walk the payload | ||
| 136 | * area of the radiotap header | ||
| 137 | * | ||
| 138 | * There is a requirement to pad args, so that args | ||
| 139 | * of a given length must begin at a boundary of that length | ||
| 140 | * -- but note that compound args are allowed (eg, 2 x u16 | ||
| 141 | * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not | ||
| 142 | * a reliable indicator of alignment requirement. | ||
| 143 | * | ||
| 144 | * upper nybble: content alignment for arg | ||
| 145 | * lower nybble: content length for arg | ||
| 146 | */ | ||
| 147 | |||
| 148 | static const u8 rt_sizes[] = { | ||
| 149 | [IEEE80211_RADIOTAP_TSFT] = 0x88, | ||
| 150 | [IEEE80211_RADIOTAP_FLAGS] = 0x11, | ||
| 151 | [IEEE80211_RADIOTAP_RATE] = 0x11, | ||
| 152 | [IEEE80211_RADIOTAP_CHANNEL] = 0x24, | ||
| 153 | [IEEE80211_RADIOTAP_FHSS] = 0x22, | ||
| 154 | [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, | ||
| 155 | [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, | ||
| 156 | [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, | ||
| 157 | [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, | ||
| 158 | [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, | ||
| 159 | [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, | ||
| 160 | [IEEE80211_RADIOTAP_ANTENNA] = 0x11, | ||
| 161 | [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, | ||
| 162 | [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, | ||
| 163 | [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, | ||
| 164 | [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, | ||
| 165 | [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, | ||
| 166 | [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, | ||
| 167 | /* | ||
| 168 | * add more here as they are defined in | ||
| 169 | * include/net/ieee80211_radiotap.h | ||
| 170 | */ | ||
| 171 | }; | ||
| 172 | |||
| 173 | /* | ||
| 174 | * for every radiotap entry we can at | ||
| 175 | * least skip (by knowing the length)... | ||
| 176 | */ | ||
| 177 | |||
| 178 | while (iterator->arg_index < sizeof(rt_sizes)) { | ||
| 179 | int hit = 0; | 202 | int hit = 0; |
| 180 | int pad; | 203 | int pad, align, size, subns, vnslen; |
| 204 | uint32_t oui; | ||
| 181 | 205 | ||
| 182 | if (!(iterator->bitmap_shifter & 1)) | 206 | /* if no more EXT bits, that's it */ |
| 207 | if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && | ||
| 208 | !(iterator->_bitmap_shifter & 1)) | ||
| 209 | return -ENOENT; | ||
| 210 | |||
| 211 | if (!(iterator->_bitmap_shifter & 1)) | ||
| 183 | goto next_entry; /* arg not present */ | 212 | goto next_entry; /* arg not present */ |
| 184 | 213 | ||
| 214 | /* get alignment/size of data */ | ||
| 215 | switch (iterator->_arg_index % 32) { | ||
| 216 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | ||
| 217 | case IEEE80211_RADIOTAP_EXT: | ||
| 218 | align = 1; | ||
| 219 | size = 0; | ||
| 220 | break; | ||
| 221 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: | ||
| 222 | align = 2; | ||
| 223 | size = 6; | ||
| 224 | break; | ||
| 225 | default: | ||
| 226 | if (!iterator->current_namespace || | ||
| 227 | iterator->_arg_index >= iterator->current_namespace->n_bits) { | ||
| 228 | if (iterator->current_namespace == &radiotap_ns) | ||
| 229 | return -ENOENT; | ||
| 230 | align = 0; | ||
| 231 | } else { | ||
| 232 | align = iterator->current_namespace->align_size[iterator->_arg_index].align; | ||
| 233 | size = iterator->current_namespace->align_size[iterator->_arg_index].size; | ||
| 234 | } | ||
| 235 | if (!align) { | ||
| 236 | /* skip all subsequent data */ | ||
| 237 | iterator->_arg = iterator->_next_ns_data; | ||
| 238 | /* give up on this namespace */ | ||
| 239 | iterator->current_namespace = NULL; | ||
| 240 | goto next_entry; | ||
| 241 | } | ||
| 242 | break; | ||
| 243 | } | ||
| 244 | |||
| 185 | /* | 245 | /* |
| 186 | * arg is present, account for alignment padding | 246 | * arg is present, account for alignment padding |
| 187 | * 8-bit args can be at any alignment | ||
| 188 | * 16-bit args must start on 16-bit boundary | ||
| 189 | * 32-bit args must start on 32-bit boundary | ||
| 190 | * 64-bit args must start on 64-bit boundary | ||
| 191 | * | 247 | * |
| 192 | * note that total arg size can differ from alignment of | 248 | * Note that these alignments are relative to the start |
| 193 | * elements inside arg, so we use upper nybble of length | 249 | * of the radiotap header. There is no guarantee |
| 194 | * table to base alignment on | ||
| 195 | * | ||
| 196 | * also note: these alignments are ** relative to the | ||
| 197 | * start of the radiotap header **. There is no guarantee | ||
| 198 | * that the radiotap header itself is aligned on any | 250 | * that the radiotap header itself is aligned on any |
| 199 | * kind of boundary. | 251 | * kind of boundary. |
| 200 | * | 252 | * |
| 201 | * the above is why get_unaligned() is used to dereference | 253 | * The above is why get_unaligned() is used to dereference |
| 202 | * multibyte elements from the radiotap area | 254 | * multibyte elements from the radiotap area. |
| 203 | */ | 255 | */ |
| 204 | 256 | ||
| 205 | pad = (((ulong)iterator->arg) - | 257 | pad = ((unsigned long)iterator->_arg - |
| 206 | ((ulong)iterator->rtheader)) & | 258 | (unsigned long)iterator->_rtheader) & (align - 1); |
| 207 | ((rt_sizes[iterator->arg_index] >> 4) - 1); | ||
| 208 | 259 | ||
| 209 | if (pad) | 260 | if (pad) |
| 210 | iterator->arg += | 261 | iterator->_arg += align - pad; |
| 211 | (rt_sizes[iterator->arg_index] >> 4) - pad; | ||
| 212 | 262 | ||
| 213 | /* | 263 | /* |
| 214 | * this is what we will return to user, but we need to | 264 | * this is what we will return to user, but we need to |
| 215 | * move on first so next call has something fresh to test | 265 | * move on first so next call has something fresh to test |
| 216 | */ | 266 | */ |
| 217 | iterator->this_arg_index = iterator->arg_index; | 267 | iterator->this_arg_index = iterator->_arg_index; |
| 218 | iterator->this_arg = iterator->arg; | 268 | iterator->this_arg = iterator->_arg; |
| 219 | hit = 1; | 269 | iterator->this_arg_size = size; |
| 220 | 270 | ||
| 221 | /* internally move on the size of this arg */ | 271 | /* internally move on the size of this arg */ |
| 222 | iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; | 272 | iterator->_arg += size; |
| 223 | 273 | ||
| 224 | /* | 274 | /* |
| 225 | * check for insanity where we are given a bitmap that | 275 | * check for insanity where we are given a bitmap that |
| @@ -228,32 +278,73 @@ int ieee80211_radiotap_iterator_next( | |||
| 228 | * max_length on the last arg, never exceeding it. | 278 | * max_length on the last arg, never exceeding it. |
| 229 | */ | 279 | */ |
| 230 | 280 | ||
| 231 | if (((ulong)iterator->arg - (ulong)iterator->rtheader) > | 281 | if ((unsigned long)iterator->_arg - |
| 232 | iterator->max_length) | 282 | (unsigned long)iterator->_rtheader > |
| 283 | (unsigned long)iterator->_max_length) | ||
| 233 | return -EINVAL; | 284 | return -EINVAL; |
| 234 | 285 | ||
| 235 | next_entry: | 286 | /* these special ones are valid in each bitmap word */ |
| 236 | iterator->arg_index++; | 287 | switch (iterator->_arg_index % 32) { |
| 237 | if (unlikely((iterator->arg_index & 31) == 0)) { | 288 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: |
| 238 | /* completed current u32 bitmap */ | 289 | iterator->_bitmap_shifter >>= 1; |
| 239 | if (iterator->bitmap_shifter & 1) { | 290 | iterator->_arg_index++; |
| 240 | /* b31 was set, there is more */ | 291 | |
| 241 | /* move to next u32 bitmap */ | 292 | iterator->_reset_on_ext = 1; |
| 242 | iterator->bitmap_shifter = | 293 | |
| 243 | get_unaligned_le32(iterator->next_bitmap); | 294 | vnslen = get_unaligned_le16(iterator->this_arg + 4); |
| 244 | iterator->next_bitmap++; | 295 | iterator->_next_ns_data = iterator->_arg + vnslen; |
| 245 | } else | 296 | oui = (*iterator->this_arg << 16) | |
| 246 | /* no more bitmaps: end */ | 297 | (*(iterator->this_arg + 1) << 8) | |
| 247 | iterator->arg_index = sizeof(rt_sizes); | 298 | *(iterator->this_arg + 2); |
| 248 | } else /* just try the next bit */ | 299 | subns = *(iterator->this_arg + 3); |
| 249 | iterator->bitmap_shifter >>= 1; | 300 | |
| 301 | find_ns(iterator, oui, subns); | ||
| 302 | |||
| 303 | iterator->is_radiotap_ns = 0; | ||
| 304 | /* allow parsers to show this information */ | ||
| 305 | iterator->this_arg_index = | ||
| 306 | IEEE80211_RADIOTAP_VENDOR_NAMESPACE; | ||
| 307 | iterator->this_arg_size += vnslen; | ||
| 308 | if ((unsigned long)iterator->this_arg + | ||
| 309 | iterator->this_arg_size - | ||
| 310 | (unsigned long)iterator->_rtheader > | ||
| 311 | (unsigned long)(unsigned long)iterator->_max_length) | ||
| 312 | return -EINVAL; | ||
| 313 | hit = 1; | ||
| 314 | break; | ||
| 315 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | ||
| 316 | iterator->_bitmap_shifter >>= 1; | ||
| 317 | iterator->_arg_index++; | ||
| 318 | |||
| 319 | iterator->_reset_on_ext = 1; | ||
| 320 | iterator->current_namespace = &radiotap_ns; | ||
| 321 | iterator->is_radiotap_ns = 1; | ||
| 322 | break; | ||
| 323 | case IEEE80211_RADIOTAP_EXT: | ||
| 324 | /* | ||
| 325 | * bit 31 was set, there is more | ||
| 326 | * -- move to next u32 bitmap | ||
| 327 | */ | ||
| 328 | iterator->_bitmap_shifter = | ||
| 329 | get_unaligned_le32(iterator->_next_bitmap); | ||
| 330 | iterator->_next_bitmap++; | ||
| 331 | if (iterator->_reset_on_ext) | ||
| 332 | iterator->_arg_index = 0; | ||
| 333 | else | ||
| 334 | iterator->_arg_index++; | ||
| 335 | iterator->_reset_on_ext = 0; | ||
| 336 | break; | ||
| 337 | default: | ||
| 338 | /* we've got a hit! */ | ||
| 339 | hit = 1; | ||
| 340 | next_entry: | ||
| 341 | iterator->_bitmap_shifter >>= 1; | ||
| 342 | iterator->_arg_index++; | ||
| 343 | } | ||
| 250 | 344 | ||
| 251 | /* if we found a valid arg earlier, return it now */ | 345 | /* if we found a valid arg earlier, return it now */ |
| 252 | if (hit) | 346 | if (hit) |
| 253 | return 0; | 347 | return 0; |
| 254 | } | 348 | } |
| 255 | |||
| 256 | /* we don't know how to handle any more args, we're done */ | ||
| 257 | return -ENOENT; | ||
| 258 | } | 349 | } |
| 259 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); | 350 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7a0754c92df4..ed89c59bb431 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
| @@ -40,8 +40,18 @@ | |||
| 40 | #include <net/cfg80211.h> | 40 | #include <net/cfg80211.h> |
| 41 | #include "core.h" | 41 | #include "core.h" |
| 42 | #include "reg.h" | 42 | #include "reg.h" |
| 43 | #include "regdb.h" | ||
| 43 | #include "nl80211.h" | 44 | #include "nl80211.h" |
| 44 | 45 | ||
| 46 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
| 47 | #define REG_DBG_PRINT(format, args...) \ | ||
| 48 | do { \ | ||
| 49 | printk(KERN_DEBUG format , ## args); \ | ||
| 50 | } while (0) | ||
| 51 | #else | ||
| 52 | #define REG_DBG_PRINT(args...) | ||
| 53 | #endif | ||
| 54 | |||
| 45 | /* Receipt of information from last regulatory request */ | 55 | /* Receipt of information from last regulatory request */ |
| 46 | static struct regulatory_request *last_request; | 56 | static struct regulatory_request *last_request; |
| 47 | 57 | ||
| @@ -124,82 +134,11 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = | |||
| 124 | &world_regdom; | 134 | &world_regdom; |
| 125 | 135 | ||
| 126 | static char *ieee80211_regdom = "00"; | 136 | static char *ieee80211_regdom = "00"; |
| 137 | static char user_alpha2[2]; | ||
| 127 | 138 | ||
| 128 | module_param(ieee80211_regdom, charp, 0444); | 139 | module_param(ieee80211_regdom, charp, 0444); |
| 129 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 140 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
| 130 | 141 | ||
| 131 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
| 132 | /* | ||
| 133 | * We assume 40 MHz bandwidth for the old regulatory work. | ||
| 134 | * We make emphasis we are using the exact same frequencies | ||
| 135 | * as before | ||
| 136 | */ | ||
| 137 | |||
| 138 | static const struct ieee80211_regdomain us_regdom = { | ||
| 139 | .n_reg_rules = 6, | ||
| 140 | .alpha2 = "US", | ||
| 141 | .reg_rules = { | ||
| 142 | /* IEEE 802.11b/g, channels 1..11 */ | ||
| 143 | REG_RULE(2412-10, 2462+10, 40, 6, 27, 0), | ||
| 144 | /* IEEE 802.11a, channel 36..48 */ | ||
| 145 | REG_RULE(5180-10, 5240+10, 40, 6, 17, 0), | ||
| 146 | /* IEEE 802.11a, channels 48..64 */ | ||
| 147 | REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 148 | /* IEEE 802.11a, channels 100..124 */ | ||
| 149 | REG_RULE(5500-10, 5590+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 150 | /* IEEE 802.11a, channels 132..144 */ | ||
| 151 | REG_RULE(5660-10, 5700+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 152 | /* IEEE 802.11a, channels 149..165, outdoor */ | ||
| 153 | REG_RULE(5745-10, 5825+10, 40, 6, 30, 0), | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | static const struct ieee80211_regdomain jp_regdom = { | ||
| 158 | .n_reg_rules = 6, | ||
| 159 | .alpha2 = "JP", | ||
| 160 | .reg_rules = { | ||
| 161 | /* IEEE 802.11b/g, channels 1..11 */ | ||
| 162 | REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), | ||
| 163 | /* IEEE 802.11b/g, channels 12..13 */ | ||
| 164 | REG_RULE(2467-10, 2472+10, 20, 6, 20, 0), | ||
| 165 | /* IEEE 802.11b/g, channel 14 */ | ||
| 166 | REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_NO_OFDM), | ||
| 167 | /* IEEE 802.11a, channels 36..48 */ | ||
| 168 | REG_RULE(5180-10, 5240+10, 40, 6, 20, 0), | ||
| 169 | /* IEEE 802.11a, channels 52..64 */ | ||
| 170 | REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 171 | /* IEEE 802.11a, channels 100..144 */ | ||
| 172 | REG_RULE(5500-10, 5700+10, 40, 6, 23, NL80211_RRF_DFS), | ||
| 173 | } | ||
| 174 | }; | ||
| 175 | |||
| 176 | static const struct ieee80211_regdomain *static_regdom(char *alpha2) | ||
| 177 | { | ||
| 178 | if (alpha2[0] == 'U' && alpha2[1] == 'S') | ||
| 179 | return &us_regdom; | ||
| 180 | if (alpha2[0] == 'J' && alpha2[1] == 'P') | ||
| 181 | return &jp_regdom; | ||
| 182 | /* Use world roaming rules for "EU", since it was a pseudo | ||
| 183 | domain anyway... */ | ||
| 184 | if (alpha2[0] == 'E' && alpha2[1] == 'U') | ||
| 185 | return &world_regdom; | ||
| 186 | /* Default, world roaming rules */ | ||
| 187 | return &world_regdom; | ||
| 188 | } | ||
| 189 | |||
| 190 | static bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
| 191 | { | ||
| 192 | if (rd == &us_regdom || rd == &jp_regdom || rd == &world_regdom) | ||
| 193 | return true; | ||
| 194 | return false; | ||
| 195 | } | ||
| 196 | #else | ||
| 197 | static inline bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
| 198 | { | ||
| 199 | return false; | ||
| 200 | } | ||
| 201 | #endif | ||
| 202 | |||
| 203 | static void reset_regdomains(void) | 142 | static void reset_regdomains(void) |
| 204 | { | 143 | { |
| 205 | /* avoid freeing static information or freeing something twice */ | 144 | /* avoid freeing static information or freeing something twice */ |
| @@ -209,8 +148,6 @@ static void reset_regdomains(void) | |||
| 209 | cfg80211_world_regdom = NULL; | 148 | cfg80211_world_regdom = NULL; |
| 210 | if (cfg80211_regdomain == &world_regdom) | 149 | if (cfg80211_regdomain == &world_regdom) |
| 211 | cfg80211_regdomain = NULL; | 150 | cfg80211_regdomain = NULL; |
| 212 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
| 213 | cfg80211_regdomain = NULL; | ||
| 214 | 151 | ||
| 215 | kfree(cfg80211_regdomain); | 152 | kfree(cfg80211_regdomain); |
| 216 | kfree(cfg80211_world_regdom); | 153 | kfree(cfg80211_world_regdom); |
| @@ -316,6 +253,27 @@ static bool regdom_changes(const char *alpha2) | |||
| 316 | return true; | 253 | return true; |
| 317 | } | 254 | } |
| 318 | 255 | ||
| 256 | /* | ||
| 257 | * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets | ||
| 258 | * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER | ||
| 259 | * has ever been issued. | ||
| 260 | */ | ||
| 261 | static bool is_user_regdom_saved(void) | ||
| 262 | { | ||
| 263 | if (user_alpha2[0] == '9' && user_alpha2[1] == '7') | ||
| 264 | return false; | ||
| 265 | |||
| 266 | /* This would indicate a mistake on the design */ | ||
| 267 | if (WARN((!is_world_regdom(user_alpha2) && | ||
| 268 | !is_an_alpha2(user_alpha2)), | ||
| 269 | "Unexpected user alpha2: %c%c\n", | ||
| 270 | user_alpha2[0], | ||
| 271 | user_alpha2[1])) | ||
| 272 | return false; | ||
| 273 | |||
| 274 | return true; | ||
| 275 | } | ||
| 276 | |||
| 319 | /** | 277 | /** |
| 320 | * country_ie_integrity_changes - tells us if the country IE has changed | 278 | * country_ie_integrity_changes - tells us if the country IE has changed |
| 321 | * @checksum: checksum of country IE of fields we are interested in | 279 | * @checksum: checksum of country IE of fields we are interested in |
| @@ -335,6 +293,98 @@ static bool country_ie_integrity_changes(u32 checksum) | |||
| 335 | return false; | 293 | return false; |
| 336 | } | 294 | } |
| 337 | 295 | ||
| 296 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
| 297 | const struct ieee80211_regdomain *src_regd) | ||
| 298 | { | ||
| 299 | struct ieee80211_regdomain *regd; | ||
| 300 | int size_of_regd = 0; | ||
| 301 | unsigned int i; | ||
| 302 | |||
| 303 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
| 304 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
| 305 | |||
| 306 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
| 307 | if (!regd) | ||
| 308 | return -ENOMEM; | ||
| 309 | |||
| 310 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
| 311 | |||
| 312 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
| 313 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
| 314 | sizeof(struct ieee80211_reg_rule)); | ||
| 315 | |||
| 316 | *dst_regd = regd; | ||
| 317 | return 0; | ||
| 318 | } | ||
| 319 | |||
| 320 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB | ||
| 321 | struct reg_regdb_search_request { | ||
| 322 | char alpha2[2]; | ||
| 323 | struct list_head list; | ||
| 324 | }; | ||
| 325 | |||
| 326 | static LIST_HEAD(reg_regdb_search_list); | ||
| 327 | static DEFINE_SPINLOCK(reg_regdb_search_lock); | ||
| 328 | |||
| 329 | static void reg_regdb_search(struct work_struct *work) | ||
| 330 | { | ||
| 331 | struct reg_regdb_search_request *request; | ||
| 332 | const struct ieee80211_regdomain *curdom, *regdom; | ||
| 333 | int i, r; | ||
| 334 | |||
| 335 | spin_lock(®_regdb_search_lock); | ||
| 336 | while (!list_empty(®_regdb_search_list)) { | ||
| 337 | request = list_first_entry(®_regdb_search_list, | ||
| 338 | struct reg_regdb_search_request, | ||
| 339 | list); | ||
| 340 | list_del(&request->list); | ||
| 341 | |||
| 342 | for (i=0; i<reg_regdb_size; i++) { | ||
| 343 | curdom = reg_regdb[i]; | ||
| 344 | |||
| 345 | if (!memcmp(request->alpha2, curdom->alpha2, 2)) { | ||
| 346 | r = reg_copy_regd(®dom, curdom); | ||
| 347 | if (r) | ||
| 348 | break; | ||
| 349 | spin_unlock(®_regdb_search_lock); | ||
| 350 | mutex_lock(&cfg80211_mutex); | ||
| 351 | set_regdom(regdom); | ||
| 352 | mutex_unlock(&cfg80211_mutex); | ||
| 353 | spin_lock(®_regdb_search_lock); | ||
| 354 | break; | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | kfree(request); | ||
| 359 | } | ||
| 360 | spin_unlock(®_regdb_search_lock); | ||
| 361 | } | ||
| 362 | |||
| 363 | static DECLARE_WORK(reg_regdb_work, reg_regdb_search); | ||
| 364 | |||
| 365 | static void reg_regdb_query(const char *alpha2) | ||
| 366 | { | ||
| 367 | struct reg_regdb_search_request *request; | ||
| 368 | |||
| 369 | if (!alpha2) | ||
| 370 | return; | ||
| 371 | |||
| 372 | request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); | ||
| 373 | if (!request) | ||
| 374 | return; | ||
| 375 | |||
| 376 | memcpy(request->alpha2, alpha2, 2); | ||
| 377 | |||
| 378 | spin_lock(®_regdb_search_lock); | ||
| 379 | list_add_tail(&request->list, ®_regdb_search_list); | ||
| 380 | spin_unlock(®_regdb_search_lock); | ||
| 381 | |||
| 382 | schedule_work(®_regdb_work); | ||
| 383 | } | ||
| 384 | #else | ||
| 385 | static inline void reg_regdb_query(const char *alpha2) {} | ||
| 386 | #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ | ||
| 387 | |||
| 338 | /* | 388 | /* |
| 339 | * This lets us keep regulatory code which is updated on a regulatory | 389 | * This lets us keep regulatory code which is updated on a regulatory |
| 340 | * basis in userspace. | 390 | * basis in userspace. |
| @@ -354,6 +404,9 @@ static int call_crda(const char *alpha2) | |||
| 354 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " | 404 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " |
| 355 | "regulatory domain\n"); | 405 | "regulatory domain\n"); |
| 356 | 406 | ||
| 407 | /* query internal regulatory database (if it exists) */ | ||
| 408 | reg_regdb_query(alpha2); | ||
| 409 | |||
| 357 | country_env[8] = alpha2[0]; | 410 | country_env[8] = alpha2[0]; |
| 358 | country_env[9] = alpha2[1]; | 411 | country_env[9] = alpha2[1]; |
| 359 | 412 | ||
| @@ -454,12 +507,212 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
| 454 | } | 507 | } |
| 455 | 508 | ||
| 456 | /* | 509 | /* |
| 510 | * This is a work around for sanity checking ieee80211_channel_to_frequency()'s | ||
| 511 | * work. ieee80211_channel_to_frequency() can for example currently provide a | ||
| 512 | * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be | ||
| 513 | * an AP providing channel 8 on a country IE triplet when it sent this on the | ||
| 514 | * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz | ||
| 515 | * channel. | ||
| 516 | * | ||
| 517 | * This can be removed once ieee80211_channel_to_frequency() takes in a band. | ||
| 518 | */ | ||
| 519 | static bool chan_in_band(int chan, enum ieee80211_band band) | ||
| 520 | { | ||
| 521 | int center_freq = ieee80211_channel_to_frequency(chan); | ||
| 522 | |||
| 523 | switch (band) { | ||
| 524 | case IEEE80211_BAND_2GHZ: | ||
| 525 | if (center_freq <= 2484) | ||
| 526 | return true; | ||
| 527 | return false; | ||
| 528 | case IEEE80211_BAND_5GHZ: | ||
| 529 | if (center_freq >= 5005) | ||
| 530 | return true; | ||
| 531 | return false; | ||
| 532 | default: | ||
| 533 | return false; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | /* | ||
| 538 | * Some APs may send a country IE triplet for each channel they | ||
| 539 | * support and while this is completely overkill and silly we still | ||
| 540 | * need to support it. We avoid making a single rule for each channel | ||
| 541 | * though and to help us with this we use this helper to find the | ||
| 542 | * actual subband end channel. These type of country IE triplet | ||
| 543 | * scenerios are handled then, all yielding two regulaotry rules from | ||
| 544 | * parsing a country IE: | ||
| 545 | * | ||
| 546 | * [1] | ||
| 547 | * [2] | ||
| 548 | * [36] | ||
| 549 | * [40] | ||
| 550 | * | ||
| 551 | * [1] | ||
| 552 | * [2-4] | ||
| 553 | * [5-12] | ||
| 554 | * [36] | ||
| 555 | * [40-44] | ||
| 556 | * | ||
| 557 | * [1-4] | ||
| 558 | * [5-7] | ||
| 559 | * [36-44] | ||
| 560 | * [48-64] | ||
| 561 | * | ||
| 562 | * [36-36] | ||
| 563 | * [40-40] | ||
| 564 | * [44-44] | ||
| 565 | * [48-48] | ||
| 566 | * [52-52] | ||
| 567 | * [56-56] | ||
| 568 | * [60-60] | ||
| 569 | * [64-64] | ||
| 570 | * [100-100] | ||
| 571 | * [104-104] | ||
| 572 | * [108-108] | ||
| 573 | * [112-112] | ||
| 574 | * [116-116] | ||
| 575 | * [120-120] | ||
| 576 | * [124-124] | ||
| 577 | * [128-128] | ||
| 578 | * [132-132] | ||
| 579 | * [136-136] | ||
| 580 | * [140-140] | ||
| 581 | * | ||
| 582 | * Returns 0 if the IE has been found to be invalid in the middle | ||
| 583 | * somewhere. | ||
| 584 | */ | ||
| 585 | static int max_subband_chan(enum ieee80211_band band, | ||
| 586 | int orig_cur_chan, | ||
| 587 | int orig_end_channel, | ||
| 588 | s8 orig_max_power, | ||
| 589 | u8 **country_ie, | ||
| 590 | u8 *country_ie_len) | ||
| 591 | { | ||
| 592 | u8 *triplets_start = *country_ie; | ||
| 593 | u8 len_at_triplet = *country_ie_len; | ||
| 594 | int end_subband_chan = orig_end_channel; | ||
| 595 | |||
| 596 | /* | ||
| 597 | * We'll deal with padding for the caller unless | ||
| 598 | * its not immediate and we don't process any channels | ||
| 599 | */ | ||
| 600 | if (*country_ie_len == 1) { | ||
| 601 | *country_ie += 1; | ||
| 602 | *country_ie_len -= 1; | ||
| 603 | return orig_end_channel; | ||
| 604 | } | ||
| 605 | |||
| 606 | /* Move to the next triplet and then start search */ | ||
| 607 | *country_ie += 3; | ||
| 608 | *country_ie_len -= 3; | ||
| 609 | |||
| 610 | if (!chan_in_band(orig_cur_chan, band)) | ||
| 611 | return 0; | ||
| 612 | |||
| 613 | while (*country_ie_len >= 3) { | ||
| 614 | int end_channel = 0; | ||
| 615 | struct ieee80211_country_ie_triplet *triplet = | ||
| 616 | (struct ieee80211_country_ie_triplet *) *country_ie; | ||
| 617 | int cur_channel = 0, next_expected_chan; | ||
| 618 | |||
| 619 | /* means last triplet is completely unrelated to this one */ | ||
| 620 | if (triplet->ext.reg_extension_id >= | ||
| 621 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
| 622 | *country_ie -= 3; | ||
| 623 | *country_ie_len += 3; | ||
| 624 | break; | ||
| 625 | } | ||
| 626 | |||
| 627 | if (triplet->chans.first_channel == 0) { | ||
| 628 | *country_ie += 1; | ||
| 629 | *country_ie_len -= 1; | ||
| 630 | if (*country_ie_len != 0) | ||
| 631 | return 0; | ||
| 632 | break; | ||
| 633 | } | ||
| 634 | |||
| 635 | if (triplet->chans.num_channels == 0) | ||
| 636 | return 0; | ||
| 637 | |||
| 638 | /* Monitonically increasing channel order */ | ||
| 639 | if (triplet->chans.first_channel <= end_subband_chan) | ||
| 640 | return 0; | ||
| 641 | |||
| 642 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
| 643 | return 0; | ||
| 644 | |||
| 645 | /* 2 GHz */ | ||
| 646 | if (triplet->chans.first_channel <= 14) { | ||
| 647 | end_channel = triplet->chans.first_channel + | ||
| 648 | triplet->chans.num_channels - 1; | ||
| 649 | } | ||
| 650 | else { | ||
| 651 | end_channel = triplet->chans.first_channel + | ||
| 652 | (4 * (triplet->chans.num_channels - 1)); | ||
| 653 | } | ||
| 654 | |||
| 655 | if (!chan_in_band(end_channel, band)) | ||
| 656 | return 0; | ||
| 657 | |||
| 658 | if (orig_max_power != triplet->chans.max_power) { | ||
| 659 | *country_ie -= 3; | ||
| 660 | *country_ie_len += 3; | ||
| 661 | break; | ||
| 662 | } | ||
| 663 | |||
| 664 | cur_channel = triplet->chans.first_channel; | ||
| 665 | |||
| 666 | /* The key is finding the right next expected channel */ | ||
| 667 | if (band == IEEE80211_BAND_2GHZ) | ||
| 668 | next_expected_chan = end_subband_chan + 1; | ||
| 669 | else | ||
| 670 | next_expected_chan = end_subband_chan + 4; | ||
| 671 | |||
| 672 | if (cur_channel != next_expected_chan) { | ||
| 673 | *country_ie -= 3; | ||
| 674 | *country_ie_len += 3; | ||
| 675 | break; | ||
| 676 | } | ||
| 677 | |||
| 678 | end_subband_chan = end_channel; | ||
| 679 | |||
| 680 | /* Move to the next one */ | ||
| 681 | *country_ie += 3; | ||
| 682 | *country_ie_len -= 3; | ||
| 683 | |||
| 684 | /* | ||
| 685 | * Padding needs to be dealt with if we processed | ||
| 686 | * some channels. | ||
| 687 | */ | ||
| 688 | if (*country_ie_len == 1) { | ||
| 689 | *country_ie += 1; | ||
| 690 | *country_ie_len -= 1; | ||
| 691 | break; | ||
| 692 | } | ||
| 693 | |||
| 694 | /* If seen, the IE is invalid */ | ||
| 695 | if (*country_ie_len == 2) | ||
| 696 | return 0; | ||
| 697 | } | ||
| 698 | |||
| 699 | if (end_subband_chan == orig_end_channel) { | ||
| 700 | *country_ie = triplets_start; | ||
| 701 | *country_ie_len = len_at_triplet; | ||
| 702 | return orig_end_channel; | ||
| 703 | } | ||
| 704 | |||
| 705 | return end_subband_chan; | ||
| 706 | } | ||
| 707 | |||
| 708 | /* | ||
| 457 | * Converts a country IE to a regulatory domain. A regulatory domain | 709 | * Converts a country IE to a regulatory domain. A regulatory domain |
| 458 | * structure has a lot of information which the IE doesn't yet have, | 710 | * structure has a lot of information which the IE doesn't yet have, |
| 459 | * so for the other values we use upper max values as we will intersect | 711 | * so for the other values we use upper max values as we will intersect |
| 460 | * with our userspace regulatory agent to get lower bounds. | 712 | * with our userspace regulatory agent to get lower bounds. |
| 461 | */ | 713 | */ |
| 462 | static struct ieee80211_regdomain *country_ie_2_rd( | 714 | static struct ieee80211_regdomain *country_ie_2_rd( |
| 715 | enum ieee80211_band band, | ||
| 463 | u8 *country_ie, | 716 | u8 *country_ie, |
| 464 | u8 country_ie_len, | 717 | u8 country_ie_len, |
| 465 | u32 *checksum) | 718 | u32 *checksum) |
| @@ -521,10 +774,29 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
| 521 | continue; | 774 | continue; |
| 522 | } | 775 | } |
| 523 | 776 | ||
| 777 | /* | ||
| 778 | * APs can add padding to make length divisible | ||
| 779 | * by two, required by the spec. | ||
| 780 | */ | ||
| 781 | if (triplet->chans.first_channel == 0) { | ||
| 782 | country_ie++; | ||
| 783 | country_ie_len--; | ||
| 784 | /* This is expected to be at the very end only */ | ||
| 785 | if (country_ie_len != 0) | ||
| 786 | return NULL; | ||
| 787 | break; | ||
| 788 | } | ||
| 789 | |||
| 790 | if (triplet->chans.num_channels == 0) | ||
| 791 | return NULL; | ||
| 792 | |||
| 793 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
| 794 | return NULL; | ||
| 795 | |||
| 524 | /* 2 GHz */ | 796 | /* 2 GHz */ |
| 525 | if (triplet->chans.first_channel <= 14) | 797 | if (band == IEEE80211_BAND_2GHZ) |
| 526 | end_channel = triplet->chans.first_channel + | 798 | end_channel = triplet->chans.first_channel + |
| 527 | triplet->chans.num_channels; | 799 | triplet->chans.num_channels - 1; |
| 528 | else | 800 | else |
| 529 | /* | 801 | /* |
| 530 | * 5 GHz -- For example in country IEs if the first | 802 | * 5 GHz -- For example in country IEs if the first |
| @@ -539,6 +811,24 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
| 539 | (4 * (triplet->chans.num_channels - 1)); | 811 | (4 * (triplet->chans.num_channels - 1)); |
| 540 | 812 | ||
| 541 | cur_channel = triplet->chans.first_channel; | 813 | cur_channel = triplet->chans.first_channel; |
| 814 | |||
| 815 | /* | ||
| 816 | * Enhancement for APs that send a triplet for every channel | ||
| 817 | * or for whatever reason sends triplets with multiple channels | ||
| 818 | * separated when in fact they should be together. | ||
| 819 | */ | ||
| 820 | end_channel = max_subband_chan(band, | ||
| 821 | cur_channel, | ||
| 822 | end_channel, | ||
| 823 | triplet->chans.max_power, | ||
| 824 | &country_ie, | ||
| 825 | &country_ie_len); | ||
| 826 | if (!end_channel) | ||
| 827 | return NULL; | ||
| 828 | |||
| 829 | if (!chan_in_band(end_channel, band)) | ||
| 830 | return NULL; | ||
| 831 | |||
| 542 | cur_sub_max_channel = end_channel; | 832 | cur_sub_max_channel = end_channel; |
| 543 | 833 | ||
| 544 | /* Basic sanity check */ | 834 | /* Basic sanity check */ |
| @@ -569,10 +859,13 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
| 569 | 859 | ||
| 570 | last_sub_max_channel = cur_sub_max_channel; | 860 | last_sub_max_channel = cur_sub_max_channel; |
| 571 | 861 | ||
| 572 | country_ie += 3; | ||
| 573 | country_ie_len -= 3; | ||
| 574 | num_rules++; | 862 | num_rules++; |
| 575 | 863 | ||
| 864 | if (country_ie_len >= 3) { | ||
| 865 | country_ie += 3; | ||
| 866 | country_ie_len -= 3; | ||
| 867 | } | ||
| 868 | |||
| 576 | /* | 869 | /* |
| 577 | * Note: this is not a IEEE requirement but | 870 | * Note: this is not a IEEE requirement but |
| 578 | * simply a memory requirement | 871 | * simply a memory requirement |
| @@ -615,6 +908,12 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
| 615 | continue; | 908 | continue; |
| 616 | } | 909 | } |
| 617 | 910 | ||
| 911 | if (triplet->chans.first_channel == 0) { | ||
| 912 | country_ie++; | ||
| 913 | country_ie_len--; | ||
| 914 | break; | ||
| 915 | } | ||
| 916 | |||
| 618 | reg_rule = &rd->reg_rules[i]; | 917 | reg_rule = &rd->reg_rules[i]; |
| 619 | freq_range = ®_rule->freq_range; | 918 | freq_range = ®_rule->freq_range; |
| 620 | power_rule = ®_rule->power_rule; | 919 | power_rule = ®_rule->power_rule; |
| @@ -622,13 +921,20 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
| 622 | reg_rule->flags = flags; | 921 | reg_rule->flags = flags; |
| 623 | 922 | ||
| 624 | /* 2 GHz */ | 923 | /* 2 GHz */ |
| 625 | if (triplet->chans.first_channel <= 14) | 924 | if (band == IEEE80211_BAND_2GHZ) |
| 626 | end_channel = triplet->chans.first_channel + | 925 | end_channel = triplet->chans.first_channel + |
| 627 | triplet->chans.num_channels; | 926 | triplet->chans.num_channels -1; |
| 628 | else | 927 | else |
| 629 | end_channel = triplet->chans.first_channel + | 928 | end_channel = triplet->chans.first_channel + |
| 630 | (4 * (triplet->chans.num_channels - 1)); | 929 | (4 * (triplet->chans.num_channels - 1)); |
| 631 | 930 | ||
| 931 | end_channel = max_subband_chan(band, | ||
| 932 | triplet->chans.first_channel, | ||
| 933 | end_channel, | ||
| 934 | triplet->chans.max_power, | ||
| 935 | &country_ie, | ||
| 936 | &country_ie_len); | ||
| 937 | |||
| 632 | /* | 938 | /* |
| 633 | * The +10 is since the regulatory domain expects | 939 | * The +10 is since the regulatory domain expects |
| 634 | * the actual band edge, not the center of freq for | 940 | * the actual band edge, not the center of freq for |
| @@ -649,12 +955,15 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
| 649 | */ | 955 | */ |
| 650 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); | 956 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); |
| 651 | power_rule->max_antenna_gain = DBI_TO_MBI(100); | 957 | power_rule->max_antenna_gain = DBI_TO_MBI(100); |
| 652 | power_rule->max_eirp = DBM_TO_MBM(100); | 958 | power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power); |
| 653 | 959 | ||
| 654 | country_ie += 3; | ||
| 655 | country_ie_len -= 3; | ||
| 656 | i++; | 960 | i++; |
| 657 | 961 | ||
| 962 | if (country_ie_len >= 3) { | ||
| 963 | country_ie += 3; | ||
| 964 | country_ie_len -= 3; | ||
| 965 | } | ||
| 966 | |||
| 658 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); | 967 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); |
| 659 | } | 968 | } |
| 660 | 969 | ||
| @@ -950,25 +1259,21 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
| 950 | if (r == -ERANGE && | 1259 | if (r == -ERANGE && |
| 951 | last_request->initiator == | 1260 | last_request->initiator == |
| 952 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 1261 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
| 953 | #ifdef CONFIG_CFG80211_REG_DEBUG | 1262 | REG_DBG_PRINT("cfg80211: Leaving channel %d MHz " |
| 954 | printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " | ||
| 955 | "intact on %s - no rule found in band on " | 1263 | "intact on %s - no rule found in band on " |
| 956 | "Country IE\n", | 1264 | "Country IE\n", |
| 957 | chan->center_freq, wiphy_name(wiphy)); | 1265 | chan->center_freq, wiphy_name(wiphy)); |
| 958 | #endif | ||
| 959 | } else { | 1266 | } else { |
| 960 | /* | 1267 | /* |
| 961 | * In this case we know the country IE has at least one reg rule | 1268 | * In this case we know the country IE has at least one reg rule |
| 962 | * for the band so we respect its band definitions | 1269 | * for the band so we respect its band definitions |
| 963 | */ | 1270 | */ |
| 964 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
| 965 | if (last_request->initiator == | 1271 | if (last_request->initiator == |
| 966 | NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1272 | NL80211_REGDOM_SET_BY_COUNTRY_IE) |
| 967 | printk(KERN_DEBUG "cfg80211: Disabling " | 1273 | REG_DBG_PRINT("cfg80211: Disabling " |
| 968 | "channel %d MHz on %s due to " | 1274 | "channel %d MHz on %s due to " |
| 969 | "Country IE\n", | 1275 | "Country IE\n", |
| 970 | chan->center_freq, wiphy_name(wiphy)); | 1276 | chan->center_freq, wiphy_name(wiphy)); |
| 971 | #endif | ||
| 972 | flags |= IEEE80211_CHAN_DISABLED; | 1277 | flags |= IEEE80211_CHAN_DISABLED; |
| 973 | chan->flags = flags; | 1278 | chan->flags = flags; |
| 974 | } | 1279 | } |
| @@ -1342,30 +1647,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
| 1342 | } | 1647 | } |
| 1343 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1648 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
| 1344 | 1649 | ||
| 1345 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
| 1346 | const struct ieee80211_regdomain *src_regd) | ||
| 1347 | { | ||
| 1348 | struct ieee80211_regdomain *regd; | ||
| 1349 | int size_of_regd = 0; | ||
| 1350 | unsigned int i; | ||
| 1351 | |||
| 1352 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
| 1353 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
| 1354 | |||
| 1355 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
| 1356 | if (!regd) | ||
| 1357 | return -ENOMEM; | ||
| 1358 | |||
| 1359 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
| 1360 | |||
| 1361 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
| 1362 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
| 1363 | sizeof(struct ieee80211_reg_rule)); | ||
| 1364 | |||
| 1365 | *dst_regd = regd; | ||
| 1366 | return 0; | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | /* | 1650 | /* |
| 1370 | * Return value which can be used by ignore_request() to indicate | 1651 | * Return value which can be used by ignore_request() to indicate |
| 1371 | * it has been determined we should intersect two regulatory domains | 1652 | * it has been determined we should intersect two regulatory domains |
| @@ -1387,7 +1668,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
| 1387 | 1668 | ||
| 1388 | switch (pending_request->initiator) { | 1669 | switch (pending_request->initiator) { |
| 1389 | case NL80211_REGDOM_SET_BY_CORE: | 1670 | case NL80211_REGDOM_SET_BY_CORE: |
| 1390 | return -EINVAL; | 1671 | return 0; |
| 1391 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1672 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
| 1392 | 1673 | ||
| 1393 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1674 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
| @@ -1418,8 +1699,6 @@ static int ignore_request(struct wiphy *wiphy, | |||
| 1418 | return REG_INTERSECT; | 1699 | return REG_INTERSECT; |
| 1419 | case NL80211_REGDOM_SET_BY_DRIVER: | 1700 | case NL80211_REGDOM_SET_BY_DRIVER: |
| 1420 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1701 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { |
| 1421 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
| 1422 | return 0; | ||
| 1423 | if (regdom_changes(pending_request->alpha2)) | 1702 | if (regdom_changes(pending_request->alpha2)) |
| 1424 | return 0; | 1703 | return 0; |
| 1425 | return -EALREADY; | 1704 | return -EALREADY; |
| @@ -1456,8 +1735,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
| 1456 | return -EAGAIN; | 1735 | return -EAGAIN; |
| 1457 | } | 1736 | } |
| 1458 | 1737 | ||
| 1459 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1738 | if (!regdom_changes(pending_request->alpha2)) |
| 1460 | !regdom_changes(pending_request->alpha2)) | ||
| 1461 | return -EALREADY; | 1739 | return -EALREADY; |
| 1462 | 1740 | ||
| 1463 | return 0; | 1741 | return 0; |
| @@ -1529,6 +1807,11 @@ new_request: | |||
| 1529 | 1807 | ||
| 1530 | pending_request = NULL; | 1808 | pending_request = NULL; |
| 1531 | 1809 | ||
| 1810 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | ||
| 1811 | user_alpha2[0] = last_request->alpha2[0]; | ||
| 1812 | user_alpha2[1] = last_request->alpha2[1]; | ||
| 1813 | } | ||
| 1814 | |||
| 1532 | /* When r == REG_INTERSECT we do need to call CRDA */ | 1815 | /* When r == REG_INTERSECT we do need to call CRDA */ |
| 1533 | if (r < 0) { | 1816 | if (r < 0) { |
| 1534 | /* | 1817 | /* |
| @@ -1648,12 +1931,16 @@ static void queue_regulatory_request(struct regulatory_request *request) | |||
| 1648 | schedule_work(®_work); | 1931 | schedule_work(®_work); |
| 1649 | } | 1932 | } |
| 1650 | 1933 | ||
| 1651 | /* Core regulatory hint -- happens once during cfg80211_init() */ | 1934 | /* |
| 1935 | * Core regulatory hint -- happens during cfg80211_init() | ||
| 1936 | * and when we restore regulatory settings. | ||
| 1937 | */ | ||
| 1652 | static int regulatory_hint_core(const char *alpha2) | 1938 | static int regulatory_hint_core(const char *alpha2) |
| 1653 | { | 1939 | { |
| 1654 | struct regulatory_request *request; | 1940 | struct regulatory_request *request; |
| 1655 | 1941 | ||
| 1656 | BUG_ON(last_request); | 1942 | kfree(last_request); |
| 1943 | last_request = NULL; | ||
| 1657 | 1944 | ||
| 1658 | request = kzalloc(sizeof(struct regulatory_request), | 1945 | request = kzalloc(sizeof(struct regulatory_request), |
| 1659 | GFP_KERNEL); | 1946 | GFP_KERNEL); |
| @@ -1664,14 +1951,12 @@ static int regulatory_hint_core(const char *alpha2) | |||
| 1664 | request->alpha2[1] = alpha2[1]; | 1951 | request->alpha2[1] = alpha2[1]; |
| 1665 | request->initiator = NL80211_REGDOM_SET_BY_CORE; | 1952 | request->initiator = NL80211_REGDOM_SET_BY_CORE; |
| 1666 | 1953 | ||
| 1667 | queue_regulatory_request(request); | ||
| 1668 | |||
| 1669 | /* | 1954 | /* |
| 1670 | * This ensures last_request is populated once modules | 1955 | * This ensures last_request is populated once modules |
| 1671 | * come swinging in and calling regulatory hints and | 1956 | * come swinging in and calling regulatory hints and |
| 1672 | * wiphy_apply_custom_regulatory(). | 1957 | * wiphy_apply_custom_regulatory(). |
| 1673 | */ | 1958 | */ |
| 1674 | flush_scheduled_work(); | 1959 | reg_process_hint(request); |
| 1675 | 1960 | ||
| 1676 | return 0; | 1961 | return 0; |
| 1677 | } | 1962 | } |
| @@ -1758,8 +2043,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, | |||
| 1758 | * therefore cannot iterate over the rdev list here. | 2043 | * therefore cannot iterate over the rdev list here. |
| 1759 | */ | 2044 | */ |
| 1760 | void regulatory_hint_11d(struct wiphy *wiphy, | 2045 | void regulatory_hint_11d(struct wiphy *wiphy, |
| 1761 | u8 *country_ie, | 2046 | enum ieee80211_band band, |
| 1762 | u8 country_ie_len) | 2047 | u8 *country_ie, |
| 2048 | u8 country_ie_len) | ||
| 1763 | { | 2049 | { |
| 1764 | struct ieee80211_regdomain *rd = NULL; | 2050 | struct ieee80211_regdomain *rd = NULL; |
| 1765 | char alpha2[2]; | 2051 | char alpha2[2]; |
| @@ -1805,9 +2091,11 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
| 1805 | wiphy_idx_valid(last_request->wiphy_idx))) | 2091 | wiphy_idx_valid(last_request->wiphy_idx))) |
| 1806 | goto out; | 2092 | goto out; |
| 1807 | 2093 | ||
| 1808 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | 2094 | rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum); |
| 1809 | if (!rd) | 2095 | if (!rd) { |
| 2096 | REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n"); | ||
| 1810 | goto out; | 2097 | goto out; |
| 2098 | } | ||
| 1811 | 2099 | ||
| 1812 | /* | 2100 | /* |
| 1813 | * This will not happen right now but we leave it here for the | 2101 | * This will not happen right now but we leave it here for the |
| @@ -1850,6 +2138,123 @@ out: | |||
| 1850 | mutex_unlock(®_mutex); | 2138 | mutex_unlock(®_mutex); |
| 1851 | } | 2139 | } |
| 1852 | 2140 | ||
| 2141 | static void restore_alpha2(char *alpha2, bool reset_user) | ||
| 2142 | { | ||
| 2143 | /* indicates there is no alpha2 to consider for restoration */ | ||
| 2144 | alpha2[0] = '9'; | ||
| 2145 | alpha2[1] = '7'; | ||
| 2146 | |||
| 2147 | /* The user setting has precedence over the module parameter */ | ||
| 2148 | if (is_user_regdom_saved()) { | ||
| 2149 | /* Unless we're asked to ignore it and reset it */ | ||
| 2150 | if (reset_user) { | ||
| 2151 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
| 2152 | "including user preference\n"); | ||
| 2153 | user_alpha2[0] = '9'; | ||
| 2154 | user_alpha2[1] = '7'; | ||
| 2155 | |||
| 2156 | /* | ||
| 2157 | * If we're ignoring user settings, we still need to | ||
| 2158 | * check the module parameter to ensure we put things | ||
| 2159 | * back as they were for a full restore. | ||
| 2160 | */ | ||
| 2161 | if (!is_world_regdom(ieee80211_regdom)) { | ||
| 2162 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
| 2163 | "module parameter ieee80211_regdom: %c%c\n", | ||
| 2164 | ieee80211_regdom[0], | ||
| 2165 | ieee80211_regdom[1]); | ||
| 2166 | alpha2[0] = ieee80211_regdom[0]; | ||
| 2167 | alpha2[1] = ieee80211_regdom[1]; | ||
| 2168 | } | ||
| 2169 | } else { | ||
| 2170 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
| 2171 | "while preserving user preference for: %c%c\n", | ||
| 2172 | user_alpha2[0], | ||
| 2173 | user_alpha2[1]); | ||
| 2174 | alpha2[0] = user_alpha2[0]; | ||
| 2175 | alpha2[1] = user_alpha2[1]; | ||
| 2176 | } | ||
| 2177 | } else if (!is_world_regdom(ieee80211_regdom)) { | ||
| 2178 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
| 2179 | "module parameter ieee80211_regdom: %c%c\n", | ||
| 2180 | ieee80211_regdom[0], | ||
| 2181 | ieee80211_regdom[1]); | ||
| 2182 | alpha2[0] = ieee80211_regdom[0]; | ||
| 2183 | alpha2[1] = ieee80211_regdom[1]; | ||
| 2184 | } else | ||
| 2185 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n"); | ||
| 2186 | } | ||
| 2187 | |||
| 2188 | /* | ||
| 2189 | * Restoring regulatory settings involves ingoring any | ||
| 2190 | * possibly stale country IE information and user regulatory | ||
| 2191 | * settings if so desired, this includes any beacon hints | ||
| 2192 | * learned as we could have traveled outside to another country | ||
| 2193 | * after disconnection. To restore regulatory settings we do | ||
| 2194 | * exactly what we did at bootup: | ||
| 2195 | * | ||
| 2196 | * - send a core regulatory hint | ||
| 2197 | * - send a user regulatory hint if applicable | ||
| 2198 | * | ||
| 2199 | * Device drivers that send a regulatory hint for a specific country | ||
| 2200 | * keep their own regulatory domain on wiphy->regd so that does does | ||
| 2201 | * not need to be remembered. | ||
| 2202 | */ | ||
| 2203 | static void restore_regulatory_settings(bool reset_user) | ||
| 2204 | { | ||
| 2205 | char alpha2[2]; | ||
| 2206 | struct reg_beacon *reg_beacon, *btmp; | ||
| 2207 | |||
| 2208 | mutex_lock(&cfg80211_mutex); | ||
| 2209 | mutex_lock(®_mutex); | ||
| 2210 | |||
| 2211 | reset_regdomains(); | ||
| 2212 | restore_alpha2(alpha2, reset_user); | ||
| 2213 | |||
| 2214 | /* Clear beacon hints */ | ||
| 2215 | spin_lock_bh(®_pending_beacons_lock); | ||
| 2216 | if (!list_empty(®_pending_beacons)) { | ||
| 2217 | list_for_each_entry_safe(reg_beacon, btmp, | ||
| 2218 | ®_pending_beacons, list) { | ||
| 2219 | list_del(®_beacon->list); | ||
| 2220 | kfree(reg_beacon); | ||
| 2221 | } | ||
| 2222 | } | ||
| 2223 | spin_unlock_bh(®_pending_beacons_lock); | ||
| 2224 | |||
| 2225 | if (!list_empty(®_beacon_list)) { | ||
| 2226 | list_for_each_entry_safe(reg_beacon, btmp, | ||
| 2227 | ®_beacon_list, list) { | ||
| 2228 | list_del(®_beacon->list); | ||
| 2229 | kfree(reg_beacon); | ||
| 2230 | } | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | /* First restore to the basic regulatory settings */ | ||
| 2234 | cfg80211_regdomain = cfg80211_world_regdom; | ||
| 2235 | |||
| 2236 | mutex_unlock(®_mutex); | ||
| 2237 | mutex_unlock(&cfg80211_mutex); | ||
| 2238 | |||
| 2239 | regulatory_hint_core(cfg80211_regdomain->alpha2); | ||
| 2240 | |||
| 2241 | /* | ||
| 2242 | * This restores the ieee80211_regdom module parameter | ||
| 2243 | * preference or the last user requested regulatory | ||
| 2244 | * settings, user regulatory settings takes precedence. | ||
| 2245 | */ | ||
| 2246 | if (is_an_alpha2(alpha2)) | ||
| 2247 | regulatory_hint_user(user_alpha2); | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | |||
| 2251 | void regulatory_hint_disconnect(void) | ||
| 2252 | { | ||
| 2253 | REG_DBG_PRINT("cfg80211: All devices are disconnected, going to " | ||
| 2254 | "restore regulatory settings\n"); | ||
| 2255 | restore_regulatory_settings(false); | ||
| 2256 | } | ||
| 2257 | |||
| 1853 | static bool freq_is_chan_12_13_14(u16 freq) | 2258 | static bool freq_is_chan_12_13_14(u16 freq) |
| 1854 | { | 2259 | { |
| 1855 | if (freq == ieee80211_channel_to_frequency(12) || | 2260 | if (freq == ieee80211_channel_to_frequency(12) || |
| @@ -1875,13 +2280,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
| 1875 | if (!reg_beacon) | 2280 | if (!reg_beacon) |
| 1876 | return -ENOMEM; | 2281 | return -ENOMEM; |
| 1877 | 2282 | ||
| 1878 | #ifdef CONFIG_CFG80211_REG_DEBUG | 2283 | REG_DBG_PRINT("cfg80211: Found new beacon on " |
| 1879 | printk(KERN_DEBUG "cfg80211: Found new beacon on " | 2284 | "frequency: %d MHz (Ch %d) on %s\n", |
| 1880 | "frequency: %d MHz (Ch %d) on %s\n", | 2285 | beacon_chan->center_freq, |
| 1881 | beacon_chan->center_freq, | 2286 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
| 1882 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 2287 | wiphy_name(wiphy)); |
| 1883 | wiphy_name(wiphy)); | 2288 | |
| 1884 | #endif | ||
| 1885 | memcpy(®_beacon->chan, beacon_chan, | 2289 | memcpy(®_beacon->chan, beacon_chan, |
| 1886 | sizeof(struct ieee80211_channel)); | 2290 | sizeof(struct ieee80211_channel)); |
| 1887 | 2291 | ||
| @@ -2039,8 +2443,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
| 2039 | * If someone else asked us to change the rd lets only bother | 2443 | * If someone else asked us to change the rd lets only bother |
| 2040 | * checking if the alpha2 changes if CRDA was already called | 2444 | * checking if the alpha2 changes if CRDA was already called |
| 2041 | */ | 2445 | */ |
| 2042 | if (!is_old_static_regdom(cfg80211_regdomain) && | 2446 | if (!regdom_changes(rd->alpha2)) |
| 2043 | !regdom_changes(rd->alpha2)) | ||
| 2044 | return -EINVAL; | 2447 | return -EINVAL; |
| 2045 | } | 2448 | } |
| 2046 | 2449 | ||
| @@ -2239,15 +2642,11 @@ int regulatory_init(void) | |||
| 2239 | spin_lock_init(®_requests_lock); | 2642 | spin_lock_init(®_requests_lock); |
| 2240 | spin_lock_init(®_pending_beacons_lock); | 2643 | spin_lock_init(®_pending_beacons_lock); |
| 2241 | 2644 | ||
| 2242 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
| 2243 | cfg80211_regdomain = static_regdom(ieee80211_regdom); | ||
| 2244 | |||
| 2245 | printk(KERN_INFO "cfg80211: Using static regulatory domain info\n"); | ||
| 2246 | print_regdomain_info(cfg80211_regdomain); | ||
| 2247 | #else | ||
| 2248 | cfg80211_regdomain = cfg80211_world_regdom; | 2645 | cfg80211_regdomain = cfg80211_world_regdom; |
| 2249 | 2646 | ||
| 2250 | #endif | 2647 | user_alpha2[0] = '9'; |
| 2648 | user_alpha2[1] = '7'; | ||
| 2649 | |||
| 2251 | /* We always try to get an update for the static regdomain */ | 2650 | /* We always try to get an update for the static regdomain */ |
| 2252 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2651 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); |
| 2253 | if (err) { | 2652 | if (err) { |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 3362c7c069b2..b26224a9f3bc 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
| @@ -41,15 +41,44 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
| 41 | * regulatory_hint_11d - hints a country IE as a regulatory domain | 41 | * regulatory_hint_11d - hints a country IE as a regulatory domain |
| 42 | * @wiphy: the wireless device giving the hint (used only for reporting | 42 | * @wiphy: the wireless device giving the hint (used only for reporting |
| 43 | * conflicts) | 43 | * conflicts) |
| 44 | * @band: the band on which the country IE was received on. This determines | ||
| 45 | * the band we'll process the country IE channel triplets for. | ||
| 44 | * @country_ie: pointer to the country IE | 46 | * @country_ie: pointer to the country IE |
| 45 | * @country_ie_len: length of the country IE | 47 | * @country_ie_len: length of the country IE |
| 46 | * | 48 | * |
| 47 | * We will intersect the rd with the what CRDA tells us should apply | 49 | * We will intersect the rd with the what CRDA tells us should apply |
| 48 | * for the alpha2 this country IE belongs to, this prevents APs from | 50 | * for the alpha2 this country IE belongs to, this prevents APs from |
| 49 | * sending us incorrect or outdated information against a country. | 51 | * sending us incorrect or outdated information against a country. |
| 52 | * | ||
| 53 | * The AP is expected to provide Country IE channel triplets for the | ||
| 54 | * band it is on. It is technically possible for APs to send channel | ||
| 55 | * country IE triplets even for channels outside of the band they are | ||
| 56 | * in but for that they would have to use the regulatory extension | ||
| 57 | * in combination with a triplet but this behaviour is currently | ||
| 58 | * not observed. For this reason if a triplet is seen with channel | ||
| 59 | * information for a band the BSS is not present in it will be ignored. | ||
| 50 | */ | 60 | */ |
| 51 | void regulatory_hint_11d(struct wiphy *wiphy, | 61 | void regulatory_hint_11d(struct wiphy *wiphy, |
| 62 | enum ieee80211_band band, | ||
| 52 | u8 *country_ie, | 63 | u8 *country_ie, |
| 53 | u8 country_ie_len); | 64 | u8 country_ie_len); |
| 54 | 65 | ||
| 66 | /** | ||
| 67 | * regulatory_hint_disconnect - informs all devices have been disconneted | ||
| 68 | * | ||
| 69 | * Regulotory rules can be enhanced further upon scanning and upon | ||
| 70 | * connection to an AP. These rules become stale if we disconnect | ||
| 71 | * and go to another country, whether or not we suspend and resume. | ||
| 72 | * If we suspend, go to another country and resume we'll automatically | ||
| 73 | * get disconnected shortly after resuming and things will be reset as well. | ||
| 74 | * This routine is a helper to restore regulatory settings to how they were | ||
| 75 | * prior to our first connect attempt. This includes ignoring country IE and | ||
| 76 | * beacon regulatory hints. The ieee80211_regdom module parameter will always | ||
| 77 | * be respected but if a user had set the regulatory domain that will take | ||
| 78 | * precedence. | ||
| 79 | * | ||
| 80 | * Must be called from process context. | ||
| 81 | */ | ||
| 82 | void regulatory_hint_disconnect(void); | ||
| 83 | |||
| 55 | #endif /* __NET_WIRELESS_REG_H */ | 84 | #endif /* __NET_WIRELESS_REG_H */ |
diff --git a/net/wireless/regdb.h b/net/wireless/regdb.h new file mode 100644 index 000000000000..818222c92513 --- /dev/null +++ b/net/wireless/regdb.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef __REGDB_H__ | ||
| 2 | #define __REGDB_H__ | ||
| 3 | |||
| 4 | extern const struct ieee80211_regdomain *reg_regdb[]; | ||
| 5 | extern int reg_regdb_size; | ||
| 6 | |||
| 7 | #endif /* __REGDB_H__ */ | ||
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0c2cbbebca95..978cac3414b5 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
| @@ -100,8 +100,10 @@ static void bss_release(struct kref *ref) | |||
| 100 | if (bss->pub.free_priv) | 100 | if (bss->pub.free_priv) |
| 101 | bss->pub.free_priv(&bss->pub); | 101 | bss->pub.free_priv(&bss->pub); |
| 102 | 102 | ||
| 103 | if (bss->ies_allocated) | 103 | if (bss->beacon_ies_allocated) |
| 104 | kfree(bss->pub.information_elements); | 104 | kfree(bss->pub.beacon_ies); |
| 105 | if (bss->proberesp_ies_allocated) | ||
| 106 | kfree(bss->pub.proberesp_ies); | ||
| 105 | 107 | ||
| 106 | BUG_ON(atomic_read(&bss->hold)); | 108 | BUG_ON(atomic_read(&bss->hold)); |
| 107 | 109 | ||
| @@ -141,9 +143,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) | |||
| 141 | dev->bss_generation++; | 143 | dev->bss_generation++; |
| 142 | } | 144 | } |
| 143 | 145 | ||
| 144 | static u8 *find_ie(u8 num, u8 *ies, int len) | 146 | const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) |
| 145 | { | 147 | { |
| 146 | while (len > 2 && ies[0] != num) { | 148 | while (len > 2 && ies[0] != eid) { |
| 147 | len -= ies[1] + 2; | 149 | len -= ies[1] + 2; |
| 148 | ies += ies[1] + 2; | 150 | ies += ies[1] + 2; |
| 149 | } | 151 | } |
| @@ -153,11 +155,12 @@ static u8 *find_ie(u8 num, u8 *ies, int len) | |||
| 153 | return NULL; | 155 | return NULL; |
| 154 | return ies; | 156 | return ies; |
| 155 | } | 157 | } |
| 158 | EXPORT_SYMBOL(cfg80211_find_ie); | ||
| 156 | 159 | ||
| 157 | static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) | 160 | static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) |
| 158 | { | 161 | { |
| 159 | const u8 *ie1 = find_ie(num, ies1, len1); | 162 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); |
| 160 | const u8 *ie2 = find_ie(num, ies2, len2); | 163 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); |
| 161 | int r; | 164 | int r; |
| 162 | 165 | ||
| 163 | if (!ie1 && !ie2) | 166 | if (!ie1 && !ie2) |
| @@ -183,9 +186,9 @@ static bool is_bss(struct cfg80211_bss *a, | |||
| 183 | if (!ssid) | 186 | if (!ssid) |
| 184 | return true; | 187 | return true; |
| 185 | 188 | ||
| 186 | ssidie = find_ie(WLAN_EID_SSID, | 189 | ssidie = cfg80211_find_ie(WLAN_EID_SSID, |
| 187 | a->information_elements, | 190 | a->information_elements, |
| 188 | a->len_information_elements); | 191 | a->len_information_elements); |
| 189 | if (!ssidie) | 192 | if (!ssidie) |
| 190 | return false; | 193 | return false; |
| 191 | if (ssidie[1] != ssid_len) | 194 | if (ssidie[1] != ssid_len) |
| @@ -202,9 +205,9 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
| 202 | if (!is_zero_ether_addr(a->bssid)) | 205 | if (!is_zero_ether_addr(a->bssid)) |
| 203 | return false; | 206 | return false; |
| 204 | 207 | ||
| 205 | ie = find_ie(WLAN_EID_MESH_ID, | 208 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, |
| 206 | a->information_elements, | 209 | a->information_elements, |
| 207 | a->len_information_elements); | 210 | a->len_information_elements); |
| 208 | if (!ie) | 211 | if (!ie) |
| 209 | return false; | 212 | return false; |
| 210 | if (ie[1] != meshidlen) | 213 | if (ie[1] != meshidlen) |
| @@ -212,9 +215,9 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
| 212 | if (memcmp(ie + 2, meshid, meshidlen)) | 215 | if (memcmp(ie + 2, meshid, meshidlen)) |
| 213 | return false; | 216 | return false; |
| 214 | 217 | ||
| 215 | ie = find_ie(WLAN_EID_MESH_CONFIG, | 218 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, |
| 216 | a->information_elements, | 219 | a->information_elements, |
| 217 | a->len_information_elements); | 220 | a->len_information_elements); |
| 218 | if (!ie) | 221 | if (!ie) |
| 219 | return false; | 222 | return false; |
| 220 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) | 223 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) |
| @@ -375,8 +378,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
| 375 | 378 | ||
| 376 | static struct cfg80211_internal_bss * | 379 | static struct cfg80211_internal_bss * |
| 377 | cfg80211_bss_update(struct cfg80211_registered_device *dev, | 380 | cfg80211_bss_update(struct cfg80211_registered_device *dev, |
| 378 | struct cfg80211_internal_bss *res, | 381 | struct cfg80211_internal_bss *res) |
| 379 | bool overwrite) | ||
| 380 | { | 382 | { |
| 381 | struct cfg80211_internal_bss *found = NULL; | 383 | struct cfg80211_internal_bss *found = NULL; |
| 382 | const u8 *meshid, *meshcfg; | 384 | const u8 *meshid, *meshcfg; |
| @@ -394,11 +396,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
| 394 | 396 | ||
| 395 | if (is_zero_ether_addr(res->pub.bssid)) { | 397 | if (is_zero_ether_addr(res->pub.bssid)) { |
| 396 | /* must be mesh, verify */ | 398 | /* must be mesh, verify */ |
| 397 | meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements, | 399 | meshid = cfg80211_find_ie(WLAN_EID_MESH_ID, |
| 398 | res->pub.len_information_elements); | 400 | res->pub.information_elements, |
| 399 | meshcfg = find_ie(WLAN_EID_MESH_CONFIG, | 401 | res->pub.len_information_elements); |
| 400 | res->pub.information_elements, | 402 | meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, |
| 401 | res->pub.len_information_elements); | 403 | res->pub.information_elements, |
| 404 | res->pub.len_information_elements); | ||
| 402 | if (!meshid || !meshcfg || | 405 | if (!meshid || !meshcfg || |
| 403 | meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { | 406 | meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { |
| 404 | /* bogus mesh */ | 407 | /* bogus mesh */ |
| @@ -418,28 +421,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
| 418 | found->pub.capability = res->pub.capability; | 421 | found->pub.capability = res->pub.capability; |
| 419 | found->ts = res->ts; | 422 | found->ts = res->ts; |
| 420 | 423 | ||
| 421 | /* overwrite IEs */ | 424 | /* Update IEs */ |
| 422 | if (overwrite) { | 425 | if (res->pub.proberesp_ies) { |
| 423 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | 426 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); |
| 424 | size_t ielen = res->pub.len_information_elements; | 427 | size_t ielen = res->pub.len_proberesp_ies; |
| 428 | |||
| 429 | if (found->pub.proberesp_ies && | ||
| 430 | !found->proberesp_ies_allocated && | ||
| 431 | ksize(found) >= used + ielen) { | ||
| 432 | memcpy(found->pub.proberesp_ies, | ||
| 433 | res->pub.proberesp_ies, ielen); | ||
| 434 | found->pub.len_proberesp_ies = ielen; | ||
| 435 | } else { | ||
| 436 | u8 *ies = found->pub.proberesp_ies; | ||
| 437 | |||
| 438 | if (found->proberesp_ies_allocated) | ||
| 439 | ies = krealloc(ies, ielen, GFP_ATOMIC); | ||
| 440 | else | ||
| 441 | ies = kmalloc(ielen, GFP_ATOMIC); | ||
| 442 | |||
| 443 | if (ies) { | ||
| 444 | memcpy(ies, res->pub.proberesp_ies, | ||
| 445 | ielen); | ||
| 446 | found->proberesp_ies_allocated = true; | ||
| 447 | found->pub.proberesp_ies = ies; | ||
| 448 | found->pub.len_proberesp_ies = ielen; | ||
| 449 | } | ||
| 450 | } | ||
| 425 | 451 | ||
| 426 | if (!found->ies_allocated && ksize(found) >= used + ielen) { | 452 | /* Override possible earlier Beacon frame IEs */ |
| 427 | memcpy(found->pub.information_elements, | 453 | found->pub.information_elements = |
| 428 | res->pub.information_elements, ielen); | 454 | found->pub.proberesp_ies; |
| 429 | found->pub.len_information_elements = ielen; | 455 | found->pub.len_information_elements = |
| 456 | found->pub.len_proberesp_ies; | ||
| 457 | } | ||
| 458 | if (res->pub.beacon_ies) { | ||
| 459 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | ||
| 460 | size_t ielen = res->pub.len_beacon_ies; | ||
| 461 | |||
| 462 | if (found->pub.beacon_ies && | ||
| 463 | !found->beacon_ies_allocated && | ||
| 464 | ksize(found) >= used + ielen) { | ||
| 465 | memcpy(found->pub.beacon_ies, | ||
| 466 | res->pub.beacon_ies, ielen); | ||
| 467 | found->pub.len_beacon_ies = ielen; | ||
| 430 | } else { | 468 | } else { |
| 431 | u8 *ies = found->pub.information_elements; | 469 | u8 *ies = found->pub.beacon_ies; |
| 432 | 470 | ||
| 433 | if (found->ies_allocated) | 471 | if (found->beacon_ies_allocated) |
| 434 | ies = krealloc(ies, ielen, GFP_ATOMIC); | 472 | ies = krealloc(ies, ielen, GFP_ATOMIC); |
| 435 | else | 473 | else |
| 436 | ies = kmalloc(ielen, GFP_ATOMIC); | 474 | ies = kmalloc(ielen, GFP_ATOMIC); |
| 437 | 475 | ||
| 438 | if (ies) { | 476 | if (ies) { |
| 439 | memcpy(ies, res->pub.information_elements, ielen); | 477 | memcpy(ies, res->pub.beacon_ies, |
| 440 | found->ies_allocated = true; | 478 | ielen); |
| 441 | found->pub.information_elements = ies; | 479 | found->beacon_ies_allocated = true; |
| 442 | found->pub.len_information_elements = ielen; | 480 | found->pub.beacon_ies = ies; |
| 481 | found->pub.len_beacon_ies = ielen; | ||
| 443 | } | 482 | } |
| 444 | } | 483 | } |
| 445 | } | 484 | } |
| @@ -489,14 +528,26 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
| 489 | res->pub.tsf = timestamp; | 528 | res->pub.tsf = timestamp; |
| 490 | res->pub.beacon_interval = beacon_interval; | 529 | res->pub.beacon_interval = beacon_interval; |
| 491 | res->pub.capability = capability; | 530 | res->pub.capability = capability; |
| 492 | /* point to after the private area */ | 531 | /* |
| 493 | res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; | 532 | * Since we do not know here whether the IEs are from a Beacon or Probe |
| 494 | memcpy(res->pub.information_elements, ie, ielen); | 533 | * Response frame, we need to pick one of the options and only use it |
| 495 | res->pub.len_information_elements = ielen; | 534 | * with the driver that does not provide the full Beacon/Probe Response |
| 535 | * frame. Use Beacon frame pointer to avoid indicating that this should | ||
| 536 | * override the information_elements pointer should we have received an | ||
| 537 | * earlier indication of Probe Response data. | ||
| 538 | * | ||
| 539 | * The initial buffer for the IEs is allocated with the BSS entry and | ||
| 540 | * is located after the private area. | ||
| 541 | */ | ||
| 542 | res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; | ||
| 543 | memcpy(res->pub.beacon_ies, ie, ielen); | ||
| 544 | res->pub.len_beacon_ies = ielen; | ||
| 545 | res->pub.information_elements = res->pub.beacon_ies; | ||
| 546 | res->pub.len_information_elements = res->pub.len_beacon_ies; | ||
| 496 | 547 | ||
| 497 | kref_init(&res->ref); | 548 | kref_init(&res->ref); |
| 498 | 549 | ||
| 499 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0); | 550 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); |
| 500 | if (!res) | 551 | if (!res) |
| 501 | return NULL; | 552 | return NULL; |
| 502 | 553 | ||
| @@ -517,7 +568,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
| 517 | struct cfg80211_internal_bss *res; | 568 | struct cfg80211_internal_bss *res; |
| 518 | size_t ielen = len - offsetof(struct ieee80211_mgmt, | 569 | size_t ielen = len - offsetof(struct ieee80211_mgmt, |
| 519 | u.probe_resp.variable); | 570 | u.probe_resp.variable); |
| 520 | bool overwrite; | ||
| 521 | size_t privsz = wiphy->bss_priv_size; | 571 | size_t privsz = wiphy->bss_priv_size; |
| 522 | 572 | ||
| 523 | if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && | 573 | if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && |
| @@ -538,16 +588,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
| 538 | res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | 588 | res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); |
| 539 | res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); | 589 | res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); |
| 540 | res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); | 590 | res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); |
| 541 | /* point to after the private area */ | 591 | /* |
| 542 | res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; | 592 | * The initial buffer for the IEs is allocated with the BSS entry and |
| 543 | memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen); | 593 | * is located after the private area. |
| 544 | res->pub.len_information_elements = ielen; | 594 | */ |
| 595 | if (ieee80211_is_probe_resp(mgmt->frame_control)) { | ||
| 596 | res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; | ||
| 597 | memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, | ||
| 598 | ielen); | ||
| 599 | res->pub.len_proberesp_ies = ielen; | ||
| 600 | res->pub.information_elements = res->pub.proberesp_ies; | ||
| 601 | res->pub.len_information_elements = res->pub.len_proberesp_ies; | ||
| 602 | } else { | ||
| 603 | res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; | ||
| 604 | memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); | ||
| 605 | res->pub.len_beacon_ies = ielen; | ||
| 606 | res->pub.information_elements = res->pub.beacon_ies; | ||
| 607 | res->pub.len_information_elements = res->pub.len_beacon_ies; | ||
| 608 | } | ||
| 545 | 609 | ||
| 546 | kref_init(&res->ref); | 610 | kref_init(&res->ref); |
| 547 | 611 | ||
| 548 | overwrite = ieee80211_is_probe_resp(mgmt->frame_control); | 612 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); |
| 549 | |||
| 550 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite); | ||
| 551 | if (!res) | 613 | if (!res) |
| 552 | return NULL; | 614 | return NULL; |
| 553 | 615 | ||
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index dc0fc4989d54..17fde0da1b08 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
| @@ -34,6 +34,44 @@ struct cfg80211_conn { | |||
| 34 | bool auto_auth, prev_bssid_valid; | 34 | bool auto_auth, prev_bssid_valid; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | bool cfg80211_is_all_idle(void) | ||
| 38 | { | ||
| 39 | struct cfg80211_registered_device *rdev; | ||
| 40 | struct wireless_dev *wdev; | ||
| 41 | bool is_all_idle = true; | ||
| 42 | |||
| 43 | mutex_lock(&cfg80211_mutex); | ||
| 44 | |||
| 45 | /* | ||
| 46 | * All devices must be idle as otherwise if you are actively | ||
| 47 | * scanning some new beacon hints could be learned and would | ||
| 48 | * count as new regulatory hints. | ||
| 49 | */ | ||
| 50 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | ||
| 51 | cfg80211_lock_rdev(rdev); | ||
| 52 | list_for_each_entry(wdev, &rdev->netdev_list, list) { | ||
| 53 | wdev_lock(wdev); | ||
| 54 | if (wdev->sme_state != CFG80211_SME_IDLE) | ||
| 55 | is_all_idle = false; | ||
| 56 | wdev_unlock(wdev); | ||
| 57 | } | ||
| 58 | cfg80211_unlock_rdev(rdev); | ||
| 59 | } | ||
| 60 | |||
| 61 | mutex_unlock(&cfg80211_mutex); | ||
| 62 | |||
| 63 | return is_all_idle; | ||
| 64 | } | ||
| 65 | |||
| 66 | static void disconnect_work(struct work_struct *work) | ||
| 67 | { | ||
| 68 | if (!cfg80211_is_all_idle()) | ||
| 69 | return; | ||
| 70 | |||
| 71 | regulatory_hint_disconnect(); | ||
| 72 | } | ||
| 73 | |||
| 74 | static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); | ||
| 37 | 75 | ||
| 38 | static int cfg80211_conn_scan(struct wireless_dev *wdev) | 76 | static int cfg80211_conn_scan(struct wireless_dev *wdev) |
| 39 | { | 77 | { |
| @@ -454,6 +492,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
| 454 | * - and country_ie[1] which is the IE length | 492 | * - and country_ie[1] which is the IE length |
| 455 | */ | 493 | */ |
| 456 | regulatory_hint_11d(wdev->wiphy, | 494 | regulatory_hint_11d(wdev->wiphy, |
| 495 | bss->channel->band, | ||
| 457 | country_ie + 2, | 496 | country_ie + 2, |
| 458 | country_ie[1]); | 497 | country_ie[1]); |
| 459 | } | 498 | } |
| @@ -657,6 +696,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
| 657 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 696 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); |
| 658 | wdev->wext.connect.ssid_len = 0; | 697 | wdev->wext.connect.ssid_len = 0; |
| 659 | #endif | 698 | #endif |
| 699 | |||
| 700 | schedule_work(&cfg80211_disconnect_work); | ||
| 660 | } | 701 | } |
| 661 | 702 | ||
| 662 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | 703 | void cfg80211_disconnected(struct net_device *dev, u16 reason, |
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index efe3c5c92b2d..9f2cef3e0ca0 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c | |||
| @@ -33,10 +33,30 @@ static ssize_t name ## _show(struct device *dev, \ | |||
| 33 | 33 | ||
| 34 | SHOW_FMT(index, "%d", wiphy_idx); | 34 | SHOW_FMT(index, "%d", wiphy_idx); |
| 35 | SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); | 35 | SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); |
| 36 | SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); | ||
| 37 | |||
| 38 | static ssize_t addresses_show(struct device *dev, | ||
| 39 | struct device_attribute *attr, | ||
| 40 | char *buf) | ||
| 41 | { | ||
| 42 | struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; | ||
| 43 | char *start = buf; | ||
| 44 | int i; | ||
| 45 | |||
| 46 | if (!wiphy->addresses) | ||
| 47 | return sprintf(buf, "%pM\n", wiphy->perm_addr); | ||
| 48 | |||
| 49 | for (i = 0; i < wiphy->n_addresses; i++) | ||
| 50 | buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr); | ||
| 51 | |||
| 52 | return buf - start; | ||
| 53 | } | ||
| 36 | 54 | ||
| 37 | static struct device_attribute ieee80211_dev_attrs[] = { | 55 | static struct device_attribute ieee80211_dev_attrs[] = { |
| 38 | __ATTR_RO(index), | 56 | __ATTR_RO(index), |
| 39 | __ATTR_RO(macaddress), | 57 | __ATTR_RO(macaddress), |
| 58 | __ATTR_RO(address_mask), | ||
| 59 | __ATTR_RO(addresses), | ||
| 40 | {} | 60 | {} |
| 41 | }; | 61 | }; |
| 42 | 62 | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c index 59361fdcb5d0..be2ab8c59e3a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
| @@ -227,8 +227,11 @@ unsigned int ieee80211_hdrlen(__le16 fc) | |||
| 227 | if (ieee80211_is_data(fc)) { | 227 | if (ieee80211_is_data(fc)) { |
| 228 | if (ieee80211_has_a4(fc)) | 228 | if (ieee80211_has_a4(fc)) |
| 229 | hdrlen = 30; | 229 | hdrlen = 30; |
| 230 | if (ieee80211_is_data_qos(fc)) | 230 | if (ieee80211_is_data_qos(fc)) { |
| 231 | hdrlen += IEEE80211_QOS_CTL_LEN; | 231 | hdrlen += IEEE80211_QOS_CTL_LEN; |
| 232 | if (ieee80211_has_order(fc)) | ||
| 233 | hdrlen += IEEE80211_HT_CTL_LEN; | ||
| 234 | } | ||
| 232 | goto out; | 235 | goto out; |
| 233 | } | 236 | } |
| 234 | 237 | ||
| @@ -285,7 +288,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) | |||
| 285 | } | 288 | } |
| 286 | } | 289 | } |
| 287 | 290 | ||
| 288 | int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | 291 | int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, |
| 289 | enum nl80211_iftype iftype) | 292 | enum nl80211_iftype iftype) |
| 290 | { | 293 | { |
| 291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 294 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
| @@ -383,7 +386,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | |||
| 383 | } | 386 | } |
| 384 | EXPORT_SYMBOL(ieee80211_data_to_8023); | 387 | EXPORT_SYMBOL(ieee80211_data_to_8023); |
| 385 | 388 | ||
| 386 | int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | 389 | int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, |
| 387 | enum nl80211_iftype iftype, u8 *bssid, bool qos) | 390 | enum nl80211_iftype iftype, u8 *bssid, bool qos) |
| 388 | { | 391 | { |
| 389 | struct ieee80211_hdr hdr; | 392 | struct ieee80211_hdr hdr; |
| @@ -497,6 +500,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | |||
| 497 | } | 500 | } |
| 498 | EXPORT_SYMBOL(ieee80211_data_from_8023); | 501 | EXPORT_SYMBOL(ieee80211_data_from_8023); |
| 499 | 502 | ||
| 503 | |||
| 504 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | ||
| 505 | const u8 *addr, enum nl80211_iftype iftype, | ||
| 506 | const unsigned int extra_headroom) | ||
| 507 | { | ||
| 508 | struct sk_buff *frame = NULL; | ||
| 509 | u16 ethertype; | ||
| 510 | u8 *payload; | ||
| 511 | const struct ethhdr *eth; | ||
| 512 | int remaining, err; | ||
| 513 | u8 dst[ETH_ALEN], src[ETH_ALEN]; | ||
| 514 | |||
| 515 | err = ieee80211_data_to_8023(skb, addr, iftype); | ||
| 516 | if (err) | ||
| 517 | goto out; | ||
| 518 | |||
| 519 | /* skip the wrapping header */ | ||
| 520 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); | ||
| 521 | if (!eth) | ||
| 522 | goto out; | ||
| 523 | |||
| 524 | while (skb != frame) { | ||
| 525 | u8 padding; | ||
| 526 | __be16 len = eth->h_proto; | ||
| 527 | unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); | ||
| 528 | |||
| 529 | remaining = skb->len; | ||
| 530 | memcpy(dst, eth->h_dest, ETH_ALEN); | ||
| 531 | memcpy(src, eth->h_source, ETH_ALEN); | ||
| 532 | |||
| 533 | padding = (4 - subframe_len) & 0x3; | ||
| 534 | /* the last MSDU has no padding */ | ||
| 535 | if (subframe_len > remaining) | ||
| 536 | goto purge; | ||
| 537 | |||
| 538 | skb_pull(skb, sizeof(struct ethhdr)); | ||
| 539 | /* reuse skb for the last subframe */ | ||
| 540 | if (remaining <= subframe_len + padding) | ||
| 541 | frame = skb; | ||
| 542 | else { | ||
| 543 | unsigned int hlen = ALIGN(extra_headroom, 4); | ||
| 544 | /* | ||
| 545 | * Allocate and reserve two bytes more for payload | ||
| 546 | * alignment since sizeof(struct ethhdr) is 14. | ||
| 547 | */ | ||
| 548 | frame = dev_alloc_skb(hlen + subframe_len + 2); | ||
| 549 | if (!frame) | ||
| 550 | goto purge; | ||
| 551 | |||
| 552 | skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); | ||
| 553 | memcpy(skb_put(frame, ntohs(len)), skb->data, | ||
| 554 | ntohs(len)); | ||
| 555 | |||
| 556 | eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + | ||
| 557 | padding); | ||
| 558 | if (!eth) { | ||
| 559 | dev_kfree_skb(frame); | ||
| 560 | goto purge; | ||
| 561 | } | ||
| 562 | } | ||
| 563 | |||
| 564 | skb_reset_network_header(frame); | ||
| 565 | frame->dev = skb->dev; | ||
| 566 | frame->priority = skb->priority; | ||
| 567 | |||
| 568 | payload = frame->data; | ||
| 569 | ethertype = (payload[6] << 8) | payload[7]; | ||
| 570 | |||
| 571 | if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && | ||
| 572 | ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || | ||
| 573 | compare_ether_addr(payload, | ||
| 574 | bridge_tunnel_header) == 0)) { | ||
| 575 | /* remove RFC1042 or Bridge-Tunnel | ||
| 576 | * encapsulation and replace EtherType */ | ||
| 577 | skb_pull(frame, 6); | ||
| 578 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
| 579 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
| 580 | } else { | ||
| 581 | memcpy(skb_push(frame, sizeof(__be16)), &len, | ||
| 582 | sizeof(__be16)); | ||
| 583 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
| 584 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
| 585 | } | ||
| 586 | __skb_queue_tail(list, frame); | ||
| 587 | } | ||
| 588 | |||
| 589 | return; | ||
| 590 | |||
| 591 | purge: | ||
| 592 | __skb_queue_purge(list); | ||
| 593 | out: | ||
| 594 | dev_kfree_skb(skb); | ||
| 595 | } | ||
| 596 | EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); | ||
| 597 | |||
| 500 | /* Given a data frame determine the 802.1p/1d tag to use. */ | 598 | /* Given a data frame determine the 802.1p/1d tag to use. */ |
| 501 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) | 599 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) |
| 502 | { | 600 | { |
| @@ -720,3 +818,36 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
| 720 | 818 | ||
| 721 | return err; | 819 | return err; |
| 722 | } | 820 | } |
| 821 | |||
| 822 | u16 cfg80211_calculate_bitrate(struct rate_info *rate) | ||
| 823 | { | ||
| 824 | int modulation, streams, bitrate; | ||
| 825 | |||
| 826 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
| 827 | return rate->legacy; | ||
| 828 | |||
| 829 | /* the formula below does only work for MCS values smaller than 32 */ | ||
| 830 | if (rate->mcs >= 32) | ||
| 831 | return 0; | ||
| 832 | |||
| 833 | modulation = rate->mcs & 7; | ||
| 834 | streams = (rate->mcs >> 3) + 1; | ||
| 835 | |||
| 836 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
| 837 | 13500000 : 6500000; | ||
| 838 | |||
| 839 | if (modulation < 4) | ||
| 840 | bitrate *= (modulation + 1); | ||
| 841 | else if (modulation == 4) | ||
| 842 | bitrate *= (modulation + 2); | ||
| 843 | else | ||
| 844 | bitrate *= (modulation + 3); | ||
| 845 | |||
| 846 | bitrate *= streams; | ||
| 847 | |||
| 848 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
| 849 | bitrate = (bitrate / 9) * 10; | ||
| 850 | |||
| 851 | /* do NOT round down here */ | ||
| 852 | return (bitrate + 50000) / 100000; | ||
| 853 | } | ||
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 54face3d4424..9ab51838849e 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
| @@ -1099,8 +1099,8 @@ int cfg80211_wext_siwpower(struct net_device *dev, | |||
| 1099 | { | 1099 | { |
| 1100 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1100 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 1101 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | 1101 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
| 1102 | bool ps = wdev->wext.ps; | 1102 | bool ps = wdev->ps; |
| 1103 | int timeout = wdev->wext.ps_timeout; | 1103 | int timeout = wdev->ps_timeout; |
| 1104 | int err; | 1104 | int err; |
| 1105 | 1105 | ||
| 1106 | if (wdev->iftype != NL80211_IFTYPE_STATION) | 1106 | if (wdev->iftype != NL80211_IFTYPE_STATION) |
| @@ -1133,8 +1133,8 @@ int cfg80211_wext_siwpower(struct net_device *dev, | |||
| 1133 | if (err) | 1133 | if (err) |
| 1134 | return err; | 1134 | return err; |
| 1135 | 1135 | ||
| 1136 | wdev->wext.ps = ps; | 1136 | wdev->ps = ps; |
| 1137 | wdev->wext.ps_timeout = timeout; | 1137 | wdev->ps_timeout = timeout; |
| 1138 | 1138 | ||
| 1139 | return 0; | 1139 | return 0; |
| 1140 | 1140 | ||
| @@ -1147,7 +1147,7 @@ int cfg80211_wext_giwpower(struct net_device *dev, | |||
| 1147 | { | 1147 | { |
| 1148 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1148 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 1149 | 1149 | ||
| 1150 | wrq->disabled = !wdev->wext.ps; | 1150 | wrq->disabled = !wdev->ps; |
| 1151 | 1151 | ||
| 1152 | return 0; | 1152 | return 0; |
| 1153 | } | 1153 | } |
| @@ -1204,21 +1204,47 @@ int cfg80211_wext_siwrate(struct net_device *dev, | |||
| 1204 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1204 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 1205 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | 1205 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
| 1206 | struct cfg80211_bitrate_mask mask; | 1206 | struct cfg80211_bitrate_mask mask; |
| 1207 | u32 fixed, maxrate; | ||
| 1208 | struct ieee80211_supported_band *sband; | ||
| 1209 | int band, ridx; | ||
| 1210 | bool match = false; | ||
| 1207 | 1211 | ||
| 1208 | if (!rdev->ops->set_bitrate_mask) | 1212 | if (!rdev->ops->set_bitrate_mask) |
| 1209 | return -EOPNOTSUPP; | 1213 | return -EOPNOTSUPP; |
| 1210 | 1214 | ||
| 1211 | mask.fixed = 0; | 1215 | memset(&mask, 0, sizeof(mask)); |
| 1212 | mask.maxrate = 0; | 1216 | fixed = 0; |
| 1217 | maxrate = (u32)-1; | ||
| 1213 | 1218 | ||
| 1214 | if (rate->value < 0) { | 1219 | if (rate->value < 0) { |
| 1215 | /* nothing */ | 1220 | /* nothing */ |
| 1216 | } else if (rate->fixed) { | 1221 | } else if (rate->fixed) { |
| 1217 | mask.fixed = rate->value / 1000; /* kbps */ | 1222 | fixed = rate->value / 100000; |
| 1218 | } else { | 1223 | } else { |
| 1219 | mask.maxrate = rate->value / 1000; /* kbps */ | 1224 | maxrate = rate->value / 100000; |
| 1220 | } | 1225 | } |
| 1221 | 1226 | ||
| 1227 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
| 1228 | sband = wdev->wiphy->bands[band]; | ||
| 1229 | if (sband == NULL) | ||
| 1230 | continue; | ||
| 1231 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { | ||
| 1232 | struct ieee80211_rate *srate = &sband->bitrates[ridx]; | ||
| 1233 | if (fixed == srate->bitrate) { | ||
| 1234 | mask.control[band].legacy = 1 << ridx; | ||
| 1235 | match = true; | ||
| 1236 | break; | ||
| 1237 | } | ||
| 1238 | if (srate->bitrate <= maxrate) { | ||
| 1239 | mask.control[band].legacy |= 1 << ridx; | ||
| 1240 | match = true; | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | if (!match) | ||
| 1246 | return -EINVAL; | ||
| 1247 | |||
| 1222 | return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); | 1248 | return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); |
| 1223 | } | 1249 | } |
| 1224 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate); | 1250 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate); |
| @@ -1257,10 +1283,7 @@ int cfg80211_wext_giwrate(struct net_device *dev, | |||
| 1257 | if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) | 1283 | if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) |
| 1258 | return -EOPNOTSUPP; | 1284 | return -EOPNOTSUPP; |
| 1259 | 1285 | ||
| 1260 | rate->value = 0; | 1286 | rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); |
| 1261 | |||
| 1262 | if (!(sinfo.txrate.flags & RATE_INFO_FLAGS_MCS)) | ||
| 1263 | rate->value = 100000 * sinfo.txrate.legacy; | ||
| 1264 | 1287 | ||
| 1265 | return 0; | 1288 | return 0; |
| 1266 | } | 1289 | } |
diff --git a/net/wireless/wext-proc.c b/net/wireless/wext-proc.c index 273a7f77c834..8bafa31fa9f8 100644 --- a/net/wireless/wext-proc.c +++ b/net/wireless/wext-proc.c | |||
| @@ -140,7 +140,7 @@ static const struct file_operations wireless_seq_fops = { | |||
| 140 | .release = seq_release_net, | 140 | .release = seq_release_net, |
| 141 | }; | 141 | }; |
| 142 | 142 | ||
| 143 | int wext_proc_init(struct net *net) | 143 | int __net_init wext_proc_init(struct net *net) |
| 144 | { | 144 | { |
| 145 | /* Create /proc/net/wireless entry */ | 145 | /* Create /proc/net/wireless entry */ |
| 146 | if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) | 146 | if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) |
| @@ -149,7 +149,7 @@ int wext_proc_init(struct net *net) | |||
| 149 | return 0; | 149 | return 0; |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | void wext_proc_exit(struct net *net) | 152 | void __net_exit wext_proc_exit(struct net *net) |
| 153 | { | 153 | { |
| 154 | proc_net_remove(net, "wireless"); | 154 | proc_net_remove(net, "wireless"); |
| 155 | } | 155 | } |
