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