Commit | Line | Data |
---|---|---|
84813812 LJ |
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-2019, Broadcom. | |
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 | * | |
26 | * | |
27 | * <<Broadcom-WL-IPTag/Open:>> | |
28 | * | |
29 | * $Id: bcmwifi_channels.c 806092 2019-02-21 08:19:13Z $ | |
30 | */ | |
31 | ||
32 | #include <bcm_cfg.h> | |
33 | #include <typedefs.h> | |
34 | #include <bcmutils.h> | |
35 | ||
36 | #ifdef BCMDRIVER | |
37 | #include <osl.h> | |
38 | #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) | |
39 | #define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) | |
40 | #else | |
41 | #include <stdio.h> | |
42 | #include <stdlib.h> | |
43 | #include <ctype.h> | |
44 | #ifndef ASSERT | |
45 | #define ASSERT(exp) | |
46 | #endif // endif | |
47 | #endif /* BCMDRIVER */ | |
48 | ||
49 | #include <bcmwifi_channels.h> | |
50 | ||
51 | #if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL)) | |
52 | #include <bcmstdlib.h> /* For wl/exe/GNUmakefile.brcm_wlu and GNUmakefile.wlm_dll */ | |
53 | #endif // endif | |
54 | ||
55 | #include <802.11.h> | |
56 | ||
57 | /* Definitions for D11AC capable (80MHz+) Chanspec type */ | |
58 | ||
59 | /* Chanspec ASCII representation: | |
60 | * [<band> 'g'] <channel> ['/'<bandwidth> [<primary-sideband>]['/'<1st80channel>'-'<2nd80channel>]] | |
61 | * | |
62 | * <band>: | |
63 | * (optional) 2, 3, 4, 5 for 2.4GHz, 3GHz, 4GHz, and 5GHz respectively. | |
64 | * Default value is 2g if channel <= 14, otherwise 5g. | |
65 | * <channel>: | |
66 | * channel number of the 5MHz, 10MHz, 20MHz channel, | |
67 | * or primary channel of 40MHz, 80MHz, 160MHz, or 80+80MHz channel. | |
68 | * <bandwidth>: | |
69 | * (optional) 5, 10, 20, 40, 80, 160, or 80+80. Default value is 20. | |
70 | * <primary-sideband>: | |
71 | * (only for 2.4GHz band 40MHz) U for upper sideband primary, L for lower. | |
72 | * | |
73 | * For 2.4GHz band 40MHz channels, the same primary channel may be the | |
74 | * upper sideband for one 40MHz channel, and the lower sideband for an | |
75 | * overlapping 40MHz channel. The U/L disambiguates which 40MHz channel | |
76 | * is being specified. | |
77 | * | |
78 | * For 40MHz in the 5GHz band and all channel bandwidths greater than | |
79 | * 40MHz, the U/L specificaion is not allowed since the channels are | |
80 | * non-overlapping and the primary sub-band is derived from its | |
81 | * position in the wide bandwidth channel. | |
82 | * | |
83 | * <1st80Channel>: | |
84 | * <2nd80Channel>: | |
85 | * Required for 80+80, otherwise not allowed. | |
86 | * Specifies the center channel of the primary and secondary 80MHz band. | |
87 | * | |
88 | * In its simplest form, it is a 20MHz channel number, with the implied band | |
89 | * of 2.4GHz if channel number <= 14, and 5GHz otherwise. | |
90 | * | |
91 | * To allow for backward compatibility with scripts, the old form for | |
92 | * 40MHz channels is also allowed: <channel><primary-sideband> | |
93 | * | |
94 | * <channel>: | |
95 | * primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz | |
96 | * <primary-sideband>: | |
97 | * "U" for upper, "L" for lower (or lower case "u" "l") | |
98 | * | |
99 | * 5 GHz Examples: | |
100 | * Chanspec BW Center Ch Channel Range Primary Ch | |
101 | * 5g8 20MHz 8 - - | |
102 | * 52 20MHz 52 - - | |
103 | * 52/40 40MHz 54 52-56 52 | |
104 | * 56/40 40MHz 54 52-56 56 | |
105 | * 52/80 80MHz 58 52-64 52 | |
106 | * 56/80 80MHz 58 52-64 56 | |
107 | * 60/80 80MHz 58 52-64 60 | |
108 | * 64/80 80MHz 58 52-64 64 | |
109 | * 52/160 160MHz 50 36-64 52 | |
110 | * 36/160 160MGz 50 36-64 36 | |
111 | * 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36 | |
112 | * | |
113 | * 2 GHz Examples: | |
114 | * Chanspec BW Center Ch Channel Range Primary Ch | |
115 | * 2g8 20MHz 8 - - | |
116 | * 8 20MHz 8 - - | |
117 | * 6 20MHz 6 - - | |
118 | * 6/40l 40MHz 8 6-10 6 | |
119 | * 6l 40MHz 8 6-10 6 | |
120 | * 6/40u 40MHz 4 2-6 6 | |
121 | * 6u 40MHz 4 2-6 6 | |
122 | */ | |
123 | ||
124 | /* bandwidth ASCII string */ | |
125 | static const char *wf_chspec_bw_str[] = | |
126 | { | |
127 | "5", | |
128 | "10", | |
129 | "20", | |
130 | "40", | |
131 | "80", | |
132 | "160", | |
133 | "80+80", | |
134 | "na" | |
135 | }; | |
136 | ||
137 | static const uint8 wf_chspec_bw_mhz[] = | |
138 | {5, 10, 20, 40, 80, 160, 160}; | |
139 | ||
140 | #define WF_NUM_BW \ | |
141 | (sizeof(wf_chspec_bw_mhz)/sizeof(uint8)) | |
142 | ||
143 | /* 40MHz channels in 5GHz band */ | |
144 | static const uint8 wf_5g_40m_chans[] = | |
145 | {38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159, 167, 175}; | |
146 | #define WF_NUM_5G_40M_CHANS \ | |
147 | (sizeof(wf_5g_40m_chans)/sizeof(uint8)) | |
148 | ||
149 | /* 80MHz channels in 5GHz band */ | |
150 | static const uint8 wf_5g_80m_chans[] = | |
151 | {42, 58, 106, 122, 138, 155, 171}; | |
152 | #define WF_NUM_5G_80M_CHANS \ | |
153 | (sizeof(wf_5g_80m_chans)/sizeof(uint8)) | |
154 | ||
155 | /* 160MHz channels in 5GHz band */ | |
156 | static const uint8 wf_5g_160m_chans[] = | |
157 | {50, 114}; | |
158 | #define WF_NUM_5G_160M_CHANS \ | |
159 | (sizeof(wf_5g_160m_chans)/sizeof(uint8)) | |
160 | ||
161 | /* opclass and channel information for US. Table E-1 */ | |
162 | static const uint16 opclass_data[] = { | |
163 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
164 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
165 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
166 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
167 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
168 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_5)&WL_CHANSPEC_BW_MASK)), | |
169 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_5)&WL_CHANSPEC_BW_MASK)), | |
170 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_10)&WL_CHANSPEC_BW_MASK)), | |
171 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_10)&WL_CHANSPEC_BW_MASK)), | |
172 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
173 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
174 | (WL_CHANSPEC_BAND_2G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
175 | (WL_CHANSPEC_BAND_3G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
176 | (WL_CHANSPEC_BAND_3G |((WL_CHANSPEC_BW_10)&WL_CHANSPEC_BW_MASK)), | |
177 | (WL_CHANSPEC_BAND_3G |((WL_CHANSPEC_BW_5)&WL_CHANSPEC_BW_MASK)), | |
178 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_5)&WL_CHANSPEC_BW_MASK)), | |
179 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_10)&WL_CHANSPEC_BW_MASK)), | |
180 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_20)&WL_CHANSPEC_BW_MASK)), | |
181 | 0, | |
182 | 0, | |
183 | 0, | |
184 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_LOWER), | |
185 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_LOWER), | |
186 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_LOWER), | |
187 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_LOWER), | |
188 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_LOWER), | |
189 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_UPPER), | |
190 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_UPPER), | |
191 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_UPPER), | |
192 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_UPPER), | |
193 | (WL_CHANSPEC_BAND_5G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_UPPER), | |
194 | (WL_CHANSPEC_BAND_2G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_LOWER), | |
195 | (WL_CHANSPEC_BAND_2G |((WL_CHANSPEC_BW_40)&WL_CHANSPEC_BW_MASK)|WL_CHANSPEC_CTL_SB_UPPER), | |
196 | }; | |
197 | ||
198 | /** | |
199 | * Return the chanspec bandwidth in MHz | |
200 | * Bandwidth of 160 MHz will be returned for 80+80MHz chanspecs. | |
201 | * | |
202 | * @param chspec chanspec_t | |
203 | * | |
204 | * @return bandwidth of chspec in MHz units | |
205 | */ | |
206 | uint | |
207 | wf_bw_chspec_to_mhz(chanspec_t chspec) | |
208 | { | |
209 | uint bw; | |
210 | ||
211 | bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT; | |
212 | return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]); | |
213 | } | |
214 | ||
215 | /* bw in MHz, return the channel count from the center channel to the | |
216 | * the channel at the edge of the band | |
217 | */ | |
218 | static uint8 | |
219 | center_chan_to_edge(uint bw) | |
220 | { | |
221 | /* edge channels separated by BW - 10MHz on each side | |
222 | * delta from cf to edge is half of that, | |
223 | * MHz to channel num conversion is 5MHz/channel | |
224 | */ | |
225 | return (uint8)(((bw - 20) / 2) / 5); | |
226 | } | |
227 | ||
228 | /* return channel number of the low edge of the band | |
229 | * given the center channel and BW | |
230 | */ | |
231 | static uint8 | |
232 | channel_low_edge(uint center_ch, uint bw) | |
233 | { | |
234 | return (uint8)(center_ch - center_chan_to_edge(bw)); | |
235 | } | |
236 | ||
237 | /* return side band number given center channel and primary20 channel | |
238 | * return -1 on error | |
239 | */ | |
240 | static int | |
241 | channel_to_sb(uint center_ch, uint primary_ch, uint bw) | |
242 | { | |
243 | uint lowest = channel_low_edge(center_ch, bw); | |
244 | uint sb; | |
245 | ||
246 | if ((primary_ch - lowest) % 4) { | |
247 | /* bad primary channel, not mult 4 */ | |
248 | return -1; | |
249 | } | |
250 | ||
251 | sb = ((primary_ch - lowest) / 4); | |
252 | ||
253 | /* sb must be a index to a 20MHz channel in range */ | |
254 | if (sb >= (bw / 20)) { | |
255 | /* primary_ch must have been too high for the center_ch */ | |
256 | return -1; | |
257 | } | |
258 | ||
259 | return (int)sb; | |
260 | } | |
261 | ||
262 | /* return primary20 channel given center channel and side band */ | |
263 | static uint8 | |
264 | channel_to_primary20_chan(uint center_ch, uint bw, uint sb) | |
265 | { | |
266 | return (uint8)(channel_low_edge(center_ch, bw) + sb * 4); | |
267 | } | |
268 | ||
269 | /* return index of 80MHz channel from channel number | |
270 | * return -1 on error | |
271 | */ | |
272 | static int | |
273 | channel_80mhz_to_id(uint ch) | |
274 | { | |
275 | uint i; | |
276 | for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) { | |
277 | if (ch == wf_5g_80m_chans[i]) | |
278 | return (int)i; | |
279 | } | |
280 | ||
281 | return -1; | |
282 | } | |
283 | ||
284 | /* wrapper function for wf_chspec_ntoa. In case of an error it puts | |
285 | * the original chanspec in the output buffer, prepended with "invalid". | |
286 | * Can be directly used in print routines as it takes care of null | |
287 | */ | |
288 | char * | |
289 | wf_chspec_ntoa_ex(chanspec_t chspec, char *buf) | |
290 | { | |
291 | if (wf_chspec_ntoa(chspec, buf) == NULL) | |
292 | snprintf(buf, CHANSPEC_STR_LEN, "invalid 0x%04x", chspec); | |
293 | return buf; | |
294 | } | |
295 | ||
296 | /* given a chanspec and a string buffer, format the chanspec as a | |
297 | * string, and return the original pointer a. | |
298 | * Min buffer length must be CHANSPEC_STR_LEN. | |
299 | * On error return NULL | |
300 | */ | |
301 | char * | |
302 | wf_chspec_ntoa(chanspec_t chspec, char *buf) | |
303 | { | |
304 | const char *band; | |
305 | uint pri_chan; | |
306 | ||
307 | if (wf_chspec_malformed(chspec)) | |
308 | return NULL; | |
309 | ||
310 | band = ""; | |
311 | ||
312 | /* check for non-default band spec */ | |
313 | if ((CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) || | |
314 | (CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL)) | |
315 | band = (CHSPEC_IS2G(chspec)) ? "2g" : "5g"; | |
316 | ||
317 | /* primary20 channel */ | |
318 | pri_chan = wf_chspec_primary20_chan(chspec); | |
319 | ||
320 | /* bandwidth and primary20 sideband */ | |
321 | if (CHSPEC_IS20(chspec)) { | |
322 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, pri_chan); | |
323 | } else if (!CHSPEC_IS8080(chspec)) { | |
324 | const char *bw; | |
325 | const char *sb = ""; | |
326 | ||
327 | bw = wf_chspec_to_bw_str(chspec); | |
328 | ||
329 | #ifdef CHANSPEC_NEW_40MHZ_FORMAT | |
330 | /* primary20 sideband string if needed for 2g 40MHz */ | |
331 | if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) { | |
332 | sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; | |
333 | } | |
334 | ||
335 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, pri_chan, bw, sb); | |
336 | #else | |
337 | /* primary20 sideband string instead of BW for 40MHz */ | |
338 | if (CHSPEC_IS40(chspec)) { | |
339 | sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; | |
340 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, pri_chan, sb); | |
341 | } else { | |
342 | snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, pri_chan, bw); | |
343 | } | |
344 | #endif /* CHANSPEC_NEW_40MHZ_FORMAT */ | |
345 | ||
346 | } else { | |
347 | /* 80+80 */ | |
348 | uint chan1 = (chspec & WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT; | |
349 | uint chan2 = (chspec & WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT; | |
350 | ||
351 | /* convert to channel number */ | |
352 | chan1 = (chan1 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan1] : 0; | |
353 | chan2 = (chan2 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan2] : 0; | |
354 | ||
355 | /* Outputs a max of CHANSPEC_STR_LEN chars including '\0' */ | |
356 | snprintf(buf, CHANSPEC_STR_LEN, "%d/80+80/%d-%d", pri_chan, chan1, chan2); | |
357 | } | |
358 | ||
359 | return (buf); | |
360 | } | |
361 | ||
362 | static int | |
363 | read_uint(const char **p, unsigned int *num) | |
364 | { | |
365 | unsigned long val; | |
366 | char *endp = NULL; | |
367 | ||
368 | val = strtoul(*p, &endp, 10); | |
369 | /* if endp is the initial pointer value, then a number was not read */ | |
370 | if (endp == *p) | |
371 | return 0; | |
372 | ||
373 | /* advance the buffer pointer to the end of the integer string */ | |
374 | *p = endp; | |
375 | /* return the parsed integer */ | |
376 | *num = (unsigned int)val; | |
377 | ||
378 | return 1; | |
379 | } | |
380 | ||
381 | /* given a chanspec string, convert to a chanspec. | |
382 | * On error return 0 | |
383 | */ | |
384 | chanspec_t | |
385 | wf_chspec_aton(const char *a) | |
386 | { | |
387 | chanspec_t chspec; | |
388 | uint chspec_ch, chspec_band, bw, chspec_bw, chspec_sb; | |
389 | uint num, pri_ch; | |
390 | uint ch1, ch2; | |
391 | char c, sb_ul = '\0'; | |
392 | int i; | |
393 | ||
394 | bw = 20; | |
395 | chspec_sb = 0; | |
396 | chspec_ch = ch1 = ch2 = 0; | |
397 | ||
398 | /* parse channel num or band */ | |
399 | if (!read_uint(&a, &num)) | |
400 | return 0; | |
401 | /* if we are looking at a 'g', then the first number was a band */ | |
402 | c = tolower(a[0]); | |
403 | if (c == 'g') { | |
404 | a++; /* consume the char */ | |
405 | ||
406 | /* band must be "2" or "5" */ | |
407 | if (num == 2) | |
408 | chspec_band = WL_CHANSPEC_BAND_2G; | |
409 | else if (num == 5) | |
410 | chspec_band = WL_CHANSPEC_BAND_5G; | |
411 | else | |
412 | return 0; | |
413 | ||
414 | /* read the channel number */ | |
415 | if (!read_uint(&a, &pri_ch)) | |
416 | return 0; | |
417 | ||
418 | c = tolower(a[0]); | |
419 | } | |
420 | else { | |
421 | /* first number is channel, use default for band */ | |
422 | pri_ch = num; | |
423 | chspec_band = ((pri_ch <= CH_MAX_2G_CHANNEL) ? | |
424 | WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); | |
425 | } | |
426 | ||
427 | if (c == '\0') { | |
428 | /* default BW of 20MHz */ | |
429 | chspec_bw = WL_CHANSPEC_BW_20; | |
430 | goto done_read; | |
431 | } | |
432 | ||
433 | a ++; /* consume the 'u','l', or '/' */ | |
434 | ||
435 | /* check 'u'/'l' */ | |
436 | if (c == 'u' || c == 'l') { | |
437 | sb_ul = c; | |
438 | chspec_bw = WL_CHANSPEC_BW_40; | |
439 | goto done_read; | |
440 | } | |
441 | ||
442 | /* next letter must be '/' */ | |
443 | if (c != '/') | |
444 | return 0; | |
445 | ||
446 | /* read bandwidth */ | |
447 | if (!read_uint(&a, &bw)) | |
448 | return 0; | |
449 | ||
450 | /* convert to chspec value */ | |
451 | if (bw == 5) { | |
452 | chspec_bw = WL_CHANSPEC_BW_5; | |
453 | } else if (bw == 10) { | |
454 | chspec_bw = WL_CHANSPEC_BW_10; | |
455 | } else if (bw == 20) { | |
456 | chspec_bw = WL_CHANSPEC_BW_20; | |
457 | } else if (bw == 40) { | |
458 | chspec_bw = WL_CHANSPEC_BW_40; | |
459 | } else if (bw == 80) { | |
460 | chspec_bw = WL_CHANSPEC_BW_80; | |
461 | } else if (bw == 160) { | |
462 | chspec_bw = WL_CHANSPEC_BW_160; | |
463 | } else { | |
464 | return 0; | |
465 | } | |
466 | ||
467 | /* So far we have <band>g<chan>/<bw> | |
468 | * Can now be followed by u/l if bw = 40, | |
469 | * or '+80' if bw = 80, to make '80+80' bw. | |
470 | */ | |
471 | ||
472 | c = (char)tolower((int)a[0]); | |
473 | ||
474 | /* if we have a 2g/40 channel, we should have a l/u spec now */ | |
475 | if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) { | |
476 | if (c == 'u' || c == 'l') { | |
477 | a ++; /* consume the u/l char */ | |
478 | sb_ul = c; | |
479 | goto done_read; | |
480 | } | |
481 | } | |
482 | ||
483 | /* check for 80+80 */ | |
484 | if (c == '+') { | |
485 | /* 80+80 */ | |
486 | const char plus80[] = "80/"; | |
487 | ||
488 | /* must be looking at '+80/' | |
489 | * check and consume this string. | |
490 | */ | |
491 | chspec_bw = WL_CHANSPEC_BW_8080; | |
492 | ||
493 | a ++; /* consume the char '+' */ | |
494 | ||
495 | /* consume the '80/' string */ | |
496 | for (i = 0; i < 3; i++) { | |
497 | if (*a++ != plus80[i]) { | |
498 | return 0; | |
499 | } | |
500 | } | |
501 | ||
502 | /* read primary 80MHz channel */ | |
503 | if (!read_uint(&a, &ch1)) | |
504 | return 0; | |
505 | ||
506 | /* must followed by '-' */ | |
507 | if (a[0] != '-') | |
508 | return 0; | |
509 | a ++; /* consume the char */ | |
510 | ||
511 | /* read secondary 80MHz channel */ | |
512 | if (!read_uint(&a, &ch2)) | |
513 | return 0; | |
514 | } | |
515 | ||
516 | done_read: | |
517 | /* skip trailing white space */ | |
518 | while (a[0] == ' ') { | |
519 | a ++; | |
520 | } | |
521 | ||
522 | /* must be end of string */ | |
523 | if (a[0] != '\0') | |
524 | return 0; | |
525 | ||
526 | /* Now have all the chanspec string parts read; | |
527 | * chspec_band, pri_ch, chspec_bw, sb_ul, ch1, ch2. | |
528 | * chspec_band and chspec_bw are chanspec values. | |
529 | * Need to convert pri_ch, sb_ul, and ch1,ch2 into | |
530 | * a center channel (or two) and sideband. | |
531 | */ | |
532 | ||
533 | /* if a sb u/l string was given, just use that, | |
534 | * guaranteed to be bw = 40 by sting parse. | |
535 | */ | |
536 | if (sb_ul != '\0') { | |
537 | if (sb_ul == 'l') { | |
538 | chspec_ch = UPPER_20_SB(pri_ch); | |
539 | chspec_sb = WL_CHANSPEC_CTL_SB_LLL; | |
540 | } else if (sb_ul == 'u') { | |
541 | chspec_ch = LOWER_20_SB(pri_ch); | |
542 | chspec_sb = WL_CHANSPEC_CTL_SB_LLU; | |
543 | } | |
544 | } | |
545 | /* if the bw is 20, center and sideband are trivial */ | |
546 | else if (chspec_bw == WL_CHANSPEC_BW_20) { | |
547 | chspec_ch = pri_ch; | |
548 | chspec_sb = WL_CHANSPEC_CTL_SB_NONE; | |
549 | } | |
550 | /* if the bw is 40/80/160, not 80+80, a single method | |
551 | * can be used to to find the center and sideband | |
552 | */ | |
553 | else if (chspec_bw != WL_CHANSPEC_BW_8080) { | |
554 | /* figure out primary20 sideband based on primary20 channel and bandwidth */ | |
555 | const uint8 *center_ch = NULL; | |
556 | int num_ch = 0; | |
557 | int sb = -1; | |
558 | ||
559 | if (chspec_bw == WL_CHANSPEC_BW_40) { | |
560 | center_ch = wf_5g_40m_chans; | |
561 | num_ch = WF_NUM_5G_40M_CHANS; | |
562 | } else if (chspec_bw == WL_CHANSPEC_BW_80) { | |
563 | center_ch = wf_5g_80m_chans; | |
564 | num_ch = WF_NUM_5G_80M_CHANS; | |
565 | } else if (chspec_bw == WL_CHANSPEC_BW_160) { | |
566 | center_ch = wf_5g_160m_chans; | |
567 | num_ch = WF_NUM_5G_160M_CHANS; | |
568 | } else { | |
569 | return 0; | |
570 | } | |
571 | ||
572 | for (i = 0; i < num_ch; i ++) { | |
573 | sb = channel_to_sb(center_ch[i], pri_ch, bw); | |
574 | if (sb >= 0) { | |
575 | chspec_ch = center_ch[i]; | |
576 | chspec_sb = (uint)(sb << WL_CHANSPEC_CTL_SB_SHIFT); | |
577 | break; | |
578 | } | |
579 | } | |
580 | ||
581 | /* check for no matching sb/center */ | |
582 | if (sb < 0) { | |
583 | return 0; | |
584 | } | |
585 | } | |
586 | /* Otherwise, bw is 80+80. Figure out channel pair and sb */ | |
587 | else { | |
588 | int ch1_id = 0, ch2_id = 0; | |
589 | int sb; | |
590 | ||
591 | /* look up the channel ID for the specified channel numbers */ | |
592 | ch1_id = channel_80mhz_to_id(ch1); | |
593 | ch2_id = channel_80mhz_to_id(ch2); | |
594 | ||
595 | /* validate channels */ | |
596 | if (ch1_id < 0 || ch2_id < 0) | |
597 | return 0; | |
598 | ||
599 | /* combine 2 channel IDs in channel field of chspec */ | |
600 | chspec_ch = (((uint)ch1_id << WL_CHANSPEC_CHAN1_SHIFT) | | |
601 | ((uint)ch2_id << WL_CHANSPEC_CHAN2_SHIFT)); | |
602 | ||
603 | /* figure out primary 20 MHz sideband */ | |
604 | ||
605 | /* is the primary channel contained in the 1st 80MHz channel? */ | |
606 | sb = channel_to_sb(ch1, pri_ch, bw); | |
607 | if (sb < 0) { | |
608 | /* no match for primary channel 'pri_ch' in segment0 80MHz channel */ | |
609 | return 0; | |
610 | } | |
611 | ||
612 | chspec_sb = (uint)(sb << WL_CHANSPEC_CTL_SB_SHIFT); | |
613 | } | |
614 | ||
615 | chspec = (chanspec_t)(chspec_ch | chspec_band | chspec_bw | chspec_sb); | |
616 | ||
617 | if (wf_chspec_malformed(chspec)) | |
618 | return 0; | |
619 | ||
620 | return chspec; | |
621 | } | |
622 | ||
623 | /* | |
624 | * Verify the chanspec is using a legal set of parameters, i.e. that the | |
625 | * chanspec specified a band, bw, pri_sb and channel and that the | |
626 | * combination could be legal given any set of circumstances. | |
627 | * RETURNS: TRUE is the chanspec is malformed, false if it looks good. | |
628 | */ | |
629 | bool | |
630 | wf_chspec_malformed(chanspec_t chanspec) | |
631 | { | |
632 | uint chspec_bw = CHSPEC_BW(chanspec); | |
633 | uint chspec_ch = CHSPEC_CHANNEL(chanspec); | |
634 | ||
635 | /* must be 2G or 5G band */ | |
636 | if (CHSPEC_IS2G(chanspec)) { | |
637 | /* must be valid bandwidth */ | |
638 | if (!BW_LE40(chspec_bw)) { | |
639 | return TRUE; | |
640 | } | |
641 | } else if (CHSPEC_IS5G(chanspec)) { | |
642 | if (chspec_bw == WL_CHANSPEC_BW_8080) { | |
643 | uint ch1_id, ch2_id; | |
644 | ||
645 | /* channel IDs in 80+80 must be in range */ | |
646 | ch1_id = CHSPEC_CHAN1(chanspec); | |
647 | ch2_id = CHSPEC_CHAN2(chanspec); | |
648 | if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS) | |
649 | return TRUE; | |
650 | ||
651 | } else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 || | |
652 | chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) { | |
653 | ||
654 | if (chspec_ch > MAXCHANNEL) { | |
655 | return TRUE; | |
656 | } | |
657 | } else { | |
658 | /* invalid bandwidth */ | |
659 | return TRUE; | |
660 | } | |
661 | } else { | |
662 | /* must be 2G or 5G band */ | |
663 | return TRUE; | |
664 | } | |
665 | ||
666 | /* side band needs to be consistent with bandwidth */ | |
667 | if (chspec_bw == WL_CHANSPEC_BW_20) { | |
668 | if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL) | |
669 | return TRUE; | |
670 | } else if (chspec_bw == WL_CHANSPEC_BW_40) { | |
671 | if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU) | |
672 | return TRUE; | |
673 | } else if (chspec_bw == WL_CHANSPEC_BW_80 || | |
674 | chspec_bw == WL_CHANSPEC_BW_8080) { | |
675 | /* both 80MHz and 80+80MHz use 80MHz side bands. | |
676 | * 80+80 SB info is relative to the primary 80MHz sub-band. | |
677 | */ | |
678 | if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU) | |
679 | return TRUE; | |
680 | } | |
681 | else if (chspec_bw == WL_CHANSPEC_BW_160) { | |
682 | ASSERT(CHSPEC_CTL_SB(chanspec) <= WL_CHANSPEC_CTL_SB_UUU); | |
683 | } | |
684 | return FALSE; | |
685 | } | |
686 | ||
687 | /* | |
688 | * Verify the chanspec specifies a valid channel according to 802.11. | |
689 | * RETURNS: TRUE if the chanspec is a valid 802.11 channel | |
690 | */ | |
691 | bool | |
692 | wf_chspec_valid(chanspec_t chanspec) | |
693 | { | |
694 | uint chspec_bw = CHSPEC_BW(chanspec); | |
695 | uint chspec_ch = CHSPEC_CHANNEL(chanspec); | |
696 | ||
697 | if (wf_chspec_malformed(chanspec)) | |
698 | return FALSE; | |
699 | ||
700 | if (CHSPEC_IS2G(chanspec)) { | |
701 | /* must be valid bandwidth and channel range */ | |
702 | if (chspec_bw == WL_CHANSPEC_BW_20) { | |
703 | if (chspec_ch >= 1 && chspec_ch <= 14) | |
704 | return TRUE; | |
705 | } else if (chspec_bw == WL_CHANSPEC_BW_40) { | |
706 | if (chspec_ch >= 3 && chspec_ch <= 11) | |
707 | return TRUE; | |
708 | } | |
709 | } else if (CHSPEC_IS5G(chanspec)) { | |
710 | if (chspec_bw == WL_CHANSPEC_BW_8080) { | |
711 | uint16 ch1, ch2; | |
712 | ||
713 | ch1 = wf_5g_80m_chans[CHSPEC_CHAN1(chanspec)]; | |
714 | ch2 = wf_5g_80m_chans[CHSPEC_CHAN2(chanspec)]; | |
715 | ||
716 | /* the two channels must be separated by more than 80MHz by VHT req */ | |
717 | if ((ch2 > ch1 + CH_80MHZ_APART) || | |
718 | (ch1 > ch2 + CH_80MHZ_APART)) | |
719 | return TRUE; | |
720 | } else { | |
721 | const uint8 *center_ch; | |
722 | uint num_ch, i; | |
723 | ||
724 | if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40) { | |
725 | center_ch = wf_5g_40m_chans; | |
726 | num_ch = WF_NUM_5G_40M_CHANS; | |
727 | } else if (chspec_bw == WL_CHANSPEC_BW_80) { | |
728 | center_ch = wf_5g_80m_chans; | |
729 | num_ch = WF_NUM_5G_80M_CHANS; | |
730 | } else if (chspec_bw == WL_CHANSPEC_BW_160) { | |
731 | center_ch = wf_5g_160m_chans; | |
732 | num_ch = WF_NUM_5G_160M_CHANS; | |
733 | } else { | |
734 | /* invalid bandwidth */ | |
735 | return FALSE; | |
736 | } | |
737 | ||
738 | /* check for a valid center channel */ | |
739 | if (chspec_bw == WL_CHANSPEC_BW_20) { | |
740 | /* We don't have an array of legal 20MHz 5G channels, but they are | |
741 | * each side of the legal 40MHz channels. Check the chanspec | |
742 | * channel against either side of the 40MHz channels. | |
743 | */ | |
744 | for (i = 0; i < num_ch; i ++) { | |
745 | if (chspec_ch == (uint)LOWER_20_SB(center_ch[i]) || | |
746 | chspec_ch == (uint)UPPER_20_SB(center_ch[i])) | |
747 | break; /* match found */ | |
748 | } | |
749 | ||
750 | if (i == num_ch) { | |
751 | /* check for channel 165 which is not the side band | |
752 | * of 40MHz 5G channel | |
753 | */ | |
754 | if (chspec_ch == 165) | |
755 | i = 0; | |
756 | ||
757 | /* check for legacy JP channels on failure */ | |
758 | if (chspec_ch == 34 || chspec_ch == 38 || | |
759 | chspec_ch == 42 || chspec_ch == 46) | |
760 | i = 0; | |
761 | } | |
762 | } else { | |
763 | /* check the chanspec channel to each legal channel */ | |
764 | for (i = 0; i < num_ch; i ++) { | |
765 | if (chspec_ch == center_ch[i]) | |
766 | break; /* match found */ | |
767 | } | |
768 | } | |
769 | ||
770 | if (i < num_ch) { | |
771 | /* match found */ | |
772 | return TRUE; | |
773 | } | |
774 | } | |
775 | } | |
776 | ||
777 | return FALSE; | |
778 | } | |
779 | ||
780 | /* | |
781 | * This function returns TRUE if both the chanspec can co-exist in PHY. | |
782 | * Addition to primary20 channel, the function checks for side band for 2g 40 channels | |
783 | */ | |
784 | bool | |
785 | wf_chspec_coexist(chanspec_t chspec1, chanspec_t chspec2) | |
786 | { | |
787 | bool same_primary; | |
788 | ||
789 | same_primary = (wf_chspec_primary20_chan(chspec1) == wf_chspec_primary20_chan(chspec2)); | |
790 | ||
791 | if (same_primary && CHSPEC_IS2G(chspec1)) { | |
792 | if (CHSPEC_IS40(chspec1) && CHSPEC_IS40(chspec2)) { | |
793 | return (CHSPEC_CTL_SB(chspec1) == CHSPEC_CTL_SB(chspec2)); | |
794 | } | |
795 | } | |
796 | return same_primary; | |
797 | } | |
798 | ||
799 | /** | |
800 | * Create a 20MHz chanspec for the given band. | |
801 | * | |
802 | * This function returns a 20MHz chanspec in the given band. | |
803 | * | |
804 | * @param channel 20MHz channel number | |
805 | * @param band a chanspec band (e.g. WL_CHANSPEC_BAND_2G) | |
806 | * | |
807 | * @return Returns a 20MHz chanspec, or IVNCHANSPEC in case of error. | |
808 | */ | |
809 | chanspec_t | |
810 | wf_create_20MHz_chspec(uint channel, chanspec_band_t band) | |
811 | { | |
812 | chanspec_t chspec; | |
813 | ||
814 | if (channel <= WL_CHANSPEC_CHAN_MASK && | |
815 | (band == WL_CHANSPEC_BAND_2G || | |
816 | band == WL_CHANSPEC_BAND_5G)) { | |
817 | chspec = band | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE | channel; | |
818 | if (!wf_chspec_valid(chspec)) { | |
819 | chspec = INVCHANSPEC; | |
820 | } | |
821 | } else { | |
822 | chspec = INVCHANSPEC; | |
823 | } | |
824 | ||
825 | return chspec; | |
826 | } | |
827 | ||
828 | /** | |
829 | * Return the primary 20MHz channel. | |
830 | * | |
831 | * This function returns the channel number of the primary 20MHz channel. For | |
832 | * 20MHz channels this is just the channel number. For 40MHz or wider channels | |
833 | * it is the primary 20MHz channel specified by the chanspec. | |
834 | * | |
835 | * @param chspec input chanspec | |
836 | * | |
837 | * @return Returns the channel number of the primary 20MHz channel | |
838 | */ | |
839 | uint8 | |
840 | wf_chspec_primary20_chan(chanspec_t chspec) | |
841 | { | |
842 | uint center_chan; | |
843 | uint bw_mhz; | |
844 | uint sb; | |
845 | ||
846 | ASSERT(!wf_chspec_malformed(chspec)); | |
847 | ||
848 | /* Is there a sideband ? */ | |
849 | if (CHSPEC_IS20(chspec)) { | |
850 | return CHSPEC_CHANNEL(chspec); | |
851 | } else { | |
852 | sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT; | |
853 | ||
854 | if (CHSPEC_IS8080(chspec)) { | |
855 | /* For an 80+80 MHz channel, the sideband 'sb' field is an 80 MHz sideband | |
856 | * (LL, LU, UL, LU) for the 80 MHz frequency segment 0. | |
857 | */ | |
858 | uint chan_id = CHSPEC_CHAN1(chspec); | |
859 | ||
860 | bw_mhz = 80; | |
861 | ||
862 | /* convert from channel index to channel number */ | |
863 | center_chan = wf_5g_80m_chans[chan_id]; | |
864 | } | |
865 | else { | |
866 | bw_mhz = wf_bw_chspec_to_mhz(chspec); | |
867 | center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT; | |
868 | } | |
869 | ||
870 | return (channel_to_primary20_chan(center_chan, bw_mhz, sb)); | |
871 | } | |
872 | } | |
873 | ||
874 | /* given a chanspec, return the bandwidth string */ | |
875 | const char * | |
876 | BCMRAMFN(wf_chspec_to_bw_str)(chanspec_t chspec) | |
877 | { | |
878 | return wf_chspec_bw_str[(CHSPEC_BW(chspec) >> WL_CHANSPEC_BW_SHIFT)]; | |
879 | } | |
880 | ||
881 | /* | |
882 | * Return the primary 20MHz chanspec of the given chanspec | |
883 | */ | |
884 | chanspec_t | |
885 | wf_chspec_primary20_chspec(chanspec_t chspec) | |
886 | { | |
887 | chanspec_t pri_chspec = chspec; | |
888 | uint8 pri_chan; | |
889 | ||
890 | ASSERT(!wf_chspec_malformed(chspec)); | |
891 | ||
892 | /* Is there a sideband ? */ | |
893 | if (!CHSPEC_IS20(chspec)) { | |
894 | pri_chan = wf_chspec_primary20_chan(chspec); | |
895 | pri_chspec = pri_chan | WL_CHANSPEC_BW_20; | |
896 | pri_chspec |= CHSPEC_BAND(chspec); | |
897 | } | |
898 | return pri_chspec; | |
899 | } | |
900 | ||
901 | /* return chanspec given primary 20MHz channel and bandwidth | |
902 | * return 0 on error | |
903 | */ | |
904 | uint16 | |
905 | wf_channel2chspec(uint pri_ch, uint bw) | |
906 | { | |
907 | uint16 chspec; | |
908 | const uint8 *center_ch = NULL; | |
909 | int num_ch = 0; | |
910 | int sb = -1; | |
911 | int i = 0; | |
912 | ||
913 | chspec = ((pri_ch <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); | |
914 | ||
915 | chspec |= bw; | |
916 | ||
917 | if (bw == WL_CHANSPEC_BW_40) { | |
918 | center_ch = wf_5g_40m_chans; | |
919 | num_ch = WF_NUM_5G_40M_CHANS; | |
920 | bw = 40; | |
921 | } else if (bw == WL_CHANSPEC_BW_80) { | |
922 | center_ch = wf_5g_80m_chans; | |
923 | num_ch = WF_NUM_5G_80M_CHANS; | |
924 | bw = 80; | |
925 | } else if (bw == WL_CHANSPEC_BW_160) { | |
926 | center_ch = wf_5g_160m_chans; | |
927 | num_ch = WF_NUM_5G_160M_CHANS; | |
928 | bw = 160; | |
929 | } else if (bw == WL_CHANSPEC_BW_20) { | |
930 | chspec |= pri_ch; | |
931 | return chspec; | |
932 | } else { | |
933 | return 0; | |
934 | } | |
935 | ||
936 | for (i = 0; i < num_ch; i ++) { | |
937 | sb = channel_to_sb(center_ch[i], pri_ch, bw); | |
938 | if (sb >= 0) { | |
939 | chspec |= center_ch[i]; | |
940 | chspec |= (sb << WL_CHANSPEC_CTL_SB_SHIFT); | |
941 | break; | |
942 | } | |
943 | } | |
944 | ||
945 | /* check for no matching sb/center */ | |
946 | if (sb < 0) { | |
947 | return 0; | |
948 | } | |
949 | ||
950 | return chspec; | |
951 | } | |
952 | ||
953 | /* | |
954 | * This function returns the chanspec for the primary 40MHz of an 80MHz or wider channel. | |
955 | * The primary 20MHz channel of the returned 40MHz chanspec is the same as the primary 20MHz | |
956 | * channel of the input chanspec. | |
957 | */ | |
958 | extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec) | |
959 | { | |
960 | chanspec_t chspec40 = chspec; | |
961 | uint center_chan; | |
962 | uint sb; | |
963 | ||
964 | ASSERT(!wf_chspec_malformed(chspec)); | |
965 | ||
966 | /* if the chanspec is > 80MHz, use the helper routine to find the primary 80 MHz channel */ | |
967 | if (CHSPEC_IS8080(chspec) || CHSPEC_IS160(chspec)) { | |
968 | chspec = wf_chspec_primary80_chspec(chspec); | |
969 | } | |
970 | ||
971 | /* determine primary 40 MHz sub-channel of an 80 MHz chanspec */ | |
972 | if (CHSPEC_IS80(chspec)) { | |
973 | center_chan = CHSPEC_CHANNEL(chspec); | |
974 | sb = CHSPEC_CTL_SB(chspec); | |
975 | ||
976 | if (sb < WL_CHANSPEC_CTL_SB_UL) { | |
977 | /* Primary 40MHz is on lower side */ | |
978 | center_chan -= CH_20MHZ_APART; | |
979 | /* sideband bits are the same for LL/LU and L/U */ | |
980 | } else { | |
981 | /* Primary 40MHz is on upper side */ | |
982 | center_chan += CH_20MHZ_APART; | |
983 | /* sideband bits need to be adjusted by UL offset */ | |
984 | sb -= WL_CHANSPEC_CTL_SB_UL; | |
985 | } | |
986 | ||
987 | /* Create primary 40MHz chanspec */ | |
988 | chspec40 = (chanspec_t)(WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_40 | | |
989 | sb | center_chan); | |
990 | } | |
991 | ||
992 | return chspec40; | |
993 | } | |
994 | ||
995 | /* | |
996 | * Return the channel number for a given frequency and base frequency. | |
997 | * The returned channel number is relative to the given base frequency. | |
998 | * If the given base frequency is zero, a base frequency of 5 GHz is assumed for | |
999 | * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz. | |
1000 | * | |
1001 | * Frequency is specified in MHz. | |
1002 | * The base frequency is specified as (start_factor * 500 kHz). | |
1003 | * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for | |
1004 | * 2.4 GHz and 5 GHz bands. | |
1005 | * | |
1006 | * The returned channel will be in the range [1, 14] in the 2.4 GHz band | |
1007 | * and [0, 200] otherwise. | |
1008 | * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the | |
1009 | * frequency is not a 2.4 GHz channel, or if the frequency is not and even | |
1010 | * multiple of 5 MHz from the base frequency to the base plus 1 GHz. | |
1011 | * | |
1012 | * Reference 802.11-2016, section 17.3.8.3 and section 16.3.6.3 | |
1013 | */ | |
1014 | int | |
1015 | wf_mhz2channel(uint freq, uint start_factor) | |
1016 | { | |
1017 | int ch = -1; | |
1018 | uint base; | |
1019 | int offset; | |
1020 | ||
1021 | /* take the default channel start frequency */ | |
1022 | if (start_factor == 0) { | |
1023 | if (freq >= 2400 && freq <= 2500) | |
1024 | start_factor = WF_CHAN_FACTOR_2_4_G; | |
1025 | else if (freq >= 5000 && freq <= 6000) | |
1026 | start_factor = WF_CHAN_FACTOR_5_G; | |
1027 | } | |
1028 | ||
1029 | if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G) | |
1030 | return 14; | |
1031 | ||
1032 | base = start_factor / 2; | |
1033 | ||
1034 | /* check that the frequency is in 1GHz range of the base */ | |
1035 | if ((freq < base) || (freq > base + 1000)) | |
1036 | return -1; | |
1037 | ||
1038 | offset = (int)(freq - base); | |
1039 | ch = offset / 5; | |
1040 | ||
1041 | /* check that frequency is a 5MHz multiple from the base */ | |
1042 | if (offset != (ch * 5)) | |
1043 | return -1; | |
1044 | ||
1045 | /* restricted channel range check for 2.4G */ | |
1046 | if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13)) | |
1047 | return -1; | |
1048 | ||
1049 | return ch; | |
1050 | } | |
1051 | ||
1052 | /* | |
1053 | * Return the center frequency in MHz of the given channel and base frequency. | |
1054 | * The channel number is interpreted relative to the given base frequency. | |
1055 | * | |
1056 | * The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise. | |
1057 | * The base frequency is specified as (start_factor * 500 kHz). | |
1058 | * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_4_G, and WF_CHAN_FACTOR_5_G | |
1059 | * are defined for 2.4 GHz, 4 GHz, and 5 GHz bands. | |
1060 | * The channel range of [1, 14] is only checked for a start_factor of | |
1061 | * WF_CHAN_FACTOR_2_4_G (4814 = 2407 * 2). | |
1062 | * Odd start_factors produce channels on .5 MHz boundaries, in which case | |
1063 | * the answer is rounded down to an integral MHz. | |
1064 | * -1 is returned for an out of range channel. | |
1065 | * | |
1066 | * Reference 802.11-2016, section 17.3.8.3 and section 16.3.6.3 | |
1067 | */ | |
1068 | int | |
1069 | wf_channel2mhz(uint ch, uint start_factor) | |
1070 | { | |
1071 | int freq; | |
1072 | ||
1073 | if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) || | |
1074 | (ch > 200)) | |
1075 | freq = -1; | |
1076 | else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14)) | |
1077 | freq = 2484; | |
1078 | else | |
1079 | freq = (int)(ch * 5 + start_factor / 2); | |
1080 | ||
1081 | return freq; | |
1082 | } | |
1083 | ||
1084 | static const uint16 sidebands[] = { | |
1085 | WL_CHANSPEC_CTL_SB_LLL, WL_CHANSPEC_CTL_SB_LLU, | |
1086 | WL_CHANSPEC_CTL_SB_LUL, WL_CHANSPEC_CTL_SB_LUU, | |
1087 | WL_CHANSPEC_CTL_SB_ULL, WL_CHANSPEC_CTL_SB_ULU, | |
1088 | WL_CHANSPEC_CTL_SB_UUL, WL_CHANSPEC_CTL_SB_UUU | |
1089 | }; | |
1090 | ||
1091 | /* | |
1092 | * Returns the chanspec 80Mhz channel corresponding to the following input | |
1093 | * parameters | |
1094 | * | |
1095 | * primary_channel - primary 20Mhz channel | |
1096 | * center_channel - center frequecny of the 80Mhz channel | |
1097 | * | |
1098 | * The center_channel can be one of {42, 58, 106, 122, 138, 155} | |
1099 | * | |
1100 | * returns INVCHANSPEC in case of error | |
1101 | */ | |
1102 | chanspec_t | |
1103 | wf_chspec_80(uint8 center_channel, uint8 primary_channel) | |
1104 | { | |
1105 | ||
1106 | chanspec_t chanspec = INVCHANSPEC; | |
1107 | chanspec_t chanspec_cur; | |
1108 | uint i; | |
1109 | ||
1110 | for (i = 0; i < WF_NUM_SIDEBANDS_80MHZ; i++) { | |
1111 | chanspec_cur = CH80MHZ_CHSPEC(center_channel, sidebands[i]); | |
1112 | if (primary_channel == wf_chspec_primary20_chan(chanspec_cur)) { | |
1113 | chanspec = chanspec_cur; | |
1114 | break; | |
1115 | } | |
1116 | } | |
1117 | /* If the loop ended early, we are good, otherwise we did not | |
1118 | * find a 80MHz chanspec with the given center_channel that had a primary channel | |
1119 | *matching the given primary_channel. | |
1120 | */ | |
1121 | return chanspec; | |
1122 | } | |
1123 | ||
1124 | /* | |
1125 | * Returns the 80+80 chanspec corresponding to the following input parameters | |
1126 | * | |
1127 | * primary_20mhz - Primary 20 MHz channel | |
1128 | * chan0 - center channel number of one frequency segment | |
1129 | * chan1 - center channel number of the other frequency segment | |
1130 | * | |
1131 | * Parameters chan0 and chan1 are channel numbers in {42, 58, 106, 122, 138, 155}. | |
1132 | * The primary channel must be contained in one of the 80MHz channels. This routine | |
1133 | * will determine which frequency segment is the primary 80 MHz segment. | |
1134 | * | |
1135 | * Returns INVCHANSPEC in case of error. | |
1136 | * | |
1137 | * Refer to 802.11-2016 section 22.3.14 "Channelization". | |
1138 | */ | |
1139 | chanspec_t | |
1140 | wf_chspec_get8080_chspec(uint8 primary_20mhz, uint8 chan0, uint8 chan1) | |
1141 | { | |
1142 | int sb = 0; | |
1143 | uint16 chanspec = 0; | |
1144 | int chan0_id = 0, chan1_id = 0; | |
1145 | int seg0, seg1; | |
1146 | ||
1147 | chan0_id = channel_80mhz_to_id(chan0); | |
1148 | chan1_id = channel_80mhz_to_id(chan1); | |
1149 | ||
1150 | /* make sure the channel numbers were valid */ | |
1151 | if (chan0_id == -1 || chan1_id == -1) | |
1152 | return INVCHANSPEC; | |
1153 | ||
1154 | /* does the primary channel fit with the 1st 80MHz channel ? */ | |
1155 | sb = channel_to_sb(chan0, primary_20mhz, 80); | |
1156 | if (sb >= 0) { | |
1157 | /* yes, so chan0 is frequency segment 0, and chan1 is seg 1 */ | |
1158 | seg0 = chan0_id; | |
1159 | seg1 = chan1_id; | |
1160 | } else { | |
1161 | /* no, so does the primary channel fit with the 2nd 80MHz channel ? */ | |
1162 | sb = channel_to_sb(chan1, primary_20mhz, 80); | |
1163 | if (sb < 0) { | |
1164 | /* no match for pri_ch to either 80MHz center channel */ | |
1165 | return INVCHANSPEC; | |
1166 | } | |
1167 | /* swapped, so chan1 is frequency segment 0, and chan0 is seg 1 */ | |
1168 | seg0 = chan1_id; | |
1169 | seg1 = chan0_id; | |
1170 | } | |
1171 | ||
1172 | chanspec = (uint16)((seg0 << WL_CHANSPEC_CHAN1_SHIFT) | | |
1173 | (seg1 << WL_CHANSPEC_CHAN2_SHIFT) | | |
1174 | (sb << WL_CHANSPEC_CTL_SB_SHIFT) | | |
1175 | WL_CHANSPEC_BW_8080 | | |
1176 | WL_CHANSPEC_BAND_5G); | |
1177 | ||
1178 | return chanspec; | |
1179 | } | |
1180 | ||
1181 | /* | |
1182 | * This function returns the 80Mhz channel for the given id. | |
1183 | */ | |
1184 | static uint8 | |
1185 | wf_chspec_get80Mhz_ch(uint8 chan_80Mhz_id) | |
1186 | { | |
1187 | if (chan_80Mhz_id < WF_NUM_5G_80M_CHANS) | |
1188 | return wf_5g_80m_chans[chan_80Mhz_id]; | |
1189 | ||
1190 | return 0; | |
1191 | } | |
1192 | ||
1193 | /* | |
1194 | * Returns the center channel of the primary 80 MHz sub-band of the provided chanspec | |
1195 | */ | |
1196 | uint8 | |
1197 | wf_chspec_primary80_channel(chanspec_t chanspec) | |
1198 | { | |
1199 | chanspec_t primary80_chspec; | |
1200 | uint8 primary80_chan; | |
1201 | ||
1202 | primary80_chspec = wf_chspec_primary80_chspec(chanspec); | |
1203 | ||
1204 | if (primary80_chspec == INVCHANSPEC) { | |
1205 | primary80_chan = INVCHANNEL; | |
1206 | } else { | |
1207 | primary80_chan = CHSPEC_CHANNEL(primary80_chspec); | |
1208 | } | |
1209 | ||
1210 | return primary80_chan; | |
1211 | } | |
1212 | ||
1213 | /* | |
1214 | * Returns the center channel of the secondary 80 MHz sub-band of the provided chanspec | |
1215 | */ | |
1216 | uint8 | |
1217 | wf_chspec_secondary80_channel(chanspec_t chanspec) | |
1218 | { | |
1219 | chanspec_t secondary80_chspec; | |
1220 | uint8 secondary80_chan; | |
1221 | ||
1222 | secondary80_chspec = wf_chspec_secondary80_chspec(chanspec); | |
1223 | ||
1224 | if (secondary80_chspec == INVCHANSPEC) { | |
1225 | secondary80_chan = INVCHANNEL; | |
1226 | } else { | |
1227 | secondary80_chan = CHSPEC_CHANNEL(secondary80_chspec); | |
1228 | } | |
1229 | ||
1230 | return secondary80_chan; | |
1231 | } | |
1232 | ||
1233 | /* | |
1234 | * Returns the chanspec for the primary 80MHz sub-band of an 160MHz or 80+80 channel | |
1235 | */ | |
1236 | chanspec_t | |
1237 | wf_chspec_primary80_chspec(chanspec_t chspec) | |
1238 | { | |
1239 | chanspec_t chspec80; | |
1240 | uint center_chan; | |
1241 | uint sb; | |
1242 | ||
1243 | ASSERT(!wf_chspec_malformed(chspec)); | |
1244 | ||
1245 | if (CHSPEC_IS80(chspec)) { | |
1246 | chspec80 = chspec; | |
1247 | } | |
1248 | else if (CHSPEC_IS8080(chspec)) { | |
1249 | sb = CHSPEC_CTL_SB(chspec); | |
1250 | ||
1251 | /* primary sub-band is stored in seg0 */ | |
1252 | center_chan = wf_chspec_get80Mhz_ch(CHSPEC_CHAN1(chspec)); | |
1253 | ||
1254 | /* Create primary 80MHz chanspec */ | |
1255 | chspec80 = (chanspec_t)(WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_80 | sb | center_chan); | |
1256 | } | |
1257 | else if (CHSPEC_IS160(chspec)) { | |
1258 | center_chan = CHSPEC_CHANNEL(chspec); | |
1259 | sb = CHSPEC_CTL_SB(chspec); | |
1260 | ||
1261 | if (sb < WL_CHANSPEC_CTL_SB_ULL) { | |
1262 | /* Primary 80MHz is on lower side */ | |
1263 | center_chan -= CH_40MHZ_APART; | |
1264 | } | |
1265 | else { | |
1266 | /* Primary 80MHz is on upper side */ | |
1267 | center_chan += CH_40MHZ_APART; | |
1268 | sb -= WL_CHANSPEC_CTL_SB_ULL; | |
1269 | } | |
1270 | ||
1271 | /* Create primary 80MHz chanspec */ | |
1272 | chspec80 = (chanspec_t)(WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_80 | sb | center_chan); | |
1273 | } | |
1274 | else { | |
1275 | chspec80 = INVCHANSPEC; | |
1276 | } | |
1277 | ||
1278 | return chspec80; | |
1279 | } | |
1280 | ||
1281 | /* | |
1282 | * Returns the chanspec for the secondary 80MHz sub-band of an 160MHz or 80+80 channel | |
1283 | */ | |
1284 | chanspec_t | |
1285 | wf_chspec_secondary80_chspec(chanspec_t chspec) | |
1286 | { | |
1287 | chanspec_t chspec80; | |
1288 | uint center_chan; | |
1289 | ||
1290 | ASSERT(!wf_chspec_malformed(chspec)); | |
1291 | ||
1292 | if (CHSPEC_IS8080(chspec)) { | |
1293 | /* secondary sub-band is stored in seg1 */ | |
1294 | center_chan = wf_chspec_get80Mhz_ch(CHSPEC_CHAN2(chspec)); | |
1295 | ||
1296 | /* Create secondary 80MHz chanspec */ | |
1297 | chspec80 = (chanspec_t)(WL_CHANSPEC_BAND_5G | | |
1298 | WL_CHANSPEC_BW_80 | | |
1299 | WL_CHANSPEC_CTL_SB_LL | | |
1300 | center_chan); | |
1301 | } | |
1302 | else if (CHSPEC_IS160(chspec)) { | |
1303 | center_chan = CHSPEC_CHANNEL(chspec); | |
1304 | ||
1305 | if (CHSPEC_CTL_SB(chspec) < WL_CHANSPEC_CTL_SB_ULL) { | |
1306 | /* Primary 80MHz is on lower side */ | |
1307 | center_chan -= CH_40MHZ_APART; | |
1308 | } | |
1309 | else { | |
1310 | /* Primary 80MHz is on upper side */ | |
1311 | center_chan += CH_40MHZ_APART; | |
1312 | } | |
1313 | ||
1314 | /* Create secondary 80MHz chanspec */ | |
1315 | chspec80 = (chanspec_t)(WL_CHANSPEC_BAND_5G | | |
1316 | WL_CHANSPEC_BW_80 | | |
1317 | WL_CHANSPEC_CTL_SB_LL | | |
1318 | center_chan); | |
1319 | } | |
1320 | else { | |
1321 | chspec80 = INVCHANSPEC; | |
1322 | } | |
1323 | ||
1324 | return chspec80; | |
1325 | } | |
1326 | ||
1327 | /* | |
1328 | * For 160MHz or 80P80 chanspec, set ch[0]/ch[1] to be the low/high 80 Mhz channels | |
1329 | * | |
1330 | * For 20/40/80MHz chanspec, set ch[0] to be the center freq, and chan[1]=-1 | |
1331 | */ | |
1332 | void | |
1333 | wf_chspec_get_80p80_channels(chanspec_t chspec, uint8 *ch) | |
1334 | { | |
1335 | ||
1336 | if (CHSPEC_IS8080(chspec)) { | |
1337 | ch[0] = wf_chspec_get80Mhz_ch(CHSPEC_CHAN1(chspec)); | |
1338 | ch[1] = wf_chspec_get80Mhz_ch(CHSPEC_CHAN2(chspec)); | |
1339 | } | |
1340 | else if (CHSPEC_IS160(chspec)) { | |
1341 | uint8 center_chan = CHSPEC_CHANNEL(chspec); | |
1342 | ch[0] = center_chan - CH_40MHZ_APART; | |
1343 | ch[1] = center_chan + CH_40MHZ_APART; | |
1344 | } | |
1345 | else { | |
1346 | /* for 20, 40, and 80 Mhz */ | |
1347 | ch[0] = CHSPEC_CHANNEL(chspec); | |
1348 | ch[1] = 0xFFu; | |
1349 | } | |
1350 | return; | |
1351 | ||
1352 | } | |
1353 | ||
1354 | #ifdef WL11AC_80P80 | |
1355 | uint8 | |
1356 | wf_chspec_channel(chanspec_t chspec) | |
1357 | { | |
1358 | if (CHSPEC_IS8080(chspec)) { | |
1359 | return wf_chspec_primary80_channel(chspec); | |
1360 | } | |
1361 | else { | |
1362 | return ((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK)); | |
1363 | } | |
1364 | } | |
1365 | #endif /* WL11AC_80P80 */ | |
1366 | ||
1367 | /* This routine returns the chanspec for a given operating class and | |
1368 | * channel number | |
1369 | */ | |
1370 | chanspec_t | |
1371 | wf_channel_create_chspec_frm_opclass(uint8 opclass, uint8 channel) | |
1372 | { | |
1373 | chanspec_t chanspec = 0; | |
1374 | uint16 opclass_info = 0; | |
1375 | uint16 lookupindex = 0; | |
1376 | switch (opclass) { | |
1377 | case 115: | |
1378 | lookupindex = 1; | |
1379 | break; | |
1380 | case 124: | |
1381 | lookupindex = 3; | |
1382 | break; | |
1383 | case 125: | |
1384 | lookupindex = 5; | |
1385 | break; | |
1386 | case 81: | |
1387 | lookupindex = 12; | |
1388 | break; | |
1389 | case 116: | |
1390 | lookupindex = 22; | |
1391 | break; | |
1392 | case 119: | |
1393 | lookupindex = 23; | |
1394 | break; | |
1395 | case 126: | |
1396 | lookupindex = 25; | |
1397 | break; | |
1398 | case 83: | |
1399 | lookupindex = 32; | |
1400 | break; | |
1401 | case 84: | |
1402 | lookupindex = 33; | |
1403 | break; | |
1404 | default: | |
1405 | lookupindex = 12; | |
1406 | } | |
1407 | ||
1408 | if (lookupindex < 33) { | |
1409 | opclass_info = opclass_data[lookupindex-1]; | |
1410 | } | |
1411 | else { | |
1412 | opclass_info = opclass_data[11]; | |
1413 | } | |
1414 | chanspec = opclass_info | (uint16)channel; | |
1415 | return chanspec; | |
1416 | } | |
1417 | ||
1418 | /* This routine returns the opclass for a given chanspec */ | |
1419 | int | |
1420 | wf_channel_create_opclass_frm_chspec(chanspec_t chspec) | |
1421 | { | |
1422 | BCM_REFERENCE(chspec); | |
1423 | /* TODO: Implement this function ! */ | |
1424 | return 12; /* opclass 12 for basic 2G channels */ | |
1425 | } | |
1426 | ||
1427 | /* Populates array with all 20MHz side bands of a given chanspec_t in the following order: | |
1428 | * primary20, secondary20, two secondary40s, four secondary80s. | |
1429 | * 'chspec' is the chanspec of interest | |
1430 | * 'pext' must point to an uint8 array of long enough to hold all side bands of the given chspec | |
1431 | * | |
1432 | * Works with 20, 40, 80, 80p80 and 160MHz chspec | |
1433 | */ | |
1434 | void | |
1435 | wf_get_all_ext(chanspec_t chspec, uint8 *pext) | |
1436 | { | |
1437 | #ifdef WL11N_20MHZONLY | |
1438 | GET_ALL_SB(chspec, pext); | |
1439 | #else /* !WL11N_20MHZONLY */ | |
1440 | chanspec_t t = (CHSPEC_IS160(chspec) || CHSPEC_IS8080(chspec)) ? /* if bw > 80MHz */ | |
1441 | wf_chspec_primary80_chspec(chspec) : (chspec); /* extract primary 80 */ | |
1442 | /* primary20 channel as first element */ | |
1443 | uint8 pri_ch = (pext)[0] = wf_chspec_primary20_chan(t); | |
1444 | if (CHSPEC_IS20(chspec)) return; /* nothing more to do since 20MHz chspec */ | |
1445 | /* 20MHz EXT */ | |
1446 | (pext)[1] = pri_ch + (uint8)(IS_CTL_IN_L20(t) ? CH_20MHZ_APART : -CH_20MHZ_APART); | |
1447 | if (CHSPEC_IS40(chspec)) return; /* nothing more to do since 40MHz chspec */ | |
1448 | /* center 40MHz EXT */ | |
1449 | t = wf_channel2chspec((uint)(pri_ch + (IS_CTL_IN_L40(chspec) ? | |
1450 | CH_40MHZ_APART : -CH_40MHZ_APART)), WL_CHANSPEC_BW_40); | |
1451 | GET_ALL_SB(t, &((pext)[2])); /* get the 20MHz side bands in 40MHz EXT */ | |
1452 | if (CHSPEC_IS80(chspec)) return; /* nothing more to do since 80MHz chspec */ | |
1453 | t = CH80MHZ_CHSPEC(wf_chspec_secondary80_channel(chspec), WL_CHANSPEC_CTL_SB_LLL); | |
1454 | /* get the 20MHz side bands in 80MHz EXT (secondary) */ | |
1455 | GET_ALL_SB(t, &((pext)[4])); | |
1456 | #endif /* !WL11N_20MHZONLY */ | |
1457 | } | |
1458 | ||
1459 | /* | |
1460 | * Given two chanspecs, returns true if they overlap. | |
1461 | * (Overlap: At least one 20MHz subband is common between the two chanspecs provided) | |
1462 | */ | |
1463 | bool wf_chspec_overlap(chanspec_t chspec0, chanspec_t chspec1) | |
1464 | { | |
1465 | uint8 ch0, ch1; | |
1466 | ||
1467 | FOREACH_20_SB(chspec0, ch0) { | |
1468 | FOREACH_20_SB(chspec1, ch1) { | |
1469 | if (ABS(ch0 - ch1) < CH_20MHZ_APART) { | |
1470 | return TRUE; | |
1471 | } | |
1472 | } | |
1473 | } | |
1474 | ||
1475 | return FALSE; | |
1476 | } | |
1477 | ||
1478 | uint8 | |
1479 | channel_bw_to_width(chanspec_t chspec) | |
1480 | { | |
1481 | uint8 channel_width; | |
1482 | ||
1483 | if (CHSPEC_IS80(chspec)) | |
1484 | channel_width = VHT_OP_CHAN_WIDTH_80; | |
1485 | else if (CHSPEC_IS160(chspec)) | |
1486 | channel_width = VHT_OP_CHAN_WIDTH_160; | |
1487 | else if (CHSPEC_IS8080(chspec)) | |
1488 | channel_width = VHT_OP_CHAN_WIDTH_80_80; | |
1489 | else | |
1490 | channel_width = VHT_OP_CHAN_WIDTH_20_40; | |
1491 | ||
1492 | return channel_width; | |
1493 | } |