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.h | 5 | ||||
-rw-r--r-- | net/wireless/db.txt | 17 | ||||
-rw-r--r-- | net/wireless/genregdb.awk | 118 | ||||
-rw-r--r-- | net/wireless/mlme.c | 48 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 282 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 15 | ||||
-rw-r--r-- | net/wireless/reg.c | 209 | ||||
-rw-r--r-- | net/wireless/regdb.h | 7 | ||||
-rw-r--r-- | net/wireless/util.c | 132 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 5 |
14 files changed, 717 insertions, 182 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.h b/net/wireless/core.h index 4ef3efc94106..30ec95f05b52 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -374,10 +374,15 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | |||
374 | struct ieee80211_channel * | 374 | struct ieee80211_channel * |
375 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, | 375 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, |
376 | struct wireless_dev *for_wdev); | 376 | struct wireless_dev *for_wdev); |
377 | struct ieee80211_channel * | ||
378 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | ||
379 | int freq, enum nl80211_channel_type channel_type); | ||
377 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 380 | int rdev_set_freq(struct cfg80211_registered_device *rdev, |
378 | struct wireless_dev *for_wdev, | 381 | struct wireless_dev *for_wdev, |
379 | int freq, enum nl80211_channel_type channel_type); | 382 | int freq, enum nl80211_channel_type channel_type); |
380 | 383 | ||
384 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); | ||
385 | |||
381 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS | 386 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS |
382 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) | 387 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) |
383 | #else | 388 | #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..8316cf075ce9 --- /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/mlme.c b/net/wireless/mlme.c index 82e6002c8d67..94d151f6f73e 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,40 @@ 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); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a6028433e3a0..e3bee3cecdfa 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -141,6 +141,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | 141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, |
142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | 142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, |
143 | .len = WLAN_PMKID_LEN }, | 143 | .len = WLAN_PMKID_LEN }, |
144 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | ||
145 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | ||
144 | }; | 146 | }; |
145 | 147 | ||
146 | /* policy for the attributes */ | 148 | /* policy for the attributes */ |
@@ -569,6 +571,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
569 | CMD(set_pmksa, SET_PMKSA); | 571 | CMD(set_pmksa, SET_PMKSA); |
570 | CMD(del_pmksa, DEL_PMKSA); | 572 | CMD(del_pmksa, DEL_PMKSA); |
571 | CMD(flush_pmksa, FLUSH_PMKSA); | 573 | CMD(flush_pmksa, FLUSH_PMKSA); |
574 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | ||
572 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 575 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
573 | i++; | 576 | i++; |
574 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 577 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
@@ -1637,42 +1640,9 @@ static int parse_station_flags(struct genl_info *info, | |||
1637 | return 0; | 1640 | return 0; |
1638 | } | 1641 | } |
1639 | 1642 | ||
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, | 1643 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
1674 | int flags, struct net_device *dev, | 1644 | int flags, struct net_device *dev, |
1675 | u8 *mac_addr, struct station_info *sinfo) | 1645 | const u8 *mac_addr, struct station_info *sinfo) |
1676 | { | 1646 | { |
1677 | void *hdr; | 1647 | void *hdr; |
1678 | struct nlattr *sinfoattr, *txrate; | 1648 | struct nlattr *sinfoattr, *txrate; |
@@ -1716,8 +1686,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
1716 | if (!txrate) | 1686 | if (!txrate) |
1717 | goto nla_put_failure; | 1687 | goto nla_put_failure; |
1718 | 1688 | ||
1719 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | 1689 | /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
1720 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | 1690 | bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); |
1721 | if (bitrate > 0) | 1691 | if (bitrate > 0) |
1722 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | 1692 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); |
1723 | 1693 | ||
@@ -2583,12 +2553,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
2583 | 2553 | ||
2584 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | 2554 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); |
2585 | 2555 | ||
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); | 2556 | r = regulatory_hint_user(data); |
2593 | 2557 | ||
2594 | return r; | 2558 | return r; |
@@ -4322,6 +4286,143 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | |||
4322 | 4286 | ||
4323 | } | 4287 | } |
4324 | 4288 | ||
4289 | static int nl80211_remain_on_channel(struct sk_buff *skb, | ||
4290 | struct genl_info *info) | ||
4291 | { | ||
4292 | struct cfg80211_registered_device *rdev; | ||
4293 | struct net_device *dev; | ||
4294 | struct ieee80211_channel *chan; | ||
4295 | struct sk_buff *msg; | ||
4296 | void *hdr; | ||
4297 | u64 cookie; | ||
4298 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4299 | u32 freq, duration; | ||
4300 | int err; | ||
4301 | |||
4302 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
4303 | !info->attrs[NL80211_ATTR_DURATION]) | ||
4304 | return -EINVAL; | ||
4305 | |||
4306 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
4307 | |||
4308 | /* | ||
4309 | * We should be on that channel for at least one jiffie, | ||
4310 | * and more than 5 seconds seems excessive. | ||
4311 | */ | ||
4312 | if (!duration || !msecs_to_jiffies(duration) || duration > 5000) | ||
4313 | return -EINVAL; | ||
4314 | |||
4315 | rtnl_lock(); | ||
4316 | |||
4317 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4318 | if (err) | ||
4319 | goto unlock_rtnl; | ||
4320 | |||
4321 | if (!rdev->ops->remain_on_channel) { | ||
4322 | err = -EOPNOTSUPP; | ||
4323 | goto out; | ||
4324 | } | ||
4325 | |||
4326 | if (!netif_running(dev)) { | ||
4327 | err = -ENETDOWN; | ||
4328 | goto out; | ||
4329 | } | ||
4330 | |||
4331 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4332 | channel_type = nla_get_u32( | ||
4333 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4334 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4335 | channel_type != NL80211_CHAN_HT20 && | ||
4336 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4337 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4338 | err = -EINVAL; | ||
4339 | goto out; | ||
4340 | } | ||
4341 | |||
4342 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4343 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4344 | if (chan == NULL) { | ||
4345 | err = -EINVAL; | ||
4346 | goto out; | ||
4347 | } | ||
4348 | |||
4349 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4350 | if (!msg) { | ||
4351 | err = -ENOMEM; | ||
4352 | goto out; | ||
4353 | } | ||
4354 | |||
4355 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4356 | NL80211_CMD_REMAIN_ON_CHANNEL); | ||
4357 | |||
4358 | if (IS_ERR(hdr)) { | ||
4359 | err = PTR_ERR(hdr); | ||
4360 | goto free_msg; | ||
4361 | } | ||
4362 | |||
4363 | err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, | ||
4364 | channel_type, duration, &cookie); | ||
4365 | |||
4366 | if (err) | ||
4367 | goto free_msg; | ||
4368 | |||
4369 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4370 | |||
4371 | genlmsg_end(msg, hdr); | ||
4372 | err = genlmsg_reply(msg, info); | ||
4373 | goto out; | ||
4374 | |||
4375 | nla_put_failure: | ||
4376 | err = -ENOBUFS; | ||
4377 | free_msg: | ||
4378 | nlmsg_free(msg); | ||
4379 | out: | ||
4380 | cfg80211_unlock_rdev(rdev); | ||
4381 | dev_put(dev); | ||
4382 | unlock_rtnl: | ||
4383 | rtnl_unlock(); | ||
4384 | return err; | ||
4385 | } | ||
4386 | |||
4387 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | ||
4388 | struct genl_info *info) | ||
4389 | { | ||
4390 | struct cfg80211_registered_device *rdev; | ||
4391 | struct net_device *dev; | ||
4392 | u64 cookie; | ||
4393 | int err; | ||
4394 | |||
4395 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
4396 | return -EINVAL; | ||
4397 | |||
4398 | rtnl_lock(); | ||
4399 | |||
4400 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4401 | if (err) | ||
4402 | goto unlock_rtnl; | ||
4403 | |||
4404 | if (!rdev->ops->cancel_remain_on_channel) { | ||
4405 | err = -EOPNOTSUPP; | ||
4406 | goto out; | ||
4407 | } | ||
4408 | |||
4409 | if (!netif_running(dev)) { | ||
4410 | err = -ENETDOWN; | ||
4411 | goto out; | ||
4412 | } | ||
4413 | |||
4414 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
4415 | |||
4416 | err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); | ||
4417 | |||
4418 | out: | ||
4419 | cfg80211_unlock_rdev(rdev); | ||
4420 | dev_put(dev); | ||
4421 | unlock_rtnl: | ||
4422 | rtnl_unlock(); | ||
4423 | return err; | ||
4424 | } | ||
4425 | |||
4325 | static struct genl_ops nl80211_ops[] = { | 4426 | static struct genl_ops nl80211_ops[] = { |
4326 | { | 4427 | { |
4327 | .cmd = NL80211_CMD_GET_WIPHY, | 4428 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4584,8 +4685,20 @@ static struct genl_ops nl80211_ops[] = { | |||
4584 | .policy = nl80211_policy, | 4685 | .policy = nl80211_policy, |
4585 | .flags = GENL_ADMIN_PERM, | 4686 | .flags = GENL_ADMIN_PERM, |
4586 | }, | 4687 | }, |
4587 | 4688 | { | |
4689 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | ||
4690 | .doit = nl80211_remain_on_channel, | ||
4691 | .policy = nl80211_policy, | ||
4692 | .flags = GENL_ADMIN_PERM, | ||
4693 | }, | ||
4694 | { | ||
4695 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
4696 | .doit = nl80211_cancel_remain_on_channel, | ||
4697 | .policy = nl80211_policy, | ||
4698 | .flags = GENL_ADMIN_PERM, | ||
4699 | }, | ||
4588 | }; | 4700 | }; |
4701 | |||
4589 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 4702 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4590 | .name = "mlme", | 4703 | .name = "mlme", |
4591 | }; | 4704 | }; |
@@ -5173,6 +5286,89 @@ nla_put_failure: | |||
5173 | nlmsg_free(msg); | 5286 | nlmsg_free(msg); |
5174 | } | 5287 | } |
5175 | 5288 | ||
5289 | static void nl80211_send_remain_on_chan_event( | ||
5290 | int cmd, struct cfg80211_registered_device *rdev, | ||
5291 | struct net_device *netdev, u64 cookie, | ||
5292 | struct ieee80211_channel *chan, | ||
5293 | enum nl80211_channel_type channel_type, | ||
5294 | unsigned int duration, gfp_t gfp) | ||
5295 | { | ||
5296 | struct sk_buff *msg; | ||
5297 | void *hdr; | ||
5298 | |||
5299 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5300 | if (!msg) | ||
5301 | return; | ||
5302 | |||
5303 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); | ||
5304 | if (!hdr) { | ||
5305 | nlmsg_free(msg); | ||
5306 | return; | ||
5307 | } | ||
5308 | |||
5309 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5310 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5311 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); | ||
5312 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); | ||
5313 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5314 | |||
5315 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) | ||
5316 | NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); | ||
5317 | |||
5318 | if (genlmsg_end(msg, hdr) < 0) { | ||
5319 | nlmsg_free(msg); | ||
5320 | return; | ||
5321 | } | ||
5322 | |||
5323 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5324 | nl80211_mlme_mcgrp.id, gfp); | ||
5325 | return; | ||
5326 | |||
5327 | nla_put_failure: | ||
5328 | genlmsg_cancel(msg, hdr); | ||
5329 | nlmsg_free(msg); | ||
5330 | } | ||
5331 | |||
5332 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
5333 | struct net_device *netdev, u64 cookie, | ||
5334 | struct ieee80211_channel *chan, | ||
5335 | enum nl80211_channel_type channel_type, | ||
5336 | unsigned int duration, gfp_t gfp) | ||
5337 | { | ||
5338 | nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, | ||
5339 | rdev, netdev, cookie, chan, | ||
5340 | channel_type, duration, gfp); | ||
5341 | } | ||
5342 | |||
5343 | void nl80211_send_remain_on_channel_cancel( | ||
5344 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
5345 | u64 cookie, struct ieee80211_channel *chan, | ||
5346 | enum nl80211_channel_type channel_type, gfp_t gfp) | ||
5347 | { | ||
5348 | nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
5349 | rdev, netdev, cookie, chan, | ||
5350 | channel_type, 0, gfp); | ||
5351 | } | ||
5352 | |||
5353 | void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | ||
5354 | struct net_device *dev, const u8 *mac_addr, | ||
5355 | struct station_info *sinfo, gfp_t gfp) | ||
5356 | { | ||
5357 | struct sk_buff *msg; | ||
5358 | |||
5359 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
5360 | if (!msg) | ||
5361 | return; | ||
5362 | |||
5363 | if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { | ||
5364 | nlmsg_free(msg); | ||
5365 | return; | ||
5366 | } | ||
5367 | |||
5368 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5369 | nl80211_mlme_mcgrp.id, gfp); | ||
5370 | } | ||
5371 | |||
5176 | /* initialisation/exit functions */ | 5372 | /* initialisation/exit functions */ |
5177 | 5373 | ||
5178 | int nl80211_init(void) | 5374 | int nl80211_init(void) |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 44cc2a76a1b0..14855b8fb430 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -59,4 +59,19 @@ 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 | |||
62 | #endif /* __NET_WIRELESS_NL80211_H */ | 77 | #endif /* __NET_WIRELESS_NL80211_H */ |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index baa898add287..87ea60d84c3c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -40,6 +40,7 @@ | |||
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 | ||
45 | /* Receipt of information from last regulatory request */ | 46 | /* Receipt of information from last regulatory request */ |
@@ -128,78 +129,6 @@ static char *ieee80211_regdom = "00"; | |||
128 | module_param(ieee80211_regdom, charp, 0444); | 129 | module_param(ieee80211_regdom, charp, 0444); |
129 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 130 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
130 | 131 | ||
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) | 132 | static void reset_regdomains(void) |
204 | { | 133 | { |
205 | /* avoid freeing static information or freeing something twice */ | 134 | /* avoid freeing static information or freeing something twice */ |
@@ -209,8 +138,6 @@ static void reset_regdomains(void) | |||
209 | cfg80211_world_regdom = NULL; | 138 | cfg80211_world_regdom = NULL; |
210 | if (cfg80211_regdomain == &world_regdom) | 139 | if (cfg80211_regdomain == &world_regdom) |
211 | cfg80211_regdomain = NULL; | 140 | cfg80211_regdomain = NULL; |
212 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
213 | cfg80211_regdomain = NULL; | ||
214 | 141 | ||
215 | kfree(cfg80211_regdomain); | 142 | kfree(cfg80211_regdomain); |
216 | kfree(cfg80211_world_regdom); | 143 | kfree(cfg80211_world_regdom); |
@@ -335,6 +262,98 @@ static bool country_ie_integrity_changes(u32 checksum) | |||
335 | return false; | 262 | return false; |
336 | } | 263 | } |
337 | 264 | ||
265 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
266 | const struct ieee80211_regdomain *src_regd) | ||
267 | { | ||
268 | struct ieee80211_regdomain *regd; | ||
269 | int size_of_regd = 0; | ||
270 | unsigned int i; | ||
271 | |||
272 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
273 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
274 | |||
275 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
276 | if (!regd) | ||
277 | return -ENOMEM; | ||
278 | |||
279 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
280 | |||
281 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
282 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
283 | sizeof(struct ieee80211_reg_rule)); | ||
284 | |||
285 | *dst_regd = regd; | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB | ||
290 | struct reg_regdb_search_request { | ||
291 | char alpha2[2]; | ||
292 | struct list_head list; | ||
293 | }; | ||
294 | |||
295 | static LIST_HEAD(reg_regdb_search_list); | ||
296 | static DEFINE_SPINLOCK(reg_regdb_search_lock); | ||
297 | |||
298 | static void reg_regdb_search(struct work_struct *work) | ||
299 | { | ||
300 | struct reg_regdb_search_request *request; | ||
301 | const struct ieee80211_regdomain *curdom, *regdom; | ||
302 | int i, r; | ||
303 | |||
304 | spin_lock(®_regdb_search_lock); | ||
305 | while (!list_empty(®_regdb_search_list)) { | ||
306 | request = list_first_entry(®_regdb_search_list, | ||
307 | struct reg_regdb_search_request, | ||
308 | list); | ||
309 | list_del(&request->list); | ||
310 | |||
311 | for (i=0; i<reg_regdb_size; i++) { | ||
312 | curdom = reg_regdb[i]; | ||
313 | |||
314 | if (!memcmp(request->alpha2, curdom->alpha2, 2)) { | ||
315 | r = reg_copy_regd(®dom, curdom); | ||
316 | if (r) | ||
317 | break; | ||
318 | spin_unlock(®_regdb_search_lock); | ||
319 | mutex_lock(&cfg80211_mutex); | ||
320 | set_regdom(regdom); | ||
321 | mutex_unlock(&cfg80211_mutex); | ||
322 | spin_lock(®_regdb_search_lock); | ||
323 | break; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | kfree(request); | ||
328 | } | ||
329 | spin_unlock(®_regdb_search_lock); | ||
330 | } | ||
331 | |||
332 | static DECLARE_WORK(reg_regdb_work, reg_regdb_search); | ||
333 | |||
334 | static void reg_regdb_query(const char *alpha2) | ||
335 | { | ||
336 | struct reg_regdb_search_request *request; | ||
337 | |||
338 | if (!alpha2) | ||
339 | return; | ||
340 | |||
341 | request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); | ||
342 | if (!request) | ||
343 | return; | ||
344 | |||
345 | memcpy(request->alpha2, alpha2, 2); | ||
346 | |||
347 | spin_lock(®_regdb_search_lock); | ||
348 | list_add_tail(&request->list, ®_regdb_search_list); | ||
349 | spin_unlock(®_regdb_search_lock); | ||
350 | |||
351 | schedule_work(®_regdb_work); | ||
352 | } | ||
353 | #else | ||
354 | static inline void reg_regdb_query(const char *alpha2) {} | ||
355 | #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ | ||
356 | |||
338 | /* | 357 | /* |
339 | * This lets us keep regulatory code which is updated on a regulatory | 358 | * This lets us keep regulatory code which is updated on a regulatory |
340 | * basis in userspace. | 359 | * basis in userspace. |
@@ -354,6 +373,9 @@ static int call_crda(const char *alpha2) | |||
354 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " | 373 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " |
355 | "regulatory domain\n"); | 374 | "regulatory domain\n"); |
356 | 375 | ||
376 | /* query internal regulatory database (if it exists) */ | ||
377 | reg_regdb_query(alpha2); | ||
378 | |||
357 | country_env[8] = alpha2[0]; | 379 | country_env[8] = alpha2[0]; |
358 | country_env[9] = alpha2[1]; | 380 | country_env[9] = alpha2[1]; |
359 | 381 | ||
@@ -1342,30 +1364,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1342 | } | 1364 | } |
1343 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1365 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1344 | 1366 | ||
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 | /* | 1367 | /* |
1370 | * Return value which can be used by ignore_request() to indicate | 1368 | * Return value which can be used by ignore_request() to indicate |
1371 | * it has been determined we should intersect two regulatory domains | 1369 | * it has been determined we should intersect two regulatory domains |
@@ -1418,8 +1416,6 @@ static int ignore_request(struct wiphy *wiphy, | |||
1418 | return REG_INTERSECT; | 1416 | return REG_INTERSECT; |
1419 | case NL80211_REGDOM_SET_BY_DRIVER: | 1417 | case NL80211_REGDOM_SET_BY_DRIVER: |
1420 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1418 | 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)) | 1419 | if (regdom_changes(pending_request->alpha2)) |
1424 | return 0; | 1420 | return 0; |
1425 | return -EALREADY; | 1421 | return -EALREADY; |
@@ -1456,8 +1452,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
1456 | return -EAGAIN; | 1452 | return -EAGAIN; |
1457 | } | 1453 | } |
1458 | 1454 | ||
1459 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1455 | if (!regdom_changes(pending_request->alpha2)) |
1460 | !regdom_changes(pending_request->alpha2)) | ||
1461 | return -EALREADY; | 1456 | return -EALREADY; |
1462 | 1457 | ||
1463 | return 0; | 1458 | return 0; |
@@ -2039,8 +2034,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2039 | * If someone else asked us to change the rd lets only bother | 2034 | * If someone else asked us to change the rd lets only bother |
2040 | * checking if the alpha2 changes if CRDA was already called | 2035 | * checking if the alpha2 changes if CRDA was already called |
2041 | */ | 2036 | */ |
2042 | if (!is_old_static_regdom(cfg80211_regdomain) && | 2037 | if (!regdom_changes(rd->alpha2)) |
2043 | !regdom_changes(rd->alpha2)) | ||
2044 | return -EINVAL; | 2038 | return -EINVAL; |
2045 | } | 2039 | } |
2046 | 2040 | ||
@@ -2239,15 +2233,8 @@ int regulatory_init(void) | |||
2239 | spin_lock_init(®_requests_lock); | 2233 | spin_lock_init(®_requests_lock); |
2240 | spin_lock_init(®_pending_beacons_lock); | 2234 | spin_lock_init(®_pending_beacons_lock); |
2241 | 2235 | ||
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; | 2236 | cfg80211_regdomain = cfg80211_world_regdom; |
2249 | 2237 | ||
2250 | #endif | ||
2251 | /* We always try to get an update for the static regdomain */ | 2238 | /* We always try to get an update for the static regdomain */ |
2252 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2239 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); |
2253 | if (err) { | 2240 | if (err) { |
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/util.c b/net/wireless/util.c index 59361fdcb5d0..23557c1d0a9c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -285,7 +285,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) | |||
285 | } | 285 | } |
286 | } | 286 | } |
287 | 287 | ||
288 | int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | 288 | int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, |
289 | enum nl80211_iftype iftype) | 289 | enum nl80211_iftype iftype) |
290 | { | 290 | { |
291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
@@ -383,7 +383,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | |||
383 | } | 383 | } |
384 | EXPORT_SYMBOL(ieee80211_data_to_8023); | 384 | EXPORT_SYMBOL(ieee80211_data_to_8023); |
385 | 385 | ||
386 | int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | 386 | int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, |
387 | enum nl80211_iftype iftype, u8 *bssid, bool qos) | 387 | enum nl80211_iftype iftype, u8 *bssid, bool qos) |
388 | { | 388 | { |
389 | struct ieee80211_hdr hdr; | 389 | struct ieee80211_hdr hdr; |
@@ -497,6 +497,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | |||
497 | } | 497 | } |
498 | EXPORT_SYMBOL(ieee80211_data_from_8023); | 498 | EXPORT_SYMBOL(ieee80211_data_from_8023); |
499 | 499 | ||
500 | |||
501 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | ||
502 | const u8 *addr, enum nl80211_iftype iftype, | ||
503 | const unsigned int extra_headroom) | ||
504 | { | ||
505 | struct sk_buff *frame = NULL; | ||
506 | u16 ethertype; | ||
507 | u8 *payload; | ||
508 | const struct ethhdr *eth; | ||
509 | int remaining, err; | ||
510 | u8 dst[ETH_ALEN], src[ETH_ALEN]; | ||
511 | |||
512 | err = ieee80211_data_to_8023(skb, addr, iftype); | ||
513 | if (err) | ||
514 | goto out; | ||
515 | |||
516 | /* skip the wrapping header */ | ||
517 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); | ||
518 | if (!eth) | ||
519 | goto out; | ||
520 | |||
521 | while (skb != frame) { | ||
522 | u8 padding; | ||
523 | __be16 len = eth->h_proto; | ||
524 | unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); | ||
525 | |||
526 | remaining = skb->len; | ||
527 | memcpy(dst, eth->h_dest, ETH_ALEN); | ||
528 | memcpy(src, eth->h_source, ETH_ALEN); | ||
529 | |||
530 | padding = (4 - subframe_len) & 0x3; | ||
531 | /* the last MSDU has no padding */ | ||
532 | if (subframe_len > remaining) | ||
533 | goto purge; | ||
534 | |||
535 | skb_pull(skb, sizeof(struct ethhdr)); | ||
536 | /* reuse skb for the last subframe */ | ||
537 | if (remaining <= subframe_len + padding) | ||
538 | frame = skb; | ||
539 | else { | ||
540 | unsigned int hlen = ALIGN(extra_headroom, 4); | ||
541 | /* | ||
542 | * Allocate and reserve two bytes more for payload | ||
543 | * alignment since sizeof(struct ethhdr) is 14. | ||
544 | */ | ||
545 | frame = dev_alloc_skb(hlen + subframe_len + 2); | ||
546 | if (!frame) | ||
547 | goto purge; | ||
548 | |||
549 | skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); | ||
550 | memcpy(skb_put(frame, ntohs(len)), skb->data, | ||
551 | ntohs(len)); | ||
552 | |||
553 | eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + | ||
554 | padding); | ||
555 | if (!eth) { | ||
556 | dev_kfree_skb(frame); | ||
557 | goto purge; | ||
558 | } | ||
559 | } | ||
560 | |||
561 | skb_reset_network_header(frame); | ||
562 | frame->dev = skb->dev; | ||
563 | frame->priority = skb->priority; | ||
564 | |||
565 | payload = frame->data; | ||
566 | ethertype = (payload[6] << 8) | payload[7]; | ||
567 | |||
568 | if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && | ||
569 | ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || | ||
570 | compare_ether_addr(payload, | ||
571 | bridge_tunnel_header) == 0)) { | ||
572 | /* remove RFC1042 or Bridge-Tunnel | ||
573 | * encapsulation and replace EtherType */ | ||
574 | skb_pull(frame, 6); | ||
575 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
576 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
577 | } else { | ||
578 | memcpy(skb_push(frame, sizeof(__be16)), &len, | ||
579 | sizeof(__be16)); | ||
580 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
581 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
582 | } | ||
583 | __skb_queue_tail(list, frame); | ||
584 | } | ||
585 | |||
586 | return; | ||
587 | |||
588 | purge: | ||
589 | __skb_queue_purge(list); | ||
590 | out: | ||
591 | dev_kfree_skb(skb); | ||
592 | } | ||
593 | EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); | ||
594 | |||
500 | /* Given a data frame determine the 802.1p/1d tag to use. */ | 595 | /* Given a data frame determine the 802.1p/1d tag to use. */ |
501 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) | 596 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) |
502 | { | 597 | { |
@@ -720,3 +815,36 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
720 | 815 | ||
721 | return err; | 816 | return err; |
722 | } | 817 | } |
818 | |||
819 | u16 cfg80211_calculate_bitrate(struct rate_info *rate) | ||
820 | { | ||
821 | int modulation, streams, bitrate; | ||
822 | |||
823 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
824 | return rate->legacy; | ||
825 | |||
826 | /* the formula below does only work for MCS values smaller than 32 */ | ||
827 | if (rate->mcs >= 32) | ||
828 | return 0; | ||
829 | |||
830 | modulation = rate->mcs & 7; | ||
831 | streams = (rate->mcs >> 3) + 1; | ||
832 | |||
833 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
834 | 13500000 : 6500000; | ||
835 | |||
836 | if (modulation < 4) | ||
837 | bitrate *= (modulation + 1); | ||
838 | else if (modulation == 4) | ||
839 | bitrate *= (modulation + 2); | ||
840 | else | ||
841 | bitrate *= (modulation + 3); | ||
842 | |||
843 | bitrate *= streams; | ||
844 | |||
845 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
846 | bitrate = (bitrate / 9) * 10; | ||
847 | |||
848 | /* do NOT round down here */ | ||
849 | return (bitrate + 50000) / 100000; | ||
850 | } | ||
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 54face3d4424..4198243a3dff 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
@@ -1257,10 +1257,7 @@ int cfg80211_wext_giwrate(struct net_device *dev, | |||
1257 | if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) | 1257 | if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) |
1258 | return -EOPNOTSUPP; | 1258 | return -EOPNOTSUPP; |
1259 | 1259 | ||
1260 | rate->value = 0; | 1260 | 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 | 1261 | ||
1265 | return 0; | 1262 | return 0; |
1266 | } | 1263 | } |