]>
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, | |
211 | const struct oper_class_map *op_class) | |
212 | { | |
213 | int chan; | |
214 | size_t i; | |
215 | struct hostapd_hw_modes *mode; | |
216 | int found; | |
217 | ||
218 | mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); | |
219 | if (!mode) | |
220 | return 0; | |
221 | ||
222 | if (op_class->op_class == 128) { | |
223 | u8 channels[] = { 42, 58, 106, 122, 138, 155 }; | |
224 | ||
225 | for (i = 0; i < ARRAY_SIZE(channels); i++) { | |
1ac4dba3 AS |
226 | if (verify_channel(mode, channels[i], op_class->bw) != |
227 | NOT_ALLOWED) | |
065c029a | 228 | return 1; |
229 | } | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | if (op_class->op_class == 129) { | |
235 | /* Check if either 160 MHz channels is allowed */ | |
1ac4dba3 AS |
236 | return verify_channel(mode, 50, op_class->bw) != NOT_ALLOWED || |
237 | verify_channel(mode, 114, op_class->bw) != NOT_ALLOWED; | |
065c029a | 238 | } |
239 | ||
240 | if (op_class->op_class == 130) { | |
241 | /* Need at least two non-contiguous 80 MHz segments */ | |
242 | found = 0; | |
243 | ||
1ac4dba3 AS |
244 | if (verify_channel(mode, 42, op_class->bw) != NOT_ALLOWED || |
245 | verify_channel(mode, 58, op_class->bw) != NOT_ALLOWED) | |
065c029a | 246 | found++; |
1ac4dba3 AS |
247 | if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED || |
248 | verify_channel(mode, 122, op_class->bw) != NOT_ALLOWED || | |
249 | verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED) | |
065c029a | 250 | found++; |
1ac4dba3 AS |
251 | if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED && |
252 | verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED) | |
065c029a | 253 | found++; |
1ac4dba3 | 254 | if (verify_channel(mode, 155, op_class->bw) != NOT_ALLOWED) |
065c029a | 255 | found++; |
256 | ||
257 | if (found >= 2) | |
258 | return 1; | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
263 | found = 0; | |
264 | for (chan = op_class->min_chan; chan <= op_class->max_chan; | |
265 | chan += op_class->inc) { | |
1ac4dba3 | 266 | if (verify_channel(mode, chan, op_class->bw) != NOT_ALLOWED) { |
065c029a | 267 | found = 1; |
268 | break; | |
269 | } | |
270 | } | |
271 | ||
272 | return found; | |
273 | } | |
274 | ||
275 | ||
276 | size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, | |
277 | size_t len) | |
278 | { | |
279 | struct wpabuf *buf; | |
280 | u8 op, current, chan; | |
281 | u8 *ie_len; | |
282 | size_t res; | |
283 | ||
284 | /* | |
285 | * Assume 20 MHz channel for now. | |
286 | * TODO: Use the secondary channel and VHT channel width that will be | |
287 | * used after association. | |
288 | */ | |
289 | if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, | |
290 | ¤t, &chan) == NUM_HOSTAPD_MODES) | |
291 | return 0; | |
292 | ||
293 | /* | |
294 | * Need 3 bytes for EID, length, and current operating class, plus | |
295 | * 1 byte for every other supported operating class. | |
296 | */ | |
297 | buf = wpabuf_alloc(global_op_class_size + 3); | |
298 | if (!buf) | |
299 | return 0; | |
300 | ||
301 | wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); | |
302 | /* Will set the length later, putting a placeholder */ | |
303 | ie_len = wpabuf_put(buf, 1); | |
304 | wpabuf_put_u8(buf, current); | |
305 | ||
306 | for (op = 0; global_op_class[op].op_class; op++) { | |
307 | if (wpas_op_class_supported(wpa_s, &global_op_class[op])) | |
308 | wpabuf_put_u8(buf, global_op_class[op].op_class); | |
309 | } | |
310 | ||
311 | *ie_len = wpabuf_len(buf) - 2; | |
312 | if (*ie_len < 2 || wpabuf_len(buf) > len) { | |
313 | wpa_printf(MSG_ERROR, | |
314 | "Failed to add supported operating classes IE"); | |
315 | res = 0; | |
316 | } else { | |
317 | os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); | |
318 | res = wpabuf_len(buf); | |
319 | wpa_hexdump_buf(MSG_DEBUG, | |
320 | "Added supported operating classes IE", buf); | |
321 | } | |
322 | ||
323 | wpabuf_free(buf); | |
324 | return res; | |
325 | } |