diff options
Diffstat (limited to 'net/wireless/chan.c')
-rw-r--r-- | net/wireless/chan.c | 313 |
1 files changed, 263 insertions, 50 deletions
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 2f876b9ee344..a7990bb16529 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -9,90 +9,303 @@ | |||
9 | #include <linux/export.h> | 9 | #include <linux/export.h> |
10 | #include <net/cfg80211.h> | 10 | #include <net/cfg80211.h> |
11 | #include "core.h" | 11 | #include "core.h" |
12 | #include "rdev-ops.h" | ||
12 | 13 | ||
13 | struct ieee80211_channel * | 14 | void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, |
14 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | 15 | struct ieee80211_channel *chan, |
15 | int freq, enum nl80211_channel_type channel_type) | 16 | enum nl80211_channel_type chan_type) |
16 | { | 17 | { |
17 | struct ieee80211_channel *chan; | 18 | if (WARN_ON(!chan)) |
18 | struct ieee80211_sta_ht_cap *ht_cap; | 19 | return; |
20 | |||
21 | chandef->chan = chan; | ||
22 | chandef->center_freq2 = 0; | ||
23 | |||
24 | switch (chan_type) { | ||
25 | case NL80211_CHAN_NO_HT: | ||
26 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; | ||
27 | chandef->center_freq1 = chan->center_freq; | ||
28 | break; | ||
29 | case NL80211_CHAN_HT20: | ||
30 | chandef->width = NL80211_CHAN_WIDTH_20; | ||
31 | chandef->center_freq1 = chan->center_freq; | ||
32 | break; | ||
33 | case NL80211_CHAN_HT40PLUS: | ||
34 | chandef->width = NL80211_CHAN_WIDTH_40; | ||
35 | chandef->center_freq1 = chan->center_freq + 10; | ||
36 | break; | ||
37 | case NL80211_CHAN_HT40MINUS: | ||
38 | chandef->width = NL80211_CHAN_WIDTH_40; | ||
39 | chandef->center_freq1 = chan->center_freq - 10; | ||
40 | break; | ||
41 | default: | ||
42 | WARN_ON(1); | ||
43 | } | ||
44 | } | ||
45 | EXPORT_SYMBOL(cfg80211_chandef_create); | ||
46 | |||
47 | bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) | ||
48 | { | ||
49 | u32 control_freq; | ||
50 | |||
51 | if (!chandef->chan) | ||
52 | return false; | ||
19 | 53 | ||
20 | chan = ieee80211_get_channel(&rdev->wiphy, freq); | 54 | control_freq = chandef->chan->center_freq; |
55 | |||
56 | switch (chandef->width) { | ||
57 | case NL80211_CHAN_WIDTH_20: | ||
58 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
59 | if (chandef->center_freq1 != control_freq) | ||
60 | return false; | ||
61 | if (chandef->center_freq2) | ||
62 | return false; | ||
63 | break; | ||
64 | case NL80211_CHAN_WIDTH_40: | ||
65 | if (chandef->center_freq1 != control_freq + 10 && | ||
66 | chandef->center_freq1 != control_freq - 10) | ||
67 | return false; | ||
68 | if (chandef->center_freq2) | ||
69 | return false; | ||
70 | break; | ||
71 | case NL80211_CHAN_WIDTH_80P80: | ||
72 | if (chandef->center_freq1 != control_freq + 30 && | ||
73 | chandef->center_freq1 != control_freq + 10 && | ||
74 | chandef->center_freq1 != control_freq - 10 && | ||
75 | chandef->center_freq1 != control_freq - 30) | ||
76 | return false; | ||
77 | if (!chandef->center_freq2) | ||
78 | return false; | ||
79 | break; | ||
80 | case NL80211_CHAN_WIDTH_80: | ||
81 | if (chandef->center_freq1 != control_freq + 30 && | ||
82 | chandef->center_freq1 != control_freq + 10 && | ||
83 | chandef->center_freq1 != control_freq - 10 && | ||
84 | chandef->center_freq1 != control_freq - 30) | ||
85 | return false; | ||
86 | if (chandef->center_freq2) | ||
87 | return false; | ||
88 | break; | ||
89 | case NL80211_CHAN_WIDTH_160: | ||
90 | if (chandef->center_freq1 != control_freq + 70 && | ||
91 | chandef->center_freq1 != control_freq + 50 && | ||
92 | chandef->center_freq1 != control_freq + 30 && | ||
93 | chandef->center_freq1 != control_freq + 10 && | ||
94 | chandef->center_freq1 != control_freq - 10 && | ||
95 | chandef->center_freq1 != control_freq - 30 && | ||
96 | chandef->center_freq1 != control_freq - 50 && | ||
97 | chandef->center_freq1 != control_freq - 70) | ||
98 | return false; | ||
99 | if (chandef->center_freq2) | ||
100 | return false; | ||
101 | break; | ||
102 | default: | ||
103 | return false; | ||
104 | } | ||
105 | |||
106 | return true; | ||
107 | } | ||
108 | EXPORT_SYMBOL(cfg80211_chandef_valid); | ||
109 | |||
110 | static void chandef_primary_freqs(const struct cfg80211_chan_def *c, | ||
111 | int *pri40, int *pri80) | ||
112 | { | ||
113 | int tmp; | ||
114 | |||
115 | switch (c->width) { | ||
116 | case NL80211_CHAN_WIDTH_40: | ||
117 | *pri40 = c->center_freq1; | ||
118 | *pri80 = 0; | ||
119 | break; | ||
120 | case NL80211_CHAN_WIDTH_80: | ||
121 | case NL80211_CHAN_WIDTH_80P80: | ||
122 | *pri80 = c->center_freq1; | ||
123 | /* n_P20 */ | ||
124 | tmp = (30 + c->chan->center_freq - c->center_freq1)/20; | ||
125 | /* n_P40 */ | ||
126 | tmp /= 2; | ||
127 | /* freq_P40 */ | ||
128 | *pri40 = c->center_freq1 - 20 + 40 * tmp; | ||
129 | break; | ||
130 | case NL80211_CHAN_WIDTH_160: | ||
131 | /* n_P20 */ | ||
132 | tmp = (70 + c->chan->center_freq - c->center_freq1)/20; | ||
133 | /* n_P40 */ | ||
134 | tmp /= 2; | ||
135 | /* freq_P40 */ | ||
136 | *pri40 = c->center_freq1 - 60 + 40 * tmp; | ||
137 | /* n_P80 */ | ||
138 | tmp /= 2; | ||
139 | *pri80 = c->center_freq1 - 40 + 80 * tmp; | ||
140 | break; | ||
141 | default: | ||
142 | WARN_ON_ONCE(1); | ||
143 | } | ||
144 | } | ||
21 | 145 | ||
22 | /* Primary channel not allowed */ | 146 | const struct cfg80211_chan_def * |
23 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | 147 | cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, |
148 | const struct cfg80211_chan_def *c2) | ||
149 | { | ||
150 | u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80; | ||
151 | |||
152 | /* If they are identical, return */ | ||
153 | if (cfg80211_chandef_identical(c1, c2)) | ||
154 | return c1; | ||
155 | |||
156 | /* otherwise, must have same control channel */ | ||
157 | if (c1->chan != c2->chan) | ||
24 | return NULL; | 158 | return NULL; |
25 | 159 | ||
26 | if (channel_type == NL80211_CHAN_HT40MINUS && | 160 | /* |
27 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | 161 | * If they have the same width, but aren't identical, |
162 | * then they can't be compatible. | ||
163 | */ | ||
164 | if (c1->width == c2->width) | ||
28 | return NULL; | 165 | return NULL; |
29 | else if (channel_type == NL80211_CHAN_HT40PLUS && | 166 | |
30 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | 167 | if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || |
168 | c1->width == NL80211_CHAN_WIDTH_20) | ||
169 | return c2; | ||
170 | |||
171 | if (c2->width == NL80211_CHAN_WIDTH_20_NOHT || | ||
172 | c2->width == NL80211_CHAN_WIDTH_20) | ||
173 | return c1; | ||
174 | |||
175 | chandef_primary_freqs(c1, &c1_pri40, &c1_pri80); | ||
176 | chandef_primary_freqs(c2, &c2_pri40, &c2_pri80); | ||
177 | |||
178 | if (c1_pri40 != c2_pri40) | ||
31 | return NULL; | 179 | return NULL; |
32 | 180 | ||
33 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; | 181 | WARN_ON(!c1_pri80 && !c2_pri80); |
182 | if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80) | ||
183 | return NULL; | ||
34 | 184 | ||
35 | if (channel_type != NL80211_CHAN_NO_HT) { | 185 | if (c1->width > c2->width) |
36 | if (!ht_cap->ht_supported) | 186 | return c1; |
37 | return NULL; | 187 | return c2; |
188 | } | ||
189 | EXPORT_SYMBOL(cfg80211_chandef_compatible); | ||
38 | 190 | ||
39 | if (channel_type != NL80211_CHAN_HT20 && | 191 | static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, |
40 | (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | 192 | u32 center_freq, u32 bandwidth, |
41 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) | 193 | u32 prohibited_flags) |
42 | return NULL; | 194 | { |
195 | struct ieee80211_channel *c; | ||
196 | u32 freq; | ||
197 | |||
198 | for (freq = center_freq - bandwidth/2 + 10; | ||
199 | freq <= center_freq + bandwidth/2 - 10; | ||
200 | freq += 20) { | ||
201 | c = ieee80211_get_channel(wiphy, freq); | ||
202 | if (!c || c->flags & prohibited_flags) | ||
203 | return false; | ||
43 | } | 204 | } |
44 | 205 | ||
45 | return chan; | 206 | return true; |
46 | } | 207 | } |
47 | 208 | ||
48 | bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, | 209 | bool cfg80211_chandef_usable(struct wiphy *wiphy, |
49 | struct ieee80211_channel *chan, | 210 | const struct cfg80211_chan_def *chandef, |
50 | enum nl80211_channel_type channel_type) | 211 | u32 prohibited_flags) |
51 | { | 212 | { |
52 | struct ieee80211_channel *sec_chan; | 213 | struct ieee80211_sta_ht_cap *ht_cap; |
53 | int diff; | 214 | struct ieee80211_sta_vht_cap *vht_cap; |
215 | u32 width, control_freq; | ||
54 | 216 | ||
55 | switch (channel_type) { | 217 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) |
56 | case NL80211_CHAN_HT40PLUS: | 218 | return false; |
57 | diff = 20; | 219 | |
220 | ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; | ||
221 | vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; | ||
222 | |||
223 | control_freq = chandef->chan->center_freq; | ||
224 | |||
225 | switch (chandef->width) { | ||
226 | case NL80211_CHAN_WIDTH_20: | ||
227 | if (!ht_cap->ht_supported) | ||
228 | return false; | ||
229 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
230 | width = 20; | ||
58 | break; | 231 | break; |
59 | case NL80211_CHAN_HT40MINUS: | 232 | case NL80211_CHAN_WIDTH_40: |
60 | diff = -20; | 233 | width = 40; |
234 | if (!ht_cap->ht_supported) | ||
235 | return false; | ||
236 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | ||
237 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) | ||
238 | return false; | ||
239 | if (chandef->center_freq1 < control_freq && | ||
240 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | ||
241 | return false; | ||
242 | if (chandef->center_freq1 > control_freq && | ||
243 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | ||
244 | return false; | ||
245 | break; | ||
246 | case NL80211_CHAN_WIDTH_80P80: | ||
247 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) | ||
248 | return false; | ||
249 | case NL80211_CHAN_WIDTH_80: | ||
250 | if (!vht_cap->vht_supported) | ||
251 | return false; | ||
252 | width = 80; | ||
253 | break; | ||
254 | case NL80211_CHAN_WIDTH_160: | ||
255 | if (!vht_cap->vht_supported) | ||
256 | return false; | ||
257 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) | ||
258 | return false; | ||
259 | width = 160; | ||
61 | break; | 260 | break; |
62 | default: | 261 | default: |
63 | return true; | 262 | WARN_ON_ONCE(1); |
263 | return false; | ||
64 | } | 264 | } |
65 | 265 | ||
66 | sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); | 266 | /* TODO: missing regulatory check on 80/160 bandwidth */ |
67 | if (!sec_chan) | ||
68 | return false; | ||
69 | 267 | ||
70 | /* we'll need a DFS capability later */ | 268 | if (width > 20) |
71 | if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | | 269 | prohibited_flags |= IEEE80211_CHAN_NO_OFDM; |
72 | IEEE80211_CHAN_PASSIVE_SCAN | | 270 | |
73 | IEEE80211_CHAN_NO_IBSS | | 271 | if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1, |
74 | IEEE80211_CHAN_RADAR)) | 272 | width, prohibited_flags)) |
75 | return false; | 273 | return false; |
76 | 274 | ||
77 | return true; | 275 | if (!chandef->center_freq2) |
276 | return true; | ||
277 | return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2, | ||
278 | width, prohibited_flags); | ||
78 | } | 279 | } |
79 | EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan); | 280 | EXPORT_SYMBOL(cfg80211_chandef_usable); |
80 | 281 | ||
81 | int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, | 282 | bool cfg80211_reg_can_beacon(struct wiphy *wiphy, |
82 | int freq, enum nl80211_channel_type chantype) | 283 | struct cfg80211_chan_def *chandef) |
83 | { | 284 | { |
84 | struct ieee80211_channel *chan; | 285 | bool res; |
286 | |||
287 | trace_cfg80211_reg_can_beacon(wiphy, chandef); | ||
85 | 288 | ||
289 | res = cfg80211_chandef_usable(wiphy, chandef, | ||
290 | IEEE80211_CHAN_DISABLED | | ||
291 | IEEE80211_CHAN_PASSIVE_SCAN | | ||
292 | IEEE80211_CHAN_NO_IBSS | | ||
293 | IEEE80211_CHAN_RADAR); | ||
294 | |||
295 | trace_cfg80211_return_bool(res); | ||
296 | return res; | ||
297 | } | ||
298 | EXPORT_SYMBOL(cfg80211_reg_can_beacon); | ||
299 | |||
300 | int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, | ||
301 | struct cfg80211_chan_def *chandef) | ||
302 | { | ||
86 | if (!rdev->ops->set_monitor_channel) | 303 | if (!rdev->ops->set_monitor_channel) |
87 | return -EOPNOTSUPP; | 304 | return -EOPNOTSUPP; |
88 | if (!cfg80211_has_monitors_only(rdev)) | 305 | if (!cfg80211_has_monitors_only(rdev)) |
89 | return -EBUSY; | 306 | return -EBUSY; |
90 | 307 | ||
91 | chan = rdev_freq_to_chan(rdev, freq, chantype); | 308 | return rdev_set_monitor_channel(rdev, chandef); |
92 | if (!chan) | ||
93 | return -EINVAL; | ||
94 | |||
95 | return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); | ||
96 | } | 309 | } |
97 | 310 | ||
98 | void | 311 | void |