nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Misc utility routines used by kernel or app-level. |
||
3 | * Contents are wifi-specific, used by any kernel or app-level |
||
4 | * software that might want wifi things as it grows. |
||
5 | * |
||
6 | * Copyright (C) 1999-2013, Broadcom Corporation |
||
7 | * |
||
8 | * Unless you and Broadcom execute a separate written software license |
||
9 | * agreement governing use of this software, this software is licensed to you |
||
10 | * under the terms of the GNU General Public License version 2 (the "GPL"), |
||
11 | * available at http://www.broadcom.com/licenses/GPLv2.php, with the |
||
12 | * following added to such license: |
||
13 | * |
||
14 | * As a special exception, the copyright holders of this software give you |
||
15 | * permission to link this software with independent modules, and to copy and |
||
16 | * distribute the resulting executable under terms of your choice, provided that |
||
17 | * you also meet, for each linked independent module, the terms and conditions of |
||
18 | * the license of that module. An independent module is a module which is not |
||
19 | * derived from this software. The special exception does not apply to any |
||
20 | * modifications of the software. |
||
21 | * |
||
22 | * Notwithstanding the above, under no circumstances may you combine this |
||
23 | * software in any way with any other Broadcom software provided under a license |
||
24 | * other than the GPL, without Broadcom's express prior written consent. |
||
25 | * $Id: bcmwifi_channels.c 309193 2012-01-19 00:03:57Z $ |
||
26 | */ |
||
27 | |||
28 | #include <bcm_cfg.h> |
||
29 | #include <typedefs.h> |
||
30 | #include <bcmutils.h> |
||
31 | |||
32 | #ifdef BCMDRIVER |
||
33 | #include <osl.h> |
||
34 | #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) |
||
35 | #define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) |
||
36 | #else |
||
37 | #include <stdio.h> |
||
38 | #include <stdlib.h> |
||
39 | #include <ctype.h> |
||
40 | #ifndef ASSERT |
||
41 | #define ASSERT(exp) |
||
42 | #endif |
||
43 | #endif /* BCMDRIVER */ |
||
44 | |||
45 | #ifdef _bcmwifi_c_ |
||
46 | /* temporary for transitional compatibility */ |
||
47 | #include <bcmwifi.h> |
||
48 | #else |
||
49 | #include <bcmwifi_channels.h> |
||
50 | #endif |
||
51 | |||
52 | #if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL)) |
||
53 | #include <bcmstdlib.h> /* For wl/exe/GNUmakefile.brcm_wlu and GNUmakefile.wlm_dll */ |
||
54 | #endif |
||
55 | |||
56 | #ifndef D11AC_IOTYPES |
||
57 | |||
58 | /* Definitions for legacy Chanspec type */ |
||
59 | |||
60 | /* Chanspec ASCII representation: |
||
61 | * <channel><band><bandwidth><ctl-sideband> |
||
62 | * digit [AB] [N] [UL] |
||
63 | * |
||
64 | * <channel>: channel number of the 10MHz or 20MHz channel, |
||
65 | * or control sideband channel of 40MHz channel. |
||
66 | * <band>: A for 5GHz, B for 2.4GHz |
||
67 | * <bandwidth>: N for 10MHz, nothing for 20MHz or 40MHz |
||
68 | * (ctl-sideband spec implies 40MHz) |
||
69 | * <ctl-sideband>: U for upper, L for lower |
||
70 | * |
||
71 | * <band> may be omitted on input, and will be assumed to be |
||
72 | * 2.4GHz if channel number <= 14. |
||
73 | * |
||
74 | * Examples: |
||
75 | * 8 -> 2.4GHz channel 8, 20MHz |
||
76 | * 8b -> 2.4GHz channel 8, 20MHz |
||
77 | * 8l -> 2.4GHz channel 8, 40MHz, lower ctl sideband |
||
78 | * 8a -> 5GHz channel 8 (low 5 GHz band), 20MHz |
||
79 | * 36 -> 5GHz channel 36, 20MHz |
||
80 | * 36l -> 5GHz channel 36, 40MHz, lower ctl sideband |
||
81 | * 40u -> 5GHz channel 40, 40MHz, upper ctl sideband |
||
82 | * 180n -> channel 180, 10MHz |
||
83 | */ |
||
84 | |||
85 | |||
86 | /* given a chanspec and a string buffer, format the chanspec as a |
||
87 | * string, and return the original pointer a. |
||
88 | * Min buffer length must be CHANSPEC_STR_LEN. |
||
89 | * On error return NULL |
||
90 | */ |
||
91 | char * |
||
92 | wf_chspec_ntoa(chanspec_t chspec, char *buf) |
||
93 | { |
||
94 | const char *band, *bw, *sb; |
||
95 | uint channel; |
||
96 | |||
97 | band = ""; |
||
98 | bw = ""; |
||
99 | sb = ""; |
||
100 | channel = CHSPEC_CHANNEL(chspec); |
||
101 | /* check for non-default band spec */ |
||
102 | if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) || |
||
103 | (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL)) |
||
104 | band = (CHSPEC_IS2G(chspec)) ? "b" : "a"; |
||
105 | if (CHSPEC_IS40(chspec)) { |
||
106 | if (CHSPEC_SB_UPPER(chspec)) { |
||
107 | sb = "u"; |
||
108 | channel += CH_10MHZ_APART; |
||
109 | } else { |
||
110 | sb = "l"; |
||
111 | channel -= CH_10MHZ_APART; |
||
112 | } |
||
113 | } else if (CHSPEC_IS10(chspec)) { |
||
114 | bw = "n"; |
||
115 | } |
||
116 | |||
117 | /* Outputs a max of 6 chars including '\0' */ |
||
118 | snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb); |
||
119 | return (buf); |
||
120 | } |
||
121 | |||
122 | /* given a chanspec string, convert to a chanspec. |
||
123 | * On error return 0 |
||
124 | */ |
||
125 | chanspec_t |
||
126 | wf_chspec_aton(const char *a) |
||
127 | { |
||
128 | char *endp = NULL; |
||
129 | uint channel, band, bw, ctl_sb; |
||
130 | char c; |
||
131 | |||
132 | channel = strtoul(a, &endp, 10); |
||
133 | |||
134 | /* check for no digits parsed */ |
||
135 | if (endp == a) |
||
136 | return 0; |
||
137 | |||
138 | if (channel > MAXCHANNEL) |
||
139 | return 0; |
||
140 | |||
141 | band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); |
||
142 | bw = WL_CHANSPEC_BW_20; |
||
143 | ctl_sb = WL_CHANSPEC_CTL_SB_NONE; |
||
144 | |||
145 | a = endp; |
||
146 | |||
147 | c = tolower(a[0]); |
||
148 | if (c == '\0') |
||
149 | goto done; |
||
150 | |||
151 | /* parse the optional ['A' | 'B'] band spec */ |
||
152 | if (c == 'a' || c == 'b') { |
||
153 | band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G; |
||
154 | a++; |
||
155 | c = tolower(a[0]); |
||
156 | if (c == '\0') |
||
157 | goto done; |
||
158 | } |
||
159 | |||
160 | /* parse bandwidth 'N' (10MHz) or 40MHz ctl sideband ['L' | 'U'] */ |
||
161 | if (c == 'n') { |
||
162 | bw = WL_CHANSPEC_BW_10; |
||
163 | } else if (c == 'l') { |
||
164 | bw = WL_CHANSPEC_BW_40; |
||
165 | ctl_sb = WL_CHANSPEC_CTL_SB_LOWER; |
||
166 | /* adjust channel to center of 40MHz band */ |
||
167 | if (channel <= (MAXCHANNEL - CH_20MHZ_APART)) |
||
168 | channel += CH_10MHZ_APART; |
||
169 | else |
||
170 | return 0; |
||
171 | } else if (c == 'u') { |
||
172 | bw = WL_CHANSPEC_BW_40; |
||
173 | ctl_sb = WL_CHANSPEC_CTL_SB_UPPER; |
||
174 | /* adjust channel to center of 40MHz band */ |
||
175 | if (channel > CH_20MHZ_APART) |
||
176 | channel -= CH_10MHZ_APART; |
||
177 | else |
||
178 | return 0; |
||
179 | } else { |
||
180 | return 0; |
||
181 | } |
||
182 | |||
183 | done: |
||
184 | return (channel | band | bw | ctl_sb); |
||
185 | } |
||
186 | |||
187 | /* |
||
188 | * Verify the chanspec is using a legal set of parameters, i.e. that the |
||
189 | * chanspec specified a band, bw, ctl_sb and channel and that the |
||
190 | * combination could be legal given any set of circumstances. |
||
191 | * RETURNS: TRUE is the chanspec is malformed, false if it looks good. |
||
192 | */ |
||
193 | bool |
||
194 | wf_chspec_malformed(chanspec_t chanspec) |
||
195 | { |
||
196 | /* must be 2G or 5G band */ |
||
197 | if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) |
||
198 | return TRUE; |
||
199 | /* must be 20 or 40 bandwidth */ |
||
200 | if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) |
||
201 | return TRUE; |
||
202 | |||
203 | /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ |
||
204 | if (CHSPEC_IS20(chanspec)) { |
||
205 | if (!CHSPEC_SB_NONE(chanspec)) |
||
206 | return TRUE; |
||
207 | } else { |
||
208 | if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) |
||
209 | return TRUE; |
||
210 | } |
||
211 | |||
212 | return FALSE; |
||
213 | } |
||
214 | |||
215 | /* |
||
216 | * This function returns the channel number that control traffic is being sent on, for legacy |
||
217 | * channels this is just the channel number, for 40MHZ channels it is the upper or lower 20MHZ |
||
218 | * sideband depending on the chanspec selected |
||
219 | */ |
||
220 | uint8 |
||
221 | wf_chspec_ctlchan(chanspec_t chspec) |
||
222 | { |
||
223 | uint8 ctl_chan; |
||
224 | |||
225 | /* Is there a sideband ? */ |
||
226 | if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { |
||
227 | return CHSPEC_CHANNEL(chspec); |
||
228 | } else { |
||
229 | /* we only support 40MHZ with sidebands */ |
||
230 | ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40); |
||
231 | /* chanspec channel holds the centre frequency, use that and the |
||
232 | * side band information to reconstruct the control channel number |
||
233 | */ |
||
234 | if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { |
||
235 | /* control chan is the upper 20 MHZ SB of the 40MHZ channel */ |
||
236 | ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); |
||
237 | } else { |
||
238 | ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER); |
||
239 | /* control chan is the lower 20 MHZ SB of the 40MHZ channel */ |
||
240 | ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); |
||
241 | } |
||
242 | } |
||
243 | |||
244 | return ctl_chan; |
||
245 | } |
||
246 | |||
247 | chanspec_t |
||
248 | wf_chspec_ctlchspec(chanspec_t chspec) |
||
249 | { |
||
250 | chanspec_t ctl_chspec = 0; |
||
251 | uint8 channel; |
||
252 | |||
253 | ASSERT(!wf_chspec_malformed(chspec)); |
||
254 | |||
255 | /* Is there a sideband ? */ |
||
256 | if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { |
||
257 | return chspec; |
||
258 | } else { |
||
259 | if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { |
||
260 | channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); |
||
261 | } else { |
||
262 | channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); |
||
263 | } |
||
264 | ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE; |
||
265 | ctl_chspec |= CHSPEC_BAND(chspec); |
||
266 | } |
||
267 | return ctl_chspec; |
||
268 | } |
||
269 | |||
270 | #else /* D11AC_IOTYPES */ |
||
271 | |||
272 | /* Definitions for D11AC capable Chanspec type */ |
||
273 | |||
274 | /* Chanspec ASCII representation with 802.11ac capability: |
||
275 | * [<band> 'g'] <channel> ['/'<bandwidth> [<ctl-sideband>]['/'<1st80channel>'-'<2nd80channel>]] |
||
276 | * |
||
277 | * <band>: |
||
278 | * (optional) 2, 3, 4, 5 for 2.4GHz, 3GHz, 4GHz, and 5GHz respectively. |
||
279 | * Default value is 2g if channel <= 14, otherwise 5g. |
||
280 | * <channel>: |
||
281 | * channel number of the 5MHz, 10MHz, 20MHz channel, |
||
282 | * or primary channel of 40MHz, 80MHz, 160MHz, or 80+80MHz channel. |
||
283 | * <bandwidth>: |
||
284 | * (optional) 5, 10, 20, 40, 80, 160, or 80+80. Default value is 20. |
||
285 | * <primary-sideband>: |
||
286 | * (only for 2.4GHz band 40MHz) U for upper sideband primary, L for lower. |
||
287 | * |
||
288 | * For 2.4GHz band 40MHz channels, the same primary channel may be the |
||
289 | * upper sideband for one 40MHz channel, and the lower sideband for an |
||
290 | * overlapping 40MHz channel. The U/L disambiguates which 40MHz channel |
||
291 | * is being specified. |
||
292 | * |
||
293 | * For 40MHz in the 5GHz band and all channel bandwidths greater than |
||
294 | * 40MHz, the U/L specificaion is not allowed since the channels are |
||
295 | * non-overlapping and the primary sub-band is derived from its |
||
296 | * position in the wide bandwidth channel. |
||
297 | * |
||
298 | * <1st80Channel>: |
||
299 | * <2nd80Channel>: |
||
300 | * Required for 80+80, otherwise not allowed. |
||
301 | * Specifies the center channel of the first and second 80MHz band. |
||
302 | * |
||
303 | * In its simplest form, it is a 20MHz channel number, with the implied band |
||
304 | * of 2.4GHz if channel number <= 14, and 5GHz otherwise. |
||
305 | * |
||
306 | * To allow for backward compatibility with scripts, the old form for |
||
307 | * 40MHz channels is also allowed: <channel><ctl-sideband> |
||
308 | * |
||
309 | * <channel>: |
||
310 | * primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz |
||
311 | * <ctl-sideband>: |
||
312 | * "U" for upper, "L" for lower (or lower case "u" "l") |
||
313 | * |
||
314 | * 5 GHz Examples: |
||
315 | * Chanspec BW Center Ch Channel Range Primary Ch |
||
316 | * 5g8 20MHz 8 - - |
||
317 | * 52 20MHz 52 - - |
||
318 | * 52/40 40MHz 54 52-56 52 |
||
319 | * 56/40 40MHz 54 52-56 56 |
||
320 | * 52/80 80MHz 58 52-64 52 |
||
321 | * 56/80 80MHz 58 52-64 56 |
||
322 | * 60/80 80MHz 58 52-64 60 |
||
323 | * 64/80 80MHz 58 52-64 64 |
||
324 | * 52/160 160MHz 50 36-64 52 |
||
325 | * 36/160 160MGz 50 36-64 36 |
||
326 | * 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36 |
||
327 | * |
||
328 | * 2 GHz Examples: |
||
329 | * Chanspec BW Center Ch Channel Range Primary Ch |
||
330 | * 2g8 20MHz 8 - - |
||
331 | * 8 20MHz 8 - - |
||
332 | * 6 20MHz 6 - - |
||
333 | * 6/40l 40MHz 8 6-10 6 |
||
334 | * 6l 40MHz 8 6-10 6 |
||
335 | * 6/40u 40MHz 4 2-6 6 |
||
336 | * 6u 40MHz 4 2-6 6 |
||
337 | */ |
||
338 | |||
339 | /* bandwidth ASCII string */ |
||
340 | static const char *wf_chspec_bw_str[] = |
||
341 | { |
||
342 | "5", |
||
343 | "10", |
||
344 | "20", |
||
345 | "40", |
||
346 | "80", |
||
347 | "160", |
||
348 | "80+80", |
||
349 | "na" |
||
350 | }; |
||
351 | |||
352 | static const uint8 wf_chspec_bw_mhz[] = |
||
353 | {5, 10, 20, 40, 80, 160, 160}; |
||
354 | |||
355 | #define WF_NUM_BW \ |
||
356 | (sizeof(wf_chspec_bw_mhz)/sizeof(uint8)) |
||
357 | |||
358 | /* 40MHz channels in 5GHz band */ |
||
359 | static const uint8 wf_5g_40m_chans[] = |
||
360 | {38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159}; |
||
361 | #define WF_NUM_5G_40M_CHANS \ |
||
362 | (sizeof(wf_5g_40m_chans)/sizeof(uint8)) |
||
363 | |||
364 | /* 80MHz channels in 5GHz band */ |
||
365 | static const uint8 wf_5g_80m_chans[] = |
||
366 | {42, 58, 106, 122, 138, 155}; |
||
367 | #define WF_NUM_5G_80M_CHANS \ |
||
368 | (sizeof(wf_5g_80m_chans)/sizeof(uint8)) |
||
369 | |||
370 | /* 160MHz channels in 5GHz band */ |
||
371 | static const uint8 wf_5g_160m_chans[] = |
||
372 | {50, 114}; |
||
373 | #define WF_NUM_5G_160M_CHANS \ |
||
374 | (sizeof(wf_5g_160m_chans)/sizeof(uint8)) |
||
375 | |||
376 | |||
377 | /* convert bandwidth from chanspec to MHz */ |
||
378 | static uint |
||
379 | bw_chspec_to_mhz(chanspec_t chspec) |
||
380 | { |
||
381 | uint bw; |
||
382 | |||
383 | bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT; |
||
384 | return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]); |
||
385 | } |
||
386 | |||
387 | /* bw in MHz, return the channel count from the center channel to the |
||
388 | * the channel at the edge of the band |
||
389 | */ |
||
390 | static uint8 |
||
391 | center_chan_to_edge(uint bw) |
||
392 | { |
||
393 | /* edge channels separated by BW - 10MHz on each side |
||
394 | * delta from cf to edge is half of that, |
||
395 | * MHz to channel num conversion is 5MHz/channel |
||
396 | */ |
||
397 | return (uint8)(((bw - 20) / 2) / 5); |
||
398 | } |
||
399 | |||
400 | /* return channel number of the low edge of the band |
||
401 | * given the center channel and BW |
||
402 | */ |
||
403 | static uint8 |
||
404 | channel_low_edge(uint center_ch, uint bw) |
||
405 | { |
||
406 | return (uint8)(center_ch - center_chan_to_edge(bw)); |
||
407 | } |
||
408 | |||
409 | /* return side band number given center channel and control channel |
||
410 | * return -1 on error |
||
411 | */ |
||
412 | static int |
||
413 | channel_to_sb(uint center_ch, uint ctl_ch, uint bw) |
||
414 | { |
||
415 | uint lowest = channel_low_edge(center_ch, bw); |
||
416 | uint sb; |
||
417 | |||
418 | if ((ctl_ch - lowest) % 4) { |
||
419 | /* bad ctl channel, not mult 4 */ |
||
420 | return -1; |
||
421 | } |
||
422 | |||
423 | sb = ((ctl_ch - lowest) / 4); |
||
424 | |||
425 | /* sb must be a index to a 20MHz channel in range */ |
||
426 | if (sb >= (bw / 20)) { |
||
427 | /* ctl_ch must have been too high for the center_ch */ |
||
428 | return -1; |
||
429 | } |
||
430 | |||
431 | return sb; |
||
432 | } |
||
433 | |||
434 | /* return control channel given center channel and side band */ |
||
435 | static uint8 |
||
436 | channel_to_ctl_chan(uint center_ch, uint bw, uint sb) |
||
437 | { |
||
438 | return (uint8)(channel_low_edge(center_ch, bw) + sb * 4); |
||
439 | } |
||
440 | |||
441 | /* return index of 80MHz channel from channel number |
||
442 | * return -1 on error |
||
443 | */ |
||
444 | static int |
||
445 | channel_80mhz_to_id(uint ch) |
||
446 | { |
||
447 | uint i; |
||
448 | for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) { |
||
449 | if (ch == wf_5g_80m_chans[i]) |
||
450 | return i; |
||
451 | } |
||
452 | |||
453 | return -1; |
||
454 | } |
||
455 | |||
456 | /* given a chanspec and a string buffer, format the chanspec as a |
||
457 | * string, and return the original pointer a. |
||
458 | * Min buffer length must be CHANSPEC_STR_LEN. |
||
459 | * On error return NULL |
||
460 | */ |
||
461 | char * |
||
462 | wf_chspec_ntoa(chanspec_t chspec, char *buf) |
||
463 | { |
||
464 | const char *band; |
||
465 | uint ctl_chan; |
||
466 | |||
467 | //if (wf_chspec_malformed(chspec)) |
||
468 | // return NULL; |
||
469 | |||
470 | band = ""; |
||
471 | |||
472 | /* check for non-default band spec */ |
||
473 | if ((CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) || |
||
474 | (CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL)) |
||
475 | band = (CHSPEC_IS2G(chspec)) ? "2g" : "5g"; |
||
476 | |||
477 | /* ctl channel */ |
||
478 | ctl_chan = wf_chspec_ctlchan(chspec); |
||
479 | |||
480 | /* bandwidth and ctl sideband */ |
||
481 | if (CHSPEC_IS20(chspec)) { |
||
482 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, ctl_chan); |
||
483 | } else if (!CHSPEC_IS8080(chspec)) { |
||
484 | const char *bw; |
||
485 | const char *sb = ""; |
||
486 | |||
487 | bw = wf_chspec_bw_str[(chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT]; |
||
488 | |||
489 | #ifdef CHANSPEC_NEW_40MHZ_FORMAT |
||
490 | /* ctl sideband string if needed for 2g 40MHz */ |
||
491 | if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) { |
||
492 | sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; |
||
493 | } |
||
494 | |||
495 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, ctl_chan, bw, sb); |
||
496 | #else |
||
497 | /* ctl sideband string instead of BW for 40MHz */ |
||
498 | if (CHSPEC_IS40(chspec)) { |
||
499 | sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; |
||
500 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, ctl_chan, sb); |
||
501 | } else { |
||
502 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, ctl_chan, bw); |
||
503 | } |
||
504 | #endif /* CHANSPEC_NEW_40MHZ_FORMAT */ |
||
505 | |||
506 | } else { |
||
507 | /* 80+80 */ |
||
508 | uint chan1 = (chspec & WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT; |
||
509 | uint chan2 = (chspec & WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT; |
||
510 | |||
511 | /* convert to channel number */ |
||
512 | chan1 = (chan1 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan1] : 0; |
||
513 | chan2 = (chan2 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan2] : 0; |
||
514 | |||
515 | /* Outputs a max of CHANSPEC_STR_LEN chars including '\0' */ |
||
516 | snprintf(buf, CHANSPEC_STR_LEN, "%d/80+80/%d-%d", ctl_chan, chan1, chan2); |
||
517 | } |
||
518 | |||
519 | return (buf); |
||
520 | } |
||
521 | |||
522 | static int |
||
523 | read_uint(const char **p, unsigned int *num) |
||
524 | { |
||
525 | unsigned long val; |
||
526 | char *endp = NULL; |
||
527 | |||
528 | val = strtoul(*p, &endp, 10); |
||
529 | /* if endp is the initial pointer value, then a number was not read */ |
||
530 | if (endp == *p) |
||
531 | return 0; |
||
532 | |||
533 | /* advance the buffer pointer to the end of the integer string */ |
||
534 | *p = endp; |
||
535 | /* return the parsed integer */ |
||
536 | *num = (unsigned int)val; |
||
537 | |||
538 | return 1; |
||
539 | } |
||
540 | |||
541 | /* given a chanspec string, convert to a chanspec. |
||
542 | * On error return 0 |
||
543 | */ |
||
544 | chanspec_t |
||
545 | wf_chspec_aton(const char *a) |
||
546 | { |
||
547 | chanspec_t chspec; |
||
548 | uint chspec_ch, chspec_band, bw, chspec_bw, chspec_sb; |
||
549 | uint num, ctl_ch; |
||
550 | uint ch1, ch2; |
||
551 | char c, sb_ul = '\0'; |
||
552 | int i; |
||
553 | |||
554 | bw = 20; |
||
555 | chspec_sb = 0; |
||
556 | chspec_ch = ch1 = ch2 = 0; |
||
557 | |||
558 | /* parse channel num or band */ |
||
559 | if (!read_uint(&a, &num)) |
||
560 | return 0; |
||
561 | |||
562 | /* if we are looking at a 'g', then the first number was a band */ |
||
563 | c = tolower((int)a[0]); |
||
564 | if (c == 'g') { |
||
565 | a ++; /* consume the char */ |
||
566 | |||
567 | /* band must be "2" or "5" */ |
||
568 | if (num == 2) |
||
569 | chspec_band = WL_CHANSPEC_BAND_2G; |
||
570 | else if (num == 5) |
||
571 | chspec_band = WL_CHANSPEC_BAND_5G; |
||
572 | else |
||
573 | return 0; |
||
574 | |||
575 | /* read the channel number */ |
||
576 | if (!read_uint(&a, &ctl_ch)) |
||
577 | return 0; |
||
578 | |||
579 | c = tolower((int)a[0]); |
||
580 | } |
||
581 | else { |
||
582 | /* first number is channel, use default for band */ |
||
583 | ctl_ch = num; |
||
584 | chspec_band = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? |
||
585 | WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); |
||
586 | } |
||
587 | |||
588 | if (c == '\0') { |
||
589 | /* default BW of 20MHz */ |
||
590 | chspec_bw = WL_CHANSPEC_BW_20; |
||
591 | goto done_read; |
||
592 | } |
||
593 | |||
594 | a ++; /* consume the 'u','l', or '/' */ |
||
595 | |||
596 | /* check 'u'/'l' */ |
||
597 | if (c == 'u' || c == 'l') { |
||
598 | sb_ul = c; |
||
599 | chspec_bw = WL_CHANSPEC_BW_40; |
||
600 | goto done_read; |
||
601 | } |
||
602 | |||
603 | /* next letter must be '/' */ |
||
604 | if (c != '/') |
||
605 | return 0; |
||
606 | |||
607 | /* read bandwidth */ |
||
608 | if (!read_uint(&a, &bw)) |
||
609 | return 0; |
||
610 | |||
611 | /* convert to chspec value */ |
||
612 | if (bw == 20) { |
||
613 | chspec_bw = WL_CHANSPEC_BW_20; |
||
614 | } else if (bw == 40) { |
||
615 | chspec_bw = WL_CHANSPEC_BW_40; |
||
616 | } else if (bw == 80) { |
||
617 | chspec_bw = WL_CHANSPEC_BW_80; |
||
618 | } else if (bw == 160) { |
||
619 | chspec_bw = WL_CHANSPEC_BW_160; |
||
620 | } else { |
||
621 | return 0; |
||
622 | } |
||
623 | |||
624 | /* So far we have <band>g<chan>/<bw> |
||
625 | * Can now be followed by u/l if bw = 40, |
||
626 | * or '+80' if bw = 80, to make '80+80' bw. |
||
627 | */ |
||
628 | |||
629 | c = tolower((int)a[0]); |
||
630 | |||
631 | /* if we have a 2g/40 channel, we should have a l/u spec now */ |
||
632 | if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) { |
||
633 | if (c == 'u' || c == 'l') { |
||
634 | a ++; /* consume the u/l char */ |
||
635 | sb_ul = c; |
||
636 | goto done_read; |
||
637 | } |
||
638 | } |
||
639 | |||
640 | /* check for 80+80 */ |
||
641 | if (c == '+') { |
||
642 | /* 80+80 */ |
||
643 | static const char *plus80 = "80/"; |
||
644 | |||
645 | /* must be looking at '+80/' |
||
646 | * check and consume this string. |
||
647 | */ |
||
648 | chspec_bw = WL_CHANSPEC_BW_8080; |
||
649 | |||
650 | a ++; /* consume the char '+' */ |
||
651 | |||
652 | /* consume the '80/' string */ |
||
653 | for (i = 0; i < 3; i++) { |
||
654 | if (*a++ != *plus80++) { |
||
655 | return 0; |
||
656 | } |
||
657 | } |
||
658 | |||
659 | /* read primary 80MHz channel */ |
||
660 | if (!read_uint(&a, &ch1)) |
||
661 | return 0; |
||
662 | |||
663 | /* must followed by '-' */ |
||
664 | if (a[0] != '-') |
||
665 | return 0; |
||
666 | a ++; /* consume the char */ |
||
667 | |||
668 | /* read secondary 80MHz channel */ |
||
669 | if (!read_uint(&a, &ch2)) |
||
670 | return 0; |
||
671 | } |
||
672 | |||
673 | done_read: |
||
674 | /* skip trailing white space */ |
||
675 | while (a[0] == ' ') { |
||
676 | a ++; |
||
677 | } |
||
678 | |||
679 | /* must be end of string */ |
||
680 | if (a[0] != '\0') |
||
681 | return 0; |
||
682 | |||
683 | /* Now have all the chanspec string parts read; |
||
684 | * chspec_band, ctl_ch, chspec_bw, sb_ul, ch1, ch2. |
||
685 | * chspec_band and chspec_bw are chanspec values. |
||
686 | * Need to convert ctl_ch, sb_ul, and ch1,ch2 into |
||
687 | * a center channel (or two) and sideband. |
||
688 | */ |
||
689 | |||
690 | /* if a sb u/l string was given, just use that, |
||
691 | * guaranteed to be bw = 40 by sting parse. |
||
692 | */ |
||
693 | if (sb_ul != '\0') { |
||
694 | if (sb_ul == 'l') { |
||
695 | chspec_ch = UPPER_20_SB(ctl_ch); |
||
696 | chspec_sb = WL_CHANSPEC_CTL_SB_LLL; |
||
697 | } else if (sb_ul == 'u') { |
||
698 | chspec_ch = LOWER_20_SB(ctl_ch); |
||
699 | chspec_sb = WL_CHANSPEC_CTL_SB_LLU; |
||
700 | } |
||
701 | } |
||
702 | /* if the bw is 20, center and sideband are trivial */ |
||
703 | else if (chspec_bw == WL_CHANSPEC_BW_20) { |
||
704 | chspec_ch = ctl_ch; |
||
705 | chspec_sb = 0; |
||
706 | } |
||
707 | /* if the bw is 40/80/160, not 80+80, a single method |
||
708 | * can be used to to find the center and sideband |
||
709 | */ |
||
710 | else if (chspec_bw != WL_CHANSPEC_BW_8080) { |
||
711 | /* figure out ctl sideband based on ctl channel and bandwidth */ |
||
712 | const uint8 *center_ch = NULL; |
||
713 | int num_ch = 0; |
||
714 | int sb = -1; |
||
715 | |||
716 | if (chspec_bw == WL_CHANSPEC_BW_40) { |
||
717 | center_ch = wf_5g_40m_chans; |
||
718 | num_ch = WF_NUM_5G_40M_CHANS; |
||
719 | } else if (chspec_bw == WL_CHANSPEC_BW_80) { |
||
720 | center_ch = wf_5g_80m_chans; |
||
721 | num_ch = WF_NUM_5G_80M_CHANS; |
||
722 | } else if (chspec_bw == WL_CHANSPEC_BW_160) { |
||
723 | center_ch = wf_5g_160m_chans; |
||
724 | num_ch = WF_NUM_5G_160M_CHANS; |
||
725 | } else { |
||
726 | return 0; |
||
727 | } |
||
728 | |||
729 | for (i = 0; i < num_ch; i ++) { |
||
730 | sb = channel_to_sb(center_ch[i], ctl_ch, bw); |
||
731 | if (sb >= 0) { |
||
732 | chspec_ch = center_ch[i]; |
||
733 | chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT; |
||
734 | break; |
||
735 | } |
||
736 | } |
||
737 | |||
738 | /* check for no matching sb/center */ |
||
739 | if (sb < 0) { |
||
740 | return 0; |
||
741 | } |
||
742 | } |
||
743 | /* Otherwise, bw is 80+80. Figure out channel pair and sb */ |
||
744 | else { |
||
745 | int ch1_id = 0, ch2_id = 0; |
||
746 | int sb; |
||
747 | |||
748 | ch1_id = channel_80mhz_to_id(ch1); |
||
749 | ch2_id = channel_80mhz_to_id(ch2); |
||
750 | |||
751 | /* validate channels */ |
||
752 | if (ch1 >= ch2 || ch1_id < 0 || ch2_id < 0) |
||
753 | return 0; |
||
754 | |||
755 | /* combined channel in chspec */ |
||
756 | chspec_ch = (((uint16)ch1_id << WL_CHANSPEC_CHAN1_SHIFT) | |
||
757 | ((uint16)ch2_id << WL_CHANSPEC_CHAN2_SHIFT)); |
||
758 | |||
759 | /* figure out ctl sideband */ |
||
760 | |||
761 | /* does the primary channel fit with the 1st 80MHz channel ? */ |
||
762 | sb = channel_to_sb(ch1, ctl_ch, bw); |
||
763 | if (sb < 0) { |
||
764 | /* no, so does the primary channel fit with the 2nd 80MHz channel ? */ |
||
765 | sb = channel_to_sb(ch2, ctl_ch, bw); |
||
766 | if (sb < 0) { |
||
767 | /* no match for ctl_ch to either 80MHz center channel */ |
||
768 | return 0; |
||
769 | } |
||
770 | /* sb index is 0-3 for the low 80MHz channel, and 4-7 for |
||
771 | * the high 80MHz channel. Add 4 to to shift to high set. |
||
772 | */ |
||
773 | sb += 4; |
||
774 | } |
||
775 | |||
776 | chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT; |
||
777 | } |
||
778 | |||
779 | chspec = (chspec_ch | chspec_band | chspec_bw | chspec_sb); |
||
780 | |||
781 | if (wf_chspec_malformed(chspec)) |
||
782 | return 0; |
||
783 | |||
784 | return chspec; |
||
785 | } |
||
786 | |||
787 | /* |
||
788 | * Verify the chanspec is using a legal set of parameters, i.e. that the |
||
789 | * chanspec specified a band, bw, ctl_sb and channel and that the |
||
790 | * combination could be legal given any set of circumstances. |
||
791 | * RETURNS: TRUE is the chanspec is malformed, false if it looks good. |
||
792 | */ |
||
793 | bool |
||
794 | wf_chspec_malformed(chanspec_t chanspec) |
||
795 | { |
||
796 | uint chspec_bw = CHSPEC_BW(chanspec); |
||
797 | uint chspec_ch = CHSPEC_CHANNEL(chanspec); |
||
798 | |||
799 | /* must be 2G or 5G band */ |
||
800 | if (CHSPEC_IS2G(chanspec)) { |
||
801 | /* must be valid bandwidth */ |
||
802 | if (chspec_bw != WL_CHANSPEC_BW_20 && |
||
803 | chspec_bw != WL_CHANSPEC_BW_40) { |
||
804 | return TRUE; |
||
805 | } |
||
806 | } else if (CHSPEC_IS5G(chanspec)) { |
||
807 | if (chspec_bw == WL_CHANSPEC_BW_8080) { |
||
808 | uint ch1_id, ch2_id; |
||
809 | |||
810 | /* channel number in 80+80 must be in range */ |
||
811 | ch1_id = CHSPEC_CHAN1(chanspec); |
||
812 | ch2_id = CHSPEC_CHAN2(chanspec); |
||
813 | if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS) |
||
814 | return TRUE; |
||
815 | |||
816 | /* ch2 must be above ch1 for the chanspec */ |
||
817 | if (ch2_id <= ch1_id) |
||
818 | return TRUE; |
||
819 | } else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 || |
||
820 | chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) { |
||
821 | |||
822 | if (chspec_ch > MAXCHANNEL) { |
||
823 | return TRUE; |
||
824 | } |
||
825 | } else { |
||
826 | /* invalid bandwidth */ |
||
827 | return TRUE; |
||
828 | } |
||
829 | } else { |
||
830 | /* must be 2G or 5G band */ |
||
831 | return TRUE; |
||
832 | } |
||
833 | |||
834 | /* side band needs to be consistent with bandwidth */ |
||
835 | if (chspec_bw == WL_CHANSPEC_BW_20) { |
||
836 | if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL) |
||
837 | return TRUE; |
||
838 | } else if (chspec_bw == WL_CHANSPEC_BW_40) { |
||
839 | if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU) |
||
840 | return TRUE; |
||
841 | } else if (chspec_bw == WL_CHANSPEC_BW_80) { |
||
842 | if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU) |
||
843 | return TRUE; |
||
844 | } |
||
845 | |||
846 | return FALSE; |
||
847 | } |
||
848 | |||
849 | /* |
||
850 | * Verify the chanspec specifies a valid channel according to 802.11. |
||
851 | * RETURNS: TRUE if the chanspec is a valid 802.11 channel |
||
852 | */ |
||
853 | bool |
||
854 | wf_chspec_valid(chanspec_t chanspec) |
||
855 | { |
||
856 | uint chspec_bw = CHSPEC_BW(chanspec); |
||
857 | uint chspec_ch = CHSPEC_CHANNEL(chanspec); |
||
858 | |||
859 | if (wf_chspec_malformed(chanspec)) |
||
860 | return FALSE; |
||
861 | |||
862 | if (CHSPEC_IS2G(chanspec)) { |
||
863 | /* must be valid bandwidth and channel range */ |
||
864 | if (chspec_bw == WL_CHANSPEC_BW_20) { |
||
865 | if (chspec_ch >= 1 && chspec_ch <= 14) |
||
866 | return TRUE; |
||
867 | } else if (chspec_bw == WL_CHANSPEC_BW_40) { |
||
868 | if (chspec_ch >= 3 && chspec_ch <= 11) |
||
869 | return TRUE; |
||
870 | } |
||
871 | } else if (CHSPEC_IS5G(chanspec)) { |
||
872 | if (chspec_bw == WL_CHANSPEC_BW_8080) { |
||
873 | uint16 ch1, ch2; |
||
874 | |||
875 | ch1 = wf_5g_80m_chans[CHSPEC_CHAN1(chanspec)]; |
||
876 | ch2 = wf_5g_80m_chans[CHSPEC_CHAN2(chanspec)]; |
||
877 | |||
878 | /* the two channels must be separated by more than 80MHz by VHT req, |
||
879 | * and ch2 above ch1 for the chanspec |
||
880 | */ |
||
881 | if (ch2 > ch1 + CH_80MHZ_APART) |
||
882 | return TRUE; |
||
883 | } else { |
||
884 | const uint8 *center_ch; |
||
885 | uint num_ch, i; |
||
886 | |||
887 | if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40) { |
||
888 | center_ch = wf_5g_40m_chans; |
||
889 | num_ch = WF_NUM_5G_40M_CHANS; |
||
890 | } else if (chspec_bw == WL_CHANSPEC_BW_80) { |
||
891 | center_ch = wf_5g_80m_chans; |
||
892 | num_ch = WF_NUM_5G_80M_CHANS; |
||
893 | } else if (chspec_bw == WL_CHANSPEC_BW_160) { |
||
894 | center_ch = wf_5g_160m_chans; |
||
895 | num_ch = WF_NUM_5G_160M_CHANS; |
||
896 | } else { |
||
897 | /* invalid bandwidth */ |
||
898 | return FALSE; |
||
899 | } |
||
900 | |||
901 | /* check for a valid center channel */ |
||
902 | if (chspec_bw == WL_CHANSPEC_BW_20) { |
||
903 | /* We don't have an array of legal 20MHz 5G channels, but they are |
||
904 | * each side of the legal 40MHz channels. Check the chanspec |
||
905 | * channel against either side of the 40MHz channels. |
||
906 | */ |
||
907 | for (i = 0; i < num_ch; i ++) { |
||
908 | if (chspec_ch == (uint)LOWER_20_SB(center_ch[i]) || |
||
909 | chspec_ch == (uint)UPPER_20_SB(center_ch[i])) |
||
910 | break; /* match found */ |
||
911 | } |
||
912 | |||
913 | if (i == num_ch) { |
||
914 | /* check for channel 165 which is not the side band |
||
915 | * of 40MHz 5G channel |
||
916 | */ |
||
917 | if (chspec_ch == 165) |
||
918 | i = 0; |
||
919 | |||
920 | /* check for legacy JP channels on failure */ |
||
921 | if (chspec_ch == 34 || chspec_ch == 38 || |
||
922 | chspec_ch == 42 || chspec_ch == 46) |
||
923 | i = 0; |
||
924 | } |
||
925 | } else { |
||
926 | /* check the chanspec channel to each legal channel */ |
||
927 | for (i = 0; i < num_ch; i ++) { |
||
928 | if (chspec_ch == center_ch[i]) |
||
929 | break; /* match found */ |
||
930 | } |
||
931 | } |
||
932 | |||
933 | if (i < num_ch) { |
||
934 | /* match found */ |
||
935 | return TRUE; |
||
936 | } |
||
937 | } |
||
938 | } |
||
939 | |||
940 | return FALSE; |
||
941 | } |
||
942 | |||
943 | /* |
||
944 | * This function returns the channel number that control traffic is being sent on, for 20MHz |
||
945 | * channels this is just the channel number, for 40MHZ, 80MHz, 160MHz channels it is the 20MHZ |
||
946 | * sideband depending on the chanspec selected |
||
947 | */ |
||
948 | uint8 |
||
949 | wf_chspec_ctlchan(chanspec_t chspec) |
||
950 | { |
||
951 | uint center_chan; |
||
952 | uint bw_mhz; |
||
953 | uint sb; |
||
954 | |||
955 | ASSERT(!wf_chspec_malformed(chspec)); |
||
956 | |||
957 | /* Is there a sideband ? */ |
||
958 | if (CHSPEC_IS20(chspec)) { |
||
959 | return CHSPEC_CHANNEL(chspec); |
||
960 | } else { |
||
961 | sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT; |
||
962 | |||
963 | if (CHSPEC_IS8080(chspec)) { |
||
964 | bw_mhz = 80; |
||
965 | |||
966 | if (sb < 4) { |
||
967 | center_chan = CHSPEC_CHAN1(chspec); |
||
968 | } |
||
969 | else { |
||
970 | center_chan = CHSPEC_CHAN2(chspec); |
||
971 | sb -= 4; |
||
972 | } |
||
973 | |||
974 | /* convert from channel index to channel number */ |
||
975 | center_chan = wf_5g_80m_chans[center_chan]; |
||
976 | } |
||
977 | else { |
||
978 | bw_mhz = bw_chspec_to_mhz(chspec); |
||
979 | center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT; |
||
980 | } |
||
981 | |||
982 | return (channel_to_ctl_chan(center_chan, bw_mhz, sb)); |
||
983 | } |
||
984 | } |
||
985 | |||
986 | /* |
||
987 | * This function returns the chanspec of the control channel of a given chanspec |
||
988 | */ |
||
989 | chanspec_t |
||
990 | wf_chspec_ctlchspec(chanspec_t chspec) |
||
991 | { |
||
992 | chanspec_t ctl_chspec = chspec; |
||
993 | uint8 ctl_chan; |
||
994 | |||
995 | ASSERT(!wf_chspec_malformed(chspec)); |
||
996 | |||
997 | /* Is there a sideband ? */ |
||
998 | if (!CHSPEC_IS20(chspec)) { |
||
999 | ctl_chan = wf_chspec_ctlchan(chspec); |
||
1000 | ctl_chspec = ctl_chan | WL_CHANSPEC_BW_20; |
||
1001 | ctl_chspec |= CHSPEC_BAND(chspec); |
||
1002 | } |
||
1003 | return ctl_chspec; |
||
1004 | } |
||
1005 | |||
1006 | /* return chanspec given control channel and bandwidth |
||
1007 | * return 0 on error |
||
1008 | */ |
||
1009 | uint16 |
||
1010 | wf_channel2chspec(uint ctl_ch, uint bw) |
||
1011 | { |
||
1012 | uint16 chspec; |
||
1013 | const uint8 *center_ch = NULL; |
||
1014 | int num_ch = 0; |
||
1015 | int sb = -1; |
||
1016 | int i = 0; |
||
1017 | |||
1018 | chspec = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); |
||
1019 | |||
1020 | chspec |= bw; |
||
1021 | |||
1022 | if (bw == WL_CHANSPEC_BW_40) { |
||
1023 | center_ch = wf_5g_40m_chans; |
||
1024 | num_ch = WF_NUM_5G_40M_CHANS; |
||
1025 | bw = 40; |
||
1026 | } else if (bw == WL_CHANSPEC_BW_80) { |
||
1027 | center_ch = wf_5g_80m_chans; |
||
1028 | num_ch = WF_NUM_5G_80M_CHANS; |
||
1029 | bw = 80; |
||
1030 | } else if (bw == WL_CHANSPEC_BW_160) { |
||
1031 | center_ch = wf_5g_160m_chans; |
||
1032 | num_ch = WF_NUM_5G_160M_CHANS; |
||
1033 | bw = 160; |
||
1034 | } else if (bw == WL_CHANSPEC_BW_20) { |
||
1035 | chspec |= ctl_ch; |
||
1036 | return chspec; |
||
1037 | } else { |
||
1038 | return 0; |
||
1039 | } |
||
1040 | |||
1041 | for (i = 0; i < num_ch; i ++) { |
||
1042 | sb = channel_to_sb(center_ch[i], ctl_ch, bw); |
||
1043 | if (sb >= 0) { |
||
1044 | chspec |= center_ch[i]; |
||
1045 | chspec |= (sb << WL_CHANSPEC_CTL_SB_SHIFT); |
||
1046 | break; |
||
1047 | } |
||
1048 | } |
||
1049 | |||
1050 | /* check for no matching sb/center */ |
||
1051 | if (sb < 0) { |
||
1052 | return 0; |
||
1053 | } |
||
1054 | |||
1055 | return chspec; |
||
1056 | } |
||
1057 | |||
1058 | #endif /* D11AC_IOTYPES */ |
||
1059 | |||
1060 | /* |
||
1061 | * This function returns the chanspec for the primary 40MHz of an 80MHz channel. |
||
1062 | * The control sideband specifies the same 20MHz channel that the 80MHz channel is using |
||
1063 | * as the primary 20MHz channel. |
||
1064 | */ |
||
1065 | extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec) |
||
1066 | { |
||
1067 | chanspec_t chspec40 = chspec; |
||
1068 | uint center_chan; |
||
1069 | uint sb; |
||
1070 | |||
1071 | ASSERT(!wf_chspec_malformed(chspec)); |
||
1072 | |||
1073 | if (CHSPEC_IS80(chspec)) { |
||
1074 | center_chan = CHSPEC_CHANNEL(chspec); |
||
1075 | sb = CHSPEC_CTL_SB(chspec); |
||
1076 | |||
1077 | if (sb == WL_CHANSPEC_CTL_SB_UL) { |
||
1078 | /* Primary 40MHz is on upper side */ |
||
1079 | sb = WL_CHANSPEC_CTL_SB_L; |
||
1080 | center_chan += CH_20MHZ_APART; |
||
1081 | } else if (sb == WL_CHANSPEC_CTL_SB_UU) { |
||
1082 | /* Primary 40MHz is on upper side */ |
||
1083 | sb = WL_CHANSPEC_CTL_SB_U; |
||
1084 | center_chan += CH_20MHZ_APART; |
||
1085 | } else { |
||
1086 | /* Primary 40MHz is on lower side */ |
||
1087 | /* sideband bits are the same for LL/LU and L/U */ |
||
1088 | center_chan -= CH_20MHZ_APART; |
||
1089 | } |
||
1090 | |||
1091 | /* Create primary 40MHz chanspec */ |
||
1092 | chspec40 = (WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_40 | |
||
1093 | sb | center_chan); |
||
1094 | } |
||
1095 | |||
1096 | return chspec40; |
||
1097 | } |
||
1098 | |||
1099 | /* |
||
1100 | * Return the channel number for a given frequency and base frequency. |
||
1101 | * The returned channel number is relative to the given base frequency. |
||
1102 | * If the given base frequency is zero, a base frequency of 5 GHz is assumed for |
||
1103 | * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz. |
||
1104 | * |
||
1105 | * Frequency is specified in MHz. |
||
1106 | * The base frequency is specified as (start_factor * 500 kHz). |
||
1107 | * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for |
||
1108 | * 2.4 GHz and 5 GHz bands. |
||
1109 | * |
||
1110 | * The returned channel will be in the range [1, 14] in the 2.4 GHz band |
||
1111 | * and [0, 200] otherwise. |
||
1112 | * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the |
||
1113 | * frequency is not a 2.4 GHz channel, or if the frequency is not and even |
||
1114 | * multiple of 5 MHz from the base frequency to the base plus 1 GHz. |
||
1115 | * |
||
1116 | * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 |
||
1117 | */ |
||
1118 | int |
||
1119 | wf_mhz2channel(uint freq, uint start_factor) |
||
1120 | { |
||
1121 | int ch = -1; |
||
1122 | uint base; |
||
1123 | int offset; |
||
1124 | |||
1125 | /* take the default channel start frequency */ |
||
1126 | if (start_factor == 0) { |
||
1127 | if (freq >= 2400 && freq <= 2500) |
||
1128 | start_factor = WF_CHAN_FACTOR_2_4_G; |
||
1129 | else if (freq >= 5000 && freq <= 6000) |
||
1130 | start_factor = WF_CHAN_FACTOR_5_G; |
||
1131 | } |
||
1132 | |||
1133 | if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G) |
||
1134 | return 14; |
||
1135 | |||
1136 | base = start_factor / 2; |
||
1137 | |||
1138 | /* check that the frequency is in 1GHz range of the base */ |
||
1139 | if ((freq < base) || (freq > base + 1000)) |
||
1140 | return -1; |
||
1141 | |||
1142 | offset = freq - base; |
||
1143 | ch = offset / 5; |
||
1144 | |||
1145 | /* check that frequency is a 5MHz multiple from the base */ |
||
1146 | if (offset != (ch * 5)) |
||
1147 | return -1; |
||
1148 | |||
1149 | /* restricted channel range check for 2.4G */ |
||
1150 | if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13)) |
||
1151 | return -1; |
||
1152 | |||
1153 | return ch; |
||
1154 | } |
||
1155 | |||
1156 | /* |
||
1157 | * Return the center frequency in MHz of the given channel and base frequency. |
||
1158 | * The channel number is interpreted relative to the given base frequency. |
||
1159 | * |
||
1160 | * The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise. |
||
1161 | * The base frequency is specified as (start_factor * 500 kHz). |
||
1162 | * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_4_G, and WF_CHAN_FACTOR_5_G |
||
1163 | * are defined for 2.4 GHz, 4 GHz, and 5 GHz bands. |
||
1164 | * The channel range of [1, 14] is only checked for a start_factor of |
||
1165 | * WF_CHAN_FACTOR_2_4_G (4814 = 2407 * 2). |
||
1166 | * Odd start_factors produce channels on .5 MHz boundaries, in which case |
||
1167 | * the answer is rounded down to an integral MHz. |
||
1168 | * -1 is returned for an out of range channel. |
||
1169 | * |
||
1170 | * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 |
||
1171 | */ |
||
1172 | int |
||
1173 | wf_channel2mhz(uint ch, uint start_factor) |
||
1174 | { |
||
1175 | int freq; |
||
1176 | |||
1177 | if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) || |
||
1178 | (ch > 200)) |
||
1179 | freq = -1; |
||
1180 | else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14)) |
||
1181 | freq = 2484; |
||
1182 | else |
||
1183 | freq = ch * 5 + start_factor / 2; |
||
1184 | |||
1185 | return freq; |
||
1186 | } |
||
1187 | |||
1188 | /* These chan_info[] & lookup routines replicate those from wlc_phy.c because of BMAC split */ |
||
1189 | static const struct chan_info { |
||
1190 | uint16 chan; /* channel number */ |
||
1191 | uint16 freq; /* in MHz */ |
||
1192 | } chan_info[] = { |
||
1193 | /* 11b/11g */ |
||
1194 | /* 0 */ {1, 2412}, |
||
1195 | /* 1 */ {2, 2417}, |
||
1196 | /* 2 */ {3, 2422}, |
||
1197 | /* 3 */ {4, 2427}, |
||
1198 | /* 4 */ {5, 2432}, |
||
1199 | /* 5 */ {6, 2437}, |
||
1200 | /* 6 */ {7, 2442}, |
||
1201 | /* 7 */ {8, 2447}, |
||
1202 | /* 8 */ {9, 2452}, |
||
1203 | /* 9 */ {10, 2457}, |
||
1204 | /* 10 */ {11, 2462}, |
||
1205 | /* 11 */ {12, 2467}, |
||
1206 | /* 12 */ {13, 2472}, |
||
1207 | /* 13 */ {14, 2484}, |
||
1208 | |||
1209 | #ifdef BAND5G |
||
1210 | /* 11a japan high */ |
||
1211 | /* 14 */ {34, 5170}, |
||
1212 | /* 15 */ {38, 5190}, |
||
1213 | /* 16 */ {42, 5210}, |
||
1214 | /* 17 */ {46, 5230}, |
||
1215 | |||
1216 | /* 11a usa low */ |
||
1217 | /* 18 */ {36, 5180}, |
||
1218 | /* 19 */ {40, 5200}, |
||
1219 | /* 20 */ {44, 5220}, |
||
1220 | /* 21 */ {48, 5240}, |
||
1221 | /* 22 */ {52, 5260}, |
||
1222 | /* 23 */ {56, 5280}, |
||
1223 | /* 24 */ {60, 5300}, |
||
1224 | /* 25 */ {64, 5320}, |
||
1225 | |||
1226 | /* 11a Europe */ |
||
1227 | /* 26 */ {100, 5500}, |
||
1228 | /* 27 */ {104, 5520}, |
||
1229 | /* 28 */ {108, 5540}, |
||
1230 | /* 29 */ {112, 5560}, |
||
1231 | /* 30 */ {116, 5580}, |
||
1232 | /* 31 */ {120, 5600}, |
||
1233 | /* 32 */ {124, 5620}, |
||
1234 | /* 33 */ {128, 5640}, |
||
1235 | /* 34 */ {132, 5660}, |
||
1236 | /* 35 */ {136, 5680}, |
||
1237 | /* 36 */ {140, 5700}, |
||
1238 | |||
1239 | /* 11a usa high, ref5 only */ |
||
1240 | /* 37 */ {149, 5745}, |
||
1241 | /* 38 */ {153, 5765}, |
||
1242 | /* 39 */ {157, 5785}, |
||
1243 | /* 40 */ {161, 5805}, |
||
1244 | /* 41 */ {165, 5825}, |
||
1245 | |||
1246 | /* 11a japan */ |
||
1247 | /* 42 */ {184, 4920}, |
||
1248 | /* 43 */ {188, 4940}, |
||
1249 | /* 44 */ {192, 4960}, |
||
1250 | /* 45 */ {196, 4980}, |
||
1251 | /* 46 */ {200, 5000}, |
||
1252 | /* 47 */ {204, 5020}, |
||
1253 | /* 48 */ {208, 5040}, |
||
1254 | /* 49 */ {212, 5060}, |
||
1255 | /* 50 */ {216, 5080} |
||
1256 | #endif /* BAND5G */ |
||
1257 | }; |
||
1258 | |||
1259 | /* |
||
1260 | * Converts channel frequency to channel number. |
||
1261 | * Returns 0 if the frequency does not match any channel definition. |
||
1262 | */ |
||
1263 | uint |
||
1264 | wf_freq2channel(uint freq) |
||
1265 | { |
||
1266 | uint i; |
||
1267 | |||
1268 | for (i = 0; i < ARRAYSIZE(chan_info); i++) { |
||
1269 | if (chan_info[i].freq == freq) |
||
1270 | return (chan_info[i].chan); |
||
1271 | } |
||
1272 | return (0); |
||
1273 | } |
||
1274 | |||
1275 | /* |
||
1276 | * Converts channel number to channel frequency. |
||
1277 | * Returns 0 if the channel is out of range. |
||
1278 | * Also used by some code in wlc_iw.c |
||
1279 | */ |
||
1280 | uint |
||
1281 | wf_channel2freq(uint channel) |
||
1282 | { |
||
1283 | uint i; |
||
1284 | |||
1285 | for (i = 0; i < ARRAYSIZE(chan_info); i++) |
||
1286 | if (chan_info[i].chan == channel) |
||
1287 | return (chan_info[i].freq); |
||
1288 | return (0); |
||
1289 | } |