]>
Commit | Line | Data |
---|---|---|
065c029a | 1 | /* |
2 | * Operating classes | |
3 | * Copyright(c) 2015 Intel Deutschland GmbH | |
4 | * Contact Information: | |
5 | * Intel Linux Wireless <ilw@linux.intel.com> | |
6 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
7 | * | |
8 | * This software may be distributed under the terms of the BSD license. | |
9 | * See README for more details. | |
10 | */ | |
11 | ||
12 | #include "utils/includes.h" | |
13 | ||
14 | #include "utils/common.h" | |
15 | #include "common/ieee802_11_common.h" | |
16 | #include "wpa_supplicant_i.h" | |
17 | ||
18 | ||
065c029a | 19 | static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan, |
20 | unsigned int *flags) | |
21 | { | |
22 | int i; | |
23 | ||
24 | for (i = 0; i < mode->num_channels; i++) { | |
25 | if (mode->channels[i].chan == chan) | |
26 | break; | |
27 | } | |
28 | ||
29 | if (i == mode->num_channels || | |
30 | (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) | |
31 | return NOT_ALLOWED; | |
32 | ||
33 | if (flags) | |
34 | *flags = mode->channels[i].flag; | |
35 | ||
1ac4dba3 AS |
36 | if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) |
37 | return NO_IR; | |
38 | ||
065c029a | 39 | return ALLOWED; |
40 | } | |
41 | ||
42 | ||
43 | static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel) | |
44 | { | |
45 | u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; | |
46 | size_t i; | |
47 | ||
48 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) | |
49 | return 0; | |
50 | ||
51 | for (i = 0; i < ARRAY_SIZE(center_channels); i++) { | |
52 | /* | |
53 | * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), | |
54 | * so the center channel is 6 channels away from the start/end. | |
55 | */ | |
56 | if (channel >= center_channels[i] - 6 && | |
57 | channel <= center_channels[i] + 6) | |
58 | return center_channels[i]; | |
59 | } | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | ||
65 | static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel) | |
66 | { | |
67 | u8 center_chan; | |
68 | unsigned int i; | |
1ac4dba3 | 69 | unsigned int no_ir = 0; |
065c029a | 70 | |
71 | center_chan = get_center_80mhz(mode, channel); | |
72 | if (!center_chan) | |
73 | return NOT_ALLOWED; | |
74 | ||
75 | /* check all the channels are available */ | |
76 | for (i = 0; i < 4; i++) { | |
77 | unsigned int flags; | |
78 | u8 adj_chan = center_chan - 6 + i * 4; | |
79 | ||
80 | if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) | |
81 | return NOT_ALLOWED; | |
82 | ||
83 | if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) || | |
84 | (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) || | |
85 | (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) || | |
86 | (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))) | |
87 | return NOT_ALLOWED; | |
1ac4dba3 AS |
88 | |
89 | if (flags & HOSTAPD_CHAN_NO_IR) | |
90 | no_ir = 1; | |
065c029a | 91 | } |
92 | ||
1ac4dba3 AS |
93 | if (no_ir) |
94 | return NO_IR; | |
95 | ||
065c029a | 96 | return ALLOWED; |
97 | } | |
98 | ||
99 | ||
100 | static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel) | |
101 | { | |
102 | u8 center_channels[] = { 50, 114 }; | |
103 | unsigned int i; | |
104 | ||
105 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) | |
106 | return 0; | |
107 | ||
108 | for (i = 0; i < ARRAY_SIZE(center_channels); i++) { | |
109 | /* | |
110 | * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), | |
111 | * so the center channel is 14 channels away from the start/end. | |
112 | */ | |
113 | if (channel >= center_channels[i] - 14 && | |
114 | channel <= center_channels[i] + 14) | |
115 | return center_channels[i]; | |
116 | } | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | ||
122 | static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, | |
123 | u8 channel) | |
124 | { | |
125 | u8 center_chan; | |
126 | unsigned int i; | |
1ac4dba3 | 127 | unsigned int no_ir = 0; |
065c029a | 128 | |
129 | center_chan = get_center_160mhz(mode, channel); | |
130 | if (!center_chan) | |
131 | return NOT_ALLOWED; | |
132 | ||
133 | /* Check all the channels are available */ | |
134 | for (i = 0; i < 8; i++) { | |
135 | unsigned int flags; | |
136 | u8 adj_chan = center_chan - 14 + i * 4; | |
137 | ||
138 | if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) | |
139 | return NOT_ALLOWED; | |
140 | ||
141 | if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) || | |
142 | (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) || | |
143 | (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) || | |
144 | (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) || | |
145 | (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) || | |
146 | (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) || | |
147 | (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) || | |
148 | (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))) | |
149 | return NOT_ALLOWED; | |
1ac4dba3 AS |
150 | |
151 | if (flags & HOSTAPD_CHAN_NO_IR) | |
152 | no_ir = 1; | |
065c029a | 153 | } |
154 | ||
1ac4dba3 AS |
155 | if (no_ir) |
156 | return NO_IR; | |
157 | ||
065c029a | 158 | return ALLOWED; |
159 | } | |
160 | ||
161 | ||
1ac4dba3 AS |
162 | enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel, |
163 | u8 bw) | |
065c029a | 164 | { |
165 | unsigned int flag = 0; | |
166 | enum chan_allowed res, res2; | |
167 | ||
168 | res2 = res = allow_channel(mode, channel, &flag); | |
169 | if (bw == BW40MINUS) { | |
170 | if (!(flag & HOSTAPD_CHAN_HT40MINUS)) | |
171 | return NOT_ALLOWED; | |
172 | res2 = allow_channel(mode, channel - 4, NULL); | |
173 | } else if (bw == BW40PLUS) { | |
174 | if (!(flag & HOSTAPD_CHAN_HT40PLUS)) | |
175 | return NOT_ALLOWED; | |
176 | res2 = allow_channel(mode, channel + 4, NULL); | |
177 | } else if (bw == BW80) { | |
178 | /* | |
179 | * channel is a center channel and as such, not necessarily a | |
180 | * valid 20 MHz channels. Override earlier allow_channel() | |
181 | * result and use only the 80 MHz specific version. | |
182 | */ | |
183 | res2 = res = verify_80mhz(mode, channel); | |
184 | } else if (bw == BW160) { | |
185 | /* | |
186 | * channel is a center channel and as such, not necessarily a | |
187 | * valid 20 MHz channels. Override earlier allow_channel() | |
188 | * result and use only the 160 MHz specific version. | |
189 | */ | |
190 | res2 = res = verify_160mhz(mode, channel); | |
191 | } else if (bw == BW80P80) { | |
192 | /* | |
193 | * channel is a center channel and as such, not necessarily a | |
194 | * valid 20 MHz channels. Override earlier allow_channel() | |
195 | * result and use only the 80 MHz specific version. | |
196 | */ | |
197 | res2 = res = verify_80mhz(mode, channel); | |
198 | } | |
199 | ||
200 | if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) | |
201 | return NOT_ALLOWED; | |
202 | ||
1ac4dba3 AS |
203 | if (res == NO_IR || res2 == NO_IR) |
204 | return NO_IR; | |
205 | ||
065c029a | 206 | return ALLOWED; |
207 | } | |
208 | ||
209 | ||
210 | static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, | |
cb828507 | 211 | struct wpa_ssid *ssid, |
065c029a | 212 | const struct oper_class_map *op_class) |
213 | { | |
214 | int chan; | |
215 | size_t i; | |
216 | struct hostapd_hw_modes *mode; | |
217 | int found; | |
b06d60a9 BG |
218 | int z; |
219 | int freq2 = 0; | |
220 | int freq5 = 0; | |
065c029a | 221 | |
222 | mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); | |
223 | if (!mode) | |
224 | return 0; | |
225 | ||
b06d60a9 BG |
226 | /* If we are configured to disable certain things, take that into |
227 | * account here. */ | |
228 | if (ssid->freq_list && ssid->freq_list[0]) { | |
229 | for (z = 0; ; z++) { | |
230 | int f = ssid->freq_list[z]; | |
231 | ||
232 | if (f == 0) | |
233 | break; /* end of list */ | |
234 | if (f > 4000 && f < 6000) | |
235 | freq5 = 1; | |
236 | else if (f > 2400 && f < 2500) | |
237 | freq2 = 1; | |
238 | } | |
239 | } else { | |
240 | /* No frequencies specified, can use anything hardware supports. | |
241 | */ | |
242 | freq2 = freq5 = 1; | |
243 | } | |
244 | ||
245 | if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5) | |
246 | return 0; | |
247 | if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2) | |
248 | return 0; | |
249 | ||
cb828507 BG |
250 | #ifdef CONFIG_HT_OVERRIDES |
251 | if (ssid->disable_ht) { | |
252 | switch (op_class->op_class) { | |
253 | case 83: | |
254 | case 84: | |
255 | case 104: | |
256 | case 105: | |
257 | case 116: | |
258 | case 117: | |
259 | case 119: | |
260 | case 120: | |
261 | case 122: | |
262 | case 123: | |
263 | case 126: | |
264 | case 127: | |
265 | case 128: | |
266 | case 129: | |
267 | case 130: | |
268 | /* Disable >= 40 MHz channels if HT is disabled */ | |
269 | return 0; | |
270 | } | |
271 | } | |
272 | #endif /* CONFIG_HT_OVERRIDES */ | |
273 | ||
274 | #ifdef CONFIG_VHT_OVERRIDES | |
275 | if (ssid->disable_vht) { | |
276 | if (op_class->op_class >= 128 && op_class->op_class <= 130) { | |
277 | /* Disable >= 80 MHz channels if VHT is disabled */ | |
278 | return 0; | |
279 | } | |
280 | } | |
281 | #endif /* CONFIG_VHT_OVERRIDES */ | |
282 | ||
065c029a | 283 | if (op_class->op_class == 128) { |
284 | u8 channels[] = { 42, 58, 106, 122, 138, 155 }; | |
285 | ||
286 | for (i = 0; i < ARRAY_SIZE(channels); i++) { | |
1ac4dba3 AS |
287 | if (verify_channel(mode, channels[i], op_class->bw) != |
288 | NOT_ALLOWED) | |
065c029a | 289 | return 1; |
290 | } | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | if (op_class->op_class == 129) { | |
296 | /* Check if either 160 MHz channels is allowed */ | |
1ac4dba3 AS |
297 | return verify_channel(mode, 50, op_class->bw) != NOT_ALLOWED || |
298 | verify_channel(mode, 114, op_class->bw) != NOT_ALLOWED; | |
065c029a | 299 | } |
300 | ||
301 | if (op_class->op_class == 130) { | |
302 | /* Need at least two non-contiguous 80 MHz segments */ | |
303 | found = 0; | |
304 | ||
1ac4dba3 AS |
305 | if (verify_channel(mode, 42, op_class->bw) != NOT_ALLOWED || |
306 | verify_channel(mode, 58, op_class->bw) != NOT_ALLOWED) | |
065c029a | 307 | found++; |
1ac4dba3 AS |
308 | if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED || |
309 | verify_channel(mode, 122, op_class->bw) != NOT_ALLOWED || | |
310 | verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED) | |
065c029a | 311 | found++; |
1ac4dba3 AS |
312 | if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED && |
313 | verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED) | |
065c029a | 314 | found++; |
1ac4dba3 | 315 | if (verify_channel(mode, 155, op_class->bw) != NOT_ALLOWED) |
065c029a | 316 | found++; |
317 | ||
318 | if (found >= 2) | |
319 | return 1; | |
320 | ||
321 | return 0; | |
322 | } | |
323 | ||
324 | found = 0; | |
325 | for (chan = op_class->min_chan; chan <= op_class->max_chan; | |
326 | chan += op_class->inc) { | |
1ac4dba3 | 327 | if (verify_channel(mode, chan, op_class->bw) != NOT_ALLOWED) { |
065c029a | 328 | found = 1; |
329 | break; | |
330 | } | |
331 | } | |
332 | ||
333 | return found; | |
334 | } | |
335 | ||
336 | ||
cb828507 BG |
337 | size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, |
338 | struct wpa_ssid *ssid, | |
339 | int freq, u8 *pos, size_t len) | |
065c029a | 340 | { |
341 | struct wpabuf *buf; | |
342 | u8 op, current, chan; | |
343 | u8 *ie_len; | |
344 | size_t res; | |
345 | ||
346 | /* | |
347 | * Assume 20 MHz channel for now. | |
348 | * TODO: Use the secondary channel and VHT channel width that will be | |
349 | * used after association. | |
350 | */ | |
464dcfd0 | 351 | if (ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT, |
065c029a | 352 | ¤t, &chan) == NUM_HOSTAPD_MODES) |
353 | return 0; | |
354 | ||
355 | /* | |
356 | * Need 3 bytes for EID, length, and current operating class, plus | |
357 | * 1 byte for every other supported operating class. | |
358 | */ | |
359 | buf = wpabuf_alloc(global_op_class_size + 3); | |
360 | if (!buf) | |
361 | return 0; | |
362 | ||
363 | wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); | |
364 | /* Will set the length later, putting a placeholder */ | |
365 | ie_len = wpabuf_put(buf, 1); | |
366 | wpabuf_put_u8(buf, current); | |
367 | ||
368 | for (op = 0; global_op_class[op].op_class; op++) { | |
cb828507 | 369 | if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op])) |
065c029a | 370 | wpabuf_put_u8(buf, global_op_class[op].op_class); |
371 | } | |
372 | ||
373 | *ie_len = wpabuf_len(buf) - 2; | |
374 | if (*ie_len < 2 || wpabuf_len(buf) > len) { | |
375 | wpa_printf(MSG_ERROR, | |
376 | "Failed to add supported operating classes IE"); | |
377 | res = 0; | |
378 | } else { | |
379 | os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); | |
380 | res = wpabuf_len(buf); | |
381 | wpa_hexdump_buf(MSG_DEBUG, | |
382 | "Added supported operating classes IE", buf); | |
383 | } | |
384 | ||
385 | wpabuf_free(buf); | |
386 | return res; | |
387 | } |