]>
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 | ||
19 | enum chan_allowed { | |
20 | NOT_ALLOWED, ALLOWED | |
21 | }; | |
22 | ||
23 | static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan, | |
24 | unsigned int *flags) | |
25 | { | |
26 | int i; | |
27 | ||
28 | for (i = 0; i < mode->num_channels; i++) { | |
29 | if (mode->channels[i].chan == chan) | |
30 | break; | |
31 | } | |
32 | ||
33 | if (i == mode->num_channels || | |
34 | (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) | |
35 | return NOT_ALLOWED; | |
36 | ||
37 | if (flags) | |
38 | *flags = mode->channels[i].flag; | |
39 | ||
40 | return ALLOWED; | |
41 | } | |
42 | ||
43 | ||
44 | static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel) | |
45 | { | |
46 | u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; | |
47 | size_t i; | |
48 | ||
49 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) | |
50 | return 0; | |
51 | ||
52 | for (i = 0; i < ARRAY_SIZE(center_channels); i++) { | |
53 | /* | |
54 | * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), | |
55 | * so the center channel is 6 channels away from the start/end. | |
56 | */ | |
57 | if (channel >= center_channels[i] - 6 && | |
58 | channel <= center_channels[i] + 6) | |
59 | return center_channels[i]; | |
60 | } | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | ||
66 | static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel) | |
67 | { | |
68 | u8 center_chan; | |
69 | unsigned int i; | |
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; | |
88 | } | |
89 | ||
90 | return ALLOWED; | |
91 | } | |
92 | ||
93 | ||
94 | static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel) | |
95 | { | |
96 | u8 center_channels[] = { 50, 114 }; | |
97 | unsigned int i; | |
98 | ||
99 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) | |
100 | return 0; | |
101 | ||
102 | for (i = 0; i < ARRAY_SIZE(center_channels); i++) { | |
103 | /* | |
104 | * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), | |
105 | * so the center channel is 14 channels away from the start/end. | |
106 | */ | |
107 | if (channel >= center_channels[i] - 14 && | |
108 | channel <= center_channels[i] + 14) | |
109 | return center_channels[i]; | |
110 | } | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | ||
116 | static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, | |
117 | u8 channel) | |
118 | { | |
119 | u8 center_chan; | |
120 | unsigned int i; | |
121 | ||
122 | center_chan = get_center_160mhz(mode, channel); | |
123 | if (!center_chan) | |
124 | return NOT_ALLOWED; | |
125 | ||
126 | /* Check all the channels are available */ | |
127 | for (i = 0; i < 8; i++) { | |
128 | unsigned int flags; | |
129 | u8 adj_chan = center_chan - 14 + i * 4; | |
130 | ||
131 | if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) | |
132 | return NOT_ALLOWED; | |
133 | ||
134 | if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) || | |
135 | (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) || | |
136 | (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) || | |
137 | (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) || | |
138 | (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) || | |
139 | (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) || | |
140 | (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) || | |
141 | (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))) | |
142 | return NOT_ALLOWED; | |
143 | } | |
144 | ||
145 | return ALLOWED; | |
146 | } | |
147 | ||
148 | ||
149 | static enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, | |
150 | u8 channel, u8 bw) | |
151 | { | |
152 | unsigned int flag = 0; | |
153 | enum chan_allowed res, res2; | |
154 | ||
155 | res2 = res = allow_channel(mode, channel, &flag); | |
156 | if (bw == BW40MINUS) { | |
157 | if (!(flag & HOSTAPD_CHAN_HT40MINUS)) | |
158 | return NOT_ALLOWED; | |
159 | res2 = allow_channel(mode, channel - 4, NULL); | |
160 | } else if (bw == BW40PLUS) { | |
161 | if (!(flag & HOSTAPD_CHAN_HT40PLUS)) | |
162 | return NOT_ALLOWED; | |
163 | res2 = allow_channel(mode, channel + 4, NULL); | |
164 | } else if (bw == BW80) { | |
165 | /* | |
166 | * channel is a center channel and as such, not necessarily a | |
167 | * valid 20 MHz channels. Override earlier allow_channel() | |
168 | * result and use only the 80 MHz specific version. | |
169 | */ | |
170 | res2 = res = verify_80mhz(mode, channel); | |
171 | } else if (bw == BW160) { | |
172 | /* | |
173 | * channel is a center channel and as such, not necessarily a | |
174 | * valid 20 MHz channels. Override earlier allow_channel() | |
175 | * result and use only the 160 MHz specific version. | |
176 | */ | |
177 | res2 = res = verify_160mhz(mode, channel); | |
178 | } else if (bw == BW80P80) { | |
179 | /* | |
180 | * channel is a center channel and as such, not necessarily a | |
181 | * valid 20 MHz channels. Override earlier allow_channel() | |
182 | * result and use only the 80 MHz specific version. | |
183 | */ | |
184 | res2 = res = verify_80mhz(mode, channel); | |
185 | } | |
186 | ||
187 | if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) | |
188 | return NOT_ALLOWED; | |
189 | ||
190 | return ALLOWED; | |
191 | } | |
192 | ||
193 | ||
194 | static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, | |
195 | const struct oper_class_map *op_class) | |
196 | { | |
197 | int chan; | |
198 | size_t i; | |
199 | struct hostapd_hw_modes *mode; | |
200 | int found; | |
201 | ||
202 | mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); | |
203 | if (!mode) | |
204 | return 0; | |
205 | ||
206 | if (op_class->op_class == 128) { | |
207 | u8 channels[] = { 42, 58, 106, 122, 138, 155 }; | |
208 | ||
209 | for (i = 0; i < ARRAY_SIZE(channels); i++) { | |
210 | if (verify_channel(mode, channels[i], op_class->bw) == | |
211 | ALLOWED) | |
212 | return 1; | |
213 | } | |
214 | ||
215 | return 0; | |
216 | } | |
217 | ||
218 | if (op_class->op_class == 129) { | |
219 | /* Check if either 160 MHz channels is allowed */ | |
220 | return verify_channel(mode, 50, op_class->bw) == ALLOWED || | |
221 | verify_channel(mode, 114, op_class->bw) == ALLOWED; | |
222 | } | |
223 | ||
224 | if (op_class->op_class == 130) { | |
225 | /* Need at least two non-contiguous 80 MHz segments */ | |
226 | found = 0; | |
227 | ||
228 | if (verify_channel(mode, 42, op_class->bw) == ALLOWED || | |
229 | verify_channel(mode, 58, op_class->bw) == ALLOWED) | |
230 | found++; | |
231 | if (verify_channel(mode, 106, op_class->bw) == ALLOWED || | |
232 | verify_channel(mode, 122, op_class->bw) == ALLOWED || | |
233 | verify_channel(mode, 138, op_class->bw) == ALLOWED) | |
234 | found++; | |
235 | if (verify_channel(mode, 106, op_class->bw) == ALLOWED && | |
236 | verify_channel(mode, 138, op_class->bw) == ALLOWED) | |
237 | found++; | |
238 | if (verify_channel(mode, 155, op_class->bw) == ALLOWED) | |
239 | found++; | |
240 | ||
241 | if (found >= 2) | |
242 | return 1; | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
247 | found = 0; | |
248 | for (chan = op_class->min_chan; chan <= op_class->max_chan; | |
249 | chan += op_class->inc) { | |
250 | if (verify_channel(mode, chan, op_class->bw) == ALLOWED) { | |
251 | found = 1; | |
252 | break; | |
253 | } | |
254 | } | |
255 | ||
256 | return found; | |
257 | } | |
258 | ||
259 | ||
260 | size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, | |
261 | size_t len) | |
262 | { | |
263 | struct wpabuf *buf; | |
264 | u8 op, current, chan; | |
265 | u8 *ie_len; | |
266 | size_t res; | |
267 | ||
268 | /* | |
269 | * Assume 20 MHz channel for now. | |
270 | * TODO: Use the secondary channel and VHT channel width that will be | |
271 | * used after association. | |
272 | */ | |
273 | if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, | |
274 | ¤t, &chan) == NUM_HOSTAPD_MODES) | |
275 | return 0; | |
276 | ||
277 | /* | |
278 | * Need 3 bytes for EID, length, and current operating class, plus | |
279 | * 1 byte for every other supported operating class. | |
280 | */ | |
281 | buf = wpabuf_alloc(global_op_class_size + 3); | |
282 | if (!buf) | |
283 | return 0; | |
284 | ||
285 | wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); | |
286 | /* Will set the length later, putting a placeholder */ | |
287 | ie_len = wpabuf_put(buf, 1); | |
288 | wpabuf_put_u8(buf, current); | |
289 | ||
290 | for (op = 0; global_op_class[op].op_class; op++) { | |
291 | if (wpas_op_class_supported(wpa_s, &global_op_class[op])) | |
292 | wpabuf_put_u8(buf, global_op_class[op].op_class); | |
293 | } | |
294 | ||
295 | *ie_len = wpabuf_len(buf) - 2; | |
296 | if (*ie_len < 2 || wpabuf_len(buf) > len) { | |
297 | wpa_printf(MSG_ERROR, | |
298 | "Failed to add supported operating classes IE"); | |
299 | res = 0; | |
300 | } else { | |
301 | os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); | |
302 | res = wpabuf_len(buf); | |
303 | wpa_hexdump_buf(MSG_DEBUG, | |
304 | "Added supported operating classes IE", buf); | |
305 | } | |
306 | ||
307 | wpabuf_free(buf); | |
308 | return res; | |
309 | } |