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) 2020, 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 *
23 * <<Broadcom-WL-IPTag/Dual:>>
24 */
25
26 #include <typedefs.h>
27 #include <bcmutils.h>
28
29 #ifdef BCMDRIVER
30 #include <osl.h>
31 #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
32 #define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
33 #else
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #ifndef ASSERT
39 #define ASSERT(exp)
40 #endif
41 #endif /* BCMDRIVER */
42
43 #include <bcmwifi_channels.h>
44
45 #if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL))
46 #include <bcmstdlib.h> /* For wlexe/Makefile.wlm_dll */
47 #endif
48
49 #include <802.11.h>
50
51 /* Definitions for D11AC capable (80MHz+) Chanspec type */
52
53 /* Chanspec ASCII representation:
54 *
55 * [<band>'g']<channel>['/'<bandwidth>[<primary-sideband>]
56 * ['/'<1st-channel-segment>'-'<2nd-channel-segment>]]
57 *
58 * <band>:
59 * (optional) 2, 4, 5, 6 for 2.4GHz, 4GHz, 5GHz, and 6GHz respectively.
60 * Default value is 2g if channel <= 14, otherwise 5g.
61 * <channel>:
62 * channel number of the 20MHz channel,
63 * or primary 20 MHz channel of 40MHz, 80MHz, 160MHz, 80+80MHz,
64 * 240MHz, 320MHz, or 160+160MHz channels.
65 * <bandwidth>:
66 * (optional) 20, 40, 80, 160, 80+80, 240, 320, or 160+160. Default value is 20.
67 * <primary-sideband>:
68 * 'u' or 'l' (only for 2.4GHz band 40MHz)
69 *
70 * For 2.4GHz band 40MHz channels, the same primary channel may be the
71 * upper sideband for one 40MHz channel, and the lower sideband for an
72 * overlapping 40MHz channel. The {u: upper, l: lower} primary sideband
73 * indication disambiguates which 40MHz channel is being specified.
74 *
75 * For 40MHz in the 5GHz or 6GHz band and all channel bandwidths greater than
76 * 40MHz, the U/L specification is not necessary or allowed since the channels are
77 * non-overlapping and the primary 20MHz channel position is derived from its
78 * position in the wide bandwidth channel.
79 * <1st-channel-segment>
80 * <2nd-channel-segment>:
81 * Required for 80+80 or 160+160, otherwise not allowed.
82 * These fields specify the center channel of the first and the second 80MHz
83 * or 160MHz channels.
84 *
85 * In its simplest form, it is a 20MHz channel number, with the implied band
86 * of 2.4GHz if channel number <= 14, and 5GHz otherwise.
87 *
88 * To allow for backward compatibility with scripts, the old form for
89 * 40MHz channels is also allowed: <channel><primary-sideband>
90 *
91 * <channel>:
92 * primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz
93 * <primary-sideband>:
94 * "U" for upper, "L" for lower (or lower case "u" "l")
95 *
96 * 5 GHz Examples:
97 * Chanspec BW Center Ch Channel Range Primary Ch
98 * 5g8 20MHz 8 - -
99 * 52 20MHz 52 - -
100 * 52/40 40MHz 54 52-56 52
101 * 56/40 40MHz 54 52-56 56
102 * 52/80 80MHz 58 52-64 52
103 * 56/80 80MHz 58 52-64 56
104 * 60/80 80MHz 58 52-64 60
105 * 64/80 80MHz 58 52-64 64
106 * 52/160 160MHz 50 36-64 52
107 * 36/160 160MGz 50 36-64 36
108 * 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36
109 *
110 * 2 GHz Examples:
111 * Chanspec BW Center Ch Channel Range Primary Ch
112 * 2g8 20MHz 8 - -
113 * 8 20MHz 8 - -
114 * 6 20MHz 6 - -
115 * 6/40l 40MHz 8 6-10 6
116 * 6l 40MHz 8 6-10 6
117 * 6/40u 40MHz 4 2-6 6
118 * 6u 40MHz 4 2-6 6
119 */
120
121 /* bandwidth ASCII string */
122 static const char *wf_chspec_bw_str[] =
123 {
124 "320",
125 "160+160",
126 "20",
127 "40",
128 "80",
129 "160",
130 "80+80",
131 "240"
132 };
133
134 static const uint16 wf_chspec_bw_mhz[] = {
135 320, 320, 20, 40, 80, 160, 160, 240
136 };
137 #define WF_NUM_BW ARRAYSIZE(wf_chspec_bw_mhz)
138
139 /* 40MHz channels in 2.4GHz band */
140 static const uint8 wf_2g_40m_chans[] = {
141 3, 4, 5, 6, 7, 8, 9, 10, 11
142 };
143 #define WF_NUM_2G_40M_CHANS ARRAYSIZE(wf_2g_40m_chans)
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 };
149 #define WF_NUM_5G_40M_CHANS ARRAYSIZE(wf_5g_40m_chans)
150
151 /* 80MHz channels in 5GHz band */
152 static const uint8 wf_5g_80m_chans[] = {
153 42, 58, 106, 122, 138, 155, 171
154 };
155 #define WF_NUM_5G_80M_CHANS ARRAYSIZE(wf_5g_80m_chans)
156
157 /* 160MHz channels in 5GHz band */
158 static const uint8 wf_5g_160m_chans[] = {
159 50, 114, 163
160 };
161 #define WF_NUM_5G_160M_CHANS ARRAYSIZE(wf_5g_160m_chans)
162
163 /** 80MHz channels in 6GHz band */
164 #define WF_NUM_6G_80M_CHANS 14
165
166 /** 160MHz channels in 6GHz band */
167 #define WF_NUM_6G_160M_CHANS 7 /* TBD */
168
169 /** 240MHz channels in 6GHz band */
170 #define WF_NUM_6G_240M_CHANS 4 /* TBD */
171
172 /** 320MHz channels in 6GHz band */
173 #define WF_NUM_6G_320M_CHANS 3 /* TBD */
174
175 /* Define the conditional macro to help with reducing the code size bloat
176 * in other branches and in trunk targets that don't need 11BE features...
177 */
178 #define WFC_2VALS_EQ(var, val) ((var) == (val))
179
180 /* compare bandwidth unconditionally for 11be related stuff */
181 #ifdef WL11BE
182 #define WFC_BW_EQ(bw, val) WFC_2VALS_EQ(bw, val)
183 #else
184 #define WFC_BW_EQ(bw, val) (FALSE)
185 #endif
186
187 static void wf_chanspec_iter_firstchan(wf_chanspec_iter_t *iter);
188 static chanspec_bw_t wf_iter_next_bw(chanspec_bw_t bw);
189 static bool wf_chanspec_iter_next_2g(wf_chanspec_iter_t *iter);
190 static bool wf_chanspec_iter_next_5g(wf_chanspec_iter_t *iter);
191 static int wf_chanspec_iter_next_5g_range(wf_chanspec_iter_t *iter, chanspec_bw_t bw);
192 static void wf_chanspec_iter_6g_range_init(wf_chanspec_iter_t *iter, chanspec_bw_t bw);
193 static bool wf_chanspec_iter_next_6g(wf_chanspec_iter_t *iter);
194
195 /**
196 * Return the chanspec bandwidth in MHz
197 * Bandwidth of 160 MHz will be returned for 80+80MHz chanspecs.
198 *
199 * @param chspec chanspec_t
200 *
201 * @return bandwidth of chspec in MHz units
202 */
203 uint
wf_bw_chspec_to_mhz(chanspec_t chspec)204 wf_bw_chspec_to_mhz(chanspec_t chspec)
205 {
206 uint bw;
207
208 bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT;
209 return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]);
210 }
211
212 /* bw in MHz, return the channel count from the center channel to the
213 * the channel at the edge of the band
214 */
215 static uint
center_chan_to_edge(chanspec_bw_t bw)216 center_chan_to_edge(chanspec_bw_t bw)
217 {
218 uint delta = 0;
219
220 /* edge channels separated by BW - 10MHz on each side
221 * delta from cf to edge is half of that,
222 */
223 if (bw == WL_CHANSPEC_BW_40) {
224 /* 10 MHz */
225 delta = 2;
226 } else if (bw == WL_CHANSPEC_BW_80) {
227 /* 30 MHz */
228 delta = 6;
229 } else if (bw == WL_CHANSPEC_BW_160) {
230 /* 70 MHz */
231 delta = 14;
232 } else if (WFC_BW_EQ(bw, WL_CHANSPEC_BW_240)) {
233 /* 110 MHz */
234 delta = 22;
235 } else if (WFC_BW_EQ(bw, WL_CHANSPEC_BW_320)) {
236 /* 150 MHz */
237 delta = 30;
238 }
239 return delta;
240 }
241
242 /* return channel number of the low edge of the band
243 * given the center channel and BW
244 */
245 static uint
channel_low_edge(uint center_ch,chanspec_bw_t bw)246 channel_low_edge(uint center_ch, chanspec_bw_t bw)
247 {
248 return (center_ch - center_chan_to_edge(bw));
249 }
250
251 /* return side band number given center channel and primary20 channel
252 * return -1 on error
253 */
254 static int
channel_to_sb(uint center_ch,uint primary_ch,chanspec_bw_t bw)255 channel_to_sb(uint center_ch, uint primary_ch, chanspec_bw_t bw)
256 {
257 uint lowest = channel_low_edge(center_ch, bw);
258 uint sb;
259
260 if (primary_ch < lowest ||
261 (primary_ch - lowest) % 4) {
262 /* bad primary channel lower than the low edge of the channel,
263 * or not mult 4.
264 */
265 return -1;
266 }
267
268 sb = ((primary_ch - lowest) / 4);
269
270 /* sb must be a index to a 20MHz channel in range */
271 if ((bw == WL_CHANSPEC_BW_20 && sb >= 1) ||
272 (bw == WL_CHANSPEC_BW_40 && sb >= 2) ||
273 (bw == WL_CHANSPEC_BW_80 && sb >= 4) ||
274 (bw == WL_CHANSPEC_BW_160 && sb >= 8) ||
275 (WFC_BW_EQ(bw, WL_CHANSPEC_BW_240) && sb >= 12) ||
276 (WFC_BW_EQ(bw, WL_CHANSPEC_BW_320) && sb >= 16)) {
277 /* primary_ch must have been too high for the center_ch */
278 return -1;
279 }
280
281 return sb;
282 }
283
284 /* return primary20 channel given center channel and side band */
285 static uint
channel_to_primary20_chan(uint center_ch,chanspec_bw_t bw,uint sb)286 channel_to_primary20_chan(uint center_ch, chanspec_bw_t bw, uint sb)
287 {
288 return (channel_low_edge(center_ch, bw) + sb * 4);
289 }
290
291 /* return index of 80MHz channel from channel number
292 * return -1 on error
293 */
294 static int
channel_80mhz_to_id(uint ch)295 channel_80mhz_to_id(uint ch)
296 {
297 uint i;
298 for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) {
299 if (ch == wf_5g_80m_chans[i])
300 return i;
301 }
302
303 return -1;
304 }
305
306 /* return index of the 6G 80MHz channel from channel number
307 * return -1 on error
308 */
309 static int
channel_6g_80mhz_to_id(uint ch)310 channel_6g_80mhz_to_id(uint ch)
311 {
312 /* The 6GHz center channels start at 7, and have a spacing of 16 */
313 if (ch >= CH_MIN_6G_80M_CHANNEL &&
314 ch <= CH_MAX_6G_80M_CHANNEL &&
315 ((ch - CH_MIN_6G_80M_CHANNEL) % 16) == 0) { // even multiple of 16
316 return (ch - CH_MIN_6G_80M_CHANNEL) / 16;
317 }
318
319 return -1;
320 }
321
322 /* return index of the 5G 160MHz channel from channel number
323 * return -1 on error
324 */
325 static int
channel_5g_160mhz_to_id(uint ch)326 channel_5g_160mhz_to_id(uint ch)
327 {
328 uint i;
329 for (i = 0; i < WF_NUM_5G_160M_CHANS; i ++) {
330 if (ch == wf_5g_160m_chans[i]) {
331 return i;
332 }
333 }
334
335 return -1;
336 }
337
338 /* return index of the 6G 160MHz channel from channel number
339 * return -1 on error
340 */
341 static int
channel_6g_160mhz_to_id(uint ch)342 channel_6g_160mhz_to_id(uint ch)
343 {
344 /* The 6GHz center channels start at 15, and have a spacing of 32 */
345 if (ch >= CH_MIN_6G_160M_CHANNEL &&
346 ch <= CH_MAX_6G_160M_CHANNEL &&
347 ((ch - CH_MIN_6G_160M_CHANNEL) % 32) == 0) {
348 return (ch - CH_MIN_6G_160M_CHANNEL) / 32;
349 }
350
351 return -1;
352 }
353
354 /* return index of the 6G 240MHz channel from channel number
355 * return -1 on error
356 */
357 static int
channel_6g_240mhz_to_id(uint ch)358 channel_6g_240mhz_to_id(uint ch)
359 {
360 /* The 6GHz center channels start at 23, and have a spacing of 48 */
361 if (ch >= CH_MIN_6G_240M_CHANNEL &&
362 ch <= CH_MAX_6G_240M_CHANNEL &&
363 ((ch - CH_MIN_6G_240M_CHANNEL) % 48) == 0) {
364 return (ch - CH_MIN_6G_240M_CHANNEL) / 48;
365 }
366
367 return -1;
368 }
369
370 /* return index of the 6G 320MHz channel from channel number
371 * return -1 on error
372 */
373 static int
channel_6g_320mhz_to_id(uint ch)374 channel_6g_320mhz_to_id(uint ch)
375 {
376 /* The 6GHz center channels start at 31, and have a spacing of 64 */
377 if (ch >= CH_MIN_6G_320M_CHANNEL &&
378 ch <= CH_MAX_6G_320M_CHANNEL &&
379 ((ch - CH_MIN_6G_320M_CHANNEL) % 64) == 0) {
380 return (ch - CH_MIN_6G_320M_CHANNEL) / 64;
381 }
382
383 return -1;
384 }
385
386 /**
387 * This function returns the the 6GHz 240MHz center channel for the given chanspec 240MHz ID
388 *
389 * @param chan_240MHz_id 240MHz chanspec ID
390 *
391 * @return Return the center channel number, or 0 on error.
392 *
393 */
394 static uint8
wf_chspec_6G_id240_to_ch(uint8 chan_240MHz_id)395 wf_chspec_6G_id240_to_ch(uint8 chan_240MHz_id)
396 {
397 uint8 ch = 0;
398
399 if (chan_240MHz_id < WF_NUM_6G_240M_CHANS) {
400 /* The 6GHz center channels have a spacing of 48
401 * starting from the first 240MHz center
402 */
403 ch = CH_MIN_6G_240M_CHANNEL + (chan_240MHz_id * 48);
404 }
405
406 return ch;
407 }
408
409 /* Retrive the chan_id and convert it to center channel */
410 uint8
wf_chspec_240_id2cch(chanspec_t chanspec)411 wf_chspec_240_id2cch(chanspec_t chanspec)
412 {
413 if (CHSPEC_BAND(chanspec) == WL_CHANSPEC_BAND_6G &&
414 CHSPEC_BW(chanspec) == WL_CHANSPEC_BW_240) {
415 uint8 ch_id = CHSPEC_GE240_CHAN(chanspec);
416
417 return wf_chspec_6G_id240_to_ch(ch_id);
418 }
419 return 0;
420 }
421
422 /**
423 * This function returns the the 6GHz 320MHz center channel for the given chanspec 320MHz ID
424 *
425 * @param chan_320MHz_id 320MHz chanspec ID
426 *
427 * @return Return the center channel number, or 0 on error.
428 *
429 */
430 static uint8
wf_chspec_6G_id320_to_ch(uint8 chan_320MHz_id)431 wf_chspec_6G_id320_to_ch(uint8 chan_320MHz_id)
432 {
433 uint8 ch = 0;
434
435 if (chan_320MHz_id < WF_NUM_6G_320M_CHANS) {
436 /* The 6GHz center channels have a spacing of 64
437 * starting from the first 320MHz center
438 */
439 ch = CH_MIN_6G_320M_CHANNEL + (chan_320MHz_id * 64);
440 }
441
442 return ch;
443 }
444
445 /* Retrive the chan_id and convert it to center channel */
446 uint8
wf_chspec_320_id2cch(chanspec_t chanspec)447 wf_chspec_320_id2cch(chanspec_t chanspec)
448 {
449 if (CHSPEC_BAND(chanspec) == WL_CHANSPEC_BAND_6G &&
450 CHSPEC_BW(chanspec) == WL_CHANSPEC_BW_320) {
451 uint8 ch_id = CHSPEC_GE240_CHAN(chanspec);
452
453 return wf_chspec_6G_id320_to_ch(ch_id);
454 }
455 return 0;
456 }
457
458 /**
459 * Convert chanspec to ascii string, or formats hex of an invalid chanspec.
460 *
461 * @param chspec chanspec to format
462 * @param buf pointer to buf with room for at least CHANSPEC_STR_LEN bytes
463 *
464 * @return Returns pointer to passed in buf. The buffer will have the ascii
465 * representation of the given chspec, or "invalid 0xHHHH" where
466 * 0xHHHH is the hex representation of the invalid chanspec.
467 *
468 * @see CHANSPEC_STR_LEN
469 *
470 * Wrapper function for wf_chspec_ntoa. In case of an error it puts
471 * the original chanspec in the output buffer, prepended with "invalid".
472 * Can be directly used in print routines as it takes care of null
473 */
474 char *
wf_chspec_ntoa_ex(chanspec_t chspec,char * buf)475 wf_chspec_ntoa_ex(chanspec_t chspec, char *buf)
476 {
477 if (wf_chspec_ntoa(chspec, buf) == NULL)
478 snprintf(buf, CHANSPEC_STR_LEN, "invalid 0x%04x", chspec);
479 return buf;
480 }
481
482 /**
483 * Convert chanspec to ascii string, or return NULL on error.
484 *
485 * @param chspec chanspec to format
486 * @param buf pointer to buf with room for at least CHANSPEC_STR_LEN bytes
487 *
488 * @return Returns pointer to passed in buf or NULL on error. On sucess, the buffer
489 * will have the ascii representation of the given chspec.
490 *
491 * @see CHANSPEC_STR_LEN
492 *
493 * Given a chanspec and a string buffer, format the chanspec as a
494 * string, and return the original pointer buf.
495 * Min buffer length must be CHANSPEC_STR_LEN.
496 * On error return NULL.
497 */
498 char *
wf_chspec_ntoa(chanspec_t chspec,char * buf)499 wf_chspec_ntoa(chanspec_t chspec, char *buf)
500 {
501 const char *band;
502 uint pri_chan;
503
504 if (wf_chspec_malformed(chspec))
505 return NULL;
506
507 band = "";
508
509 /* check for non-default band spec */
510 if (CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) {
511 band = "2g";
512 } else if (CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL) {
513 band = "5g";
514 } else if (CHSPEC_IS6G(chspec)) {
515 band = "6g";
516 }
517
518 /* primary20 channel */
519 pri_chan = wf_chspec_primary20_chan(chspec);
520
521 /* bandwidth and primary20 sideband */
522 if (CHSPEC_IS20(chspec)) {
523 snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, pri_chan);
524 } else if (CHSPEC_IS240(chspec)) {
525 /* 240 */
526 const char *bw;
527
528 bw = wf_chspec_to_bw_str(chspec);
529
530 snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, pri_chan, bw);
531 } else if (CHSPEC_IS320(chspec)) {
532 /* 320 */
533 const char *bw;
534
535 bw = wf_chspec_to_bw_str(chspec);
536
537 snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, pri_chan, bw);
538 } else {
539 const char *bw;
540 const char *sb = "";
541
542 bw = wf_chspec_to_bw_str(chspec);
543
544 #ifdef CHANSPEC_NEW_40MHZ_FORMAT
545 /* primary20 sideband string if needed for 2g 40MHz */
546 if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) {
547 sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
548 }
549
550 snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, pri_chan, bw, sb);
551 #else
552 /* primary20 sideband string instead of BW for 40MHz */
553 if (CHSPEC_IS40(chspec) && !CHSPEC_IS6G(chspec)) {
554 sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
555 snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, pri_chan, sb);
556 } else {
557 snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, pri_chan, bw);
558 }
559 #endif /* CHANSPEC_NEW_40MHZ_FORMAT */
560 }
561
562 return (buf);
563 }
564
565 static int
read_uint(const char ** p,unsigned int * num)566 read_uint(const char **p, unsigned int *num)
567 {
568 unsigned long val;
569 char *endp = NULL;
570
571 val = strtoul(*p, &endp, 10);
572 /* if endp is the initial pointer value, then a number was not read */
573 if (endp == *p)
574 return 0;
575
576 /* advance the buffer pointer to the end of the integer string */
577 *p = endp;
578 /* return the parsed integer */
579 *num = (unsigned int)val;
580
581 return 1;
582 }
583
584 /**
585 * Convert ascii string to chanspec
586 *
587 * @param a pointer to input string
588 *
589 * @return Return > 0 if successful or 0 otherwise
590 */
591 chanspec_t
wf_chspec_aton(const char * a)592 wf_chspec_aton(const char *a)
593 {
594 chanspec_t chspec;
595 chanspec_band_t chspec_band;
596 chanspec_subband_t chspec_sb;
597 chanspec_bw_t chspec_bw;
598 uint bw;
599 uint num, pri_ch;
600 char c, sb_ul = '\0';
601
602 bw = 20;
603 chspec_sb = 0;
604
605 /* parse channel num or band */
606 if (!read_uint(&a, &num))
607 return 0;
608 /* if we are looking at a 'g', then the first number was a band */
609 c = tolower((int)a[0]);
610 if (c == 'g') {
611 a++; /* consume the char */
612
613 /* band must be "2", "5", or "6" */
614 if (num == 2)
615 chspec_band = WL_CHANSPEC_BAND_2G;
616 else if (num == 5)
617 chspec_band = WL_CHANSPEC_BAND_5G;
618 else if (num == 6)
619 chspec_band = WL_CHANSPEC_BAND_6G;
620 else
621 return 0;
622
623 /* read the channel number */
624 if (!read_uint(&a, &pri_ch))
625 return 0;
626
627 c = tolower((int)a[0]);
628 } else {
629 /* first number is channel, use default for band */
630 pri_ch = num;
631 chspec_band = ((pri_ch <= CH_MAX_2G_CHANNEL) ?
632 WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
633 }
634
635 if (c == '\0') {
636 /* default BW of 20MHz */
637 chspec_bw = WL_CHANSPEC_BW_20;
638 goto done_read;
639 }
640
641 a ++; /* consume the 'u','l', or '/' */
642
643 /* check 'u'/'l' */
644 if (c == 'u' || c == 'l') {
645 sb_ul = c;
646 chspec_bw = WL_CHANSPEC_BW_40;
647 goto done_read;
648 }
649
650 /* next letter must be '/' */
651 if (c != '/')
652 return 0;
653
654 /* read bandwidth */
655 if (!read_uint(&a, &bw))
656 return 0;
657
658 /* convert to chspec value */
659 if (bw == 20) {
660 chspec_bw = WL_CHANSPEC_BW_20;
661 } else if (bw == 40) {
662 chspec_bw = WL_CHANSPEC_BW_40;
663 } else if (bw == 80) {
664 chspec_bw = WL_CHANSPEC_BW_80;
665 } else if (bw == 160) {
666 chspec_bw = WL_CHANSPEC_BW_160;
667 } else if (WFC_BW_EQ(bw, 240)) {
668 chspec_bw = WL_CHANSPEC_BW_240;
669 } else if (WFC_BW_EQ(bw, 320)) {
670 chspec_bw = WL_CHANSPEC_BW_320;
671 } else {
672 return 0;
673 }
674
675 /* So far we have <band>g<chan>/<bw>
676 * Can now be followed by u/l if bw = 40,
677 */
678
679 c = tolower((int)a[0]);
680
681 /* if we have a 2g/40 channel, we should have a l/u spec now */
682 if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) {
683 if (c == 'u' || c == 'l') {
684 a ++; /* consume the u/l char */
685 sb_ul = c;
686 goto done_read;
687 }
688 }
689
690 /* check for 80+80 or 160+160 */
691 if (c == '+') {
692 return 0;
693 }
694
695 done_read:
696 /* skip trailing white space */
697 while (a[0] == ' ') {
698 a ++;
699 }
700
701 /* must be end of string */
702 if (a[0] != '\0')
703 return 0;
704
705 /* Now have all the chanspec string parts read;
706 * chspec_band, pri_ch, chspec_bw, sb_ul.
707 * chspec_band and chspec_bw are chanspec values.
708 * Need to convert pri_ch, and sb_ul into
709 * a center channel (or two) and sideband.
710 */
711
712 /* if a sb u/l string was given, just use that,
713 * guaranteed to be bw = 40 by string parse.
714 */
715 if (sb_ul != '\0') {
716 if (sb_ul == 'l') {
717 chspec_sb = WL_CHANSPEC_CTL_SB_LLL;
718 } else if (sb_ul == 'u') {
719 chspec_sb = WL_CHANSPEC_CTL_SB_LLU;
720 }
721 chspec = wf_create_40MHz_chspec_primary_sb(pri_ch, chspec_sb, chspec_band);
722 } else if (chspec_bw == WL_CHANSPEC_BW_20) {
723 /* if the bw is 20, only need the primary channel and band */
724 chspec = wf_create_20MHz_chspec(pri_ch, chspec_band);
725 } else {
726 /* If the bw is 40/80/160/240/320 (and not 40MHz 2G), the channels are
727 * non-overlapping in 5G or 6G bands. Each primary channel is contained
728 * in only one higher bandwidth channel. The wf_create_chspec_from_primary()
729 * will create the chanspec. 2G 40MHz is handled just above, assuming a {u,l}
730 * sub-band spec was given.
731 */
732 chspec = wf_create_chspec_from_primary(pri_ch, chspec_bw, chspec_band);
733 }
734
735 if (wf_chspec_malformed(chspec))
736 return 0;
737
738 return chspec;
739 }
740
741 /**
742 * Verify the chanspec is using a legal set of parameters, i.e. that the
743 * chanspec specified a band, bw, pri_sb and channel and that the
744 * combination could be legal given any set of circumstances.
745 *
746 * @param chanspec the chanspec to check
747 *
748 * @return Returns TRUE if the chanspec is malformed, FALSE if it looks good.
749 */
750 bool
751 #ifdef BCMPOSTTRAPFN
BCMPOSTTRAPFN(wf_chspec_malformed)752 BCMPOSTTRAPFN(wf_chspec_malformed)(chanspec_t chanspec)
753 #else
754 wf_chspec_malformed(chanspec_t chanspec)
755 #endif
756 {
757 uint chspec_bw = CHSPEC_BW(chanspec);
758 uint chspec_sb;
759
760 if (CHSPEC_IS2G(chanspec)) {
761 /* must be valid bandwidth for 2G */
762 if (!BW_LE40(chspec_bw)) {
763 return TRUE;
764 }
765
766 /* check for invalid channel number */
767 if (CHSPEC_CHANNEL(chanspec) == INVCHANNEL) {
768 return TRUE;
769 }
770 } else if (CHSPEC_IS5G(chanspec) || CHSPEC_IS6G(chanspec)) {
771 if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_240)) {
772 uint ch_id;
773
774 ch_id = CHSPEC_GE240_CHAN(chanspec);
775
776 /* channel IDs in 240 must be in range */
777 if (CHSPEC_IS6G(chanspec)) {
778 if (ch_id >= WF_NUM_6G_240M_CHANS) {
779 /* bad 240MHz channel ID for the band */
780 return TRUE;
781 }
782 } else {
783 return TRUE;
784 }
785 } else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
786 uint ch_id;
787
788 ch_id = CHSPEC_GE240_CHAN(chanspec);
789
790 /* channel IDs in 320 must be in range */
791 if (CHSPEC_IS6G(chanspec)) {
792 if (ch_id >= WF_NUM_6G_320M_CHANS) {
793 /* bad 320MHz channel ID for the band */
794 return TRUE;
795 }
796 } else {
797 return TRUE;
798 }
799 } else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 ||
800 chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) {
801
802 /* check for invalid channel number */
803 if (CHSPEC_CHANNEL(chanspec) == INVCHANNEL) {
804 return TRUE;
805 }
806 } else {
807 /* invalid bandwidth */
808 return TRUE;
809 }
810 } else {
811 /* must be a valid band */
812 return TRUE;
813 }
814
815 /* retrive sideband */
816 if ((WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_240)) ||
817 (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320))) {
818 chspec_sb = CHSPEC_GE240_SB(chanspec);
819 } else {
820 chspec_sb = CHSPEC_CTL_SB(chanspec);
821 }
822
823 /* side band needs to be consistent with bandwidth */
824 if (chspec_bw == WL_CHANSPEC_BW_20) {
825 if (chspec_sb != WL_CHANSPEC_CTL_SB_LLL)
826 return TRUE;
827 } else if (chspec_bw == WL_CHANSPEC_BW_40) {
828 if (chspec_sb > WL_CHANSPEC_CTL_SB_LLU)
829 return TRUE;
830 } else if (chspec_bw == WL_CHANSPEC_BW_80) {
831 /* both 80MHz and 80+80MHz use 80MHz side bands.
832 * 80+80 SB info is relative to the primary 80MHz sub-band.
833 */
834 if (chspec_sb > WL_CHANSPEC_CTL_SB_LUU)
835 return TRUE;
836 } else if (chspec_bw == WL_CHANSPEC_BW_160) {
837 ASSERT(chspec_sb <= WL_CHANSPEC_CTL_SB_UUU);
838 } else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_240)) {
839 /* FIXME: define the max sideband index */
840 ASSERT((chspec_sb >> WL_CHANSPEC_GE240_SB_SHIFT) <= 11);
841 } else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
842 /* FIXME: define the max sideband index */
843 ASSERT((chspec_sb >> WL_CHANSPEC_GE240_SB_SHIFT) <= 15);
844 }
845
846 return FALSE;
847 }
848
849 /**
850 * Verify the chanspec specifies a valid channel according to 802.11.
851 *
852 * @param chanspec the chanspec to check
853 *
854 * @return Returns TRUE if the chanspec is a valid 802.11 channel
855 */
856 bool
wf_chspec_valid(chanspec_t chanspec)857 wf_chspec_valid(chanspec_t chanspec)
858 {
859 chanspec_band_t chspec_band = CHSPEC_BAND(chanspec);
860 chanspec_bw_t chspec_bw = CHSPEC_BW(chanspec);
861 uint chspec_ch = -1;
862
863 if (wf_chspec_malformed(chanspec)) {
864 return FALSE;
865 }
866
867 if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_240)) {
868 if (CHSPEC_IS6G(chanspec)) {
869 chspec_ch = wf_chspec_6G_id240_to_ch(CHSPEC_GE240_CHAN(chanspec));
870 } else {
871 return FALSE;
872 }
873 } else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
874 if (CHSPEC_IS6G(chanspec)) {
875 chspec_ch = wf_chspec_6G_id320_to_ch(CHSPEC_GE240_CHAN(chanspec));
876 } else {
877 return FALSE;
878 }
879 } else {
880 chspec_ch = CHSPEC_CHANNEL(chanspec);
881 }
882
883 /* After the malformed check, we know that we have
884 * a valid band field,
885 * a valid bandwidth for the band,
886 * and a valid sub-band value for the bandwidth.
887 *
888 * Since all sub-band specs are valid for any channel, the only thing remaining to
889 * check is that
890 * the 20MHz channel,
891 * or the center channel for higher BW,
892 * or both center channels for an 80+80MHz channel,
893 * are valid for the specified band.
894 * Also, 80+80MHz channels need to be non-contiguous.
895 */
896
897 if (chspec_bw == WL_CHANSPEC_BW_20) {
898
899 return wf_valid_20MHz_chan(chspec_ch, chspec_band);
900
901 } else if (chspec_bw == WL_CHANSPEC_BW_40) {
902
903 return wf_valid_40MHz_center_chan(chspec_ch, chspec_band);
904
905 } else if (chspec_bw == WL_CHANSPEC_BW_80) {
906
907 return wf_valid_80MHz_center_chan(chspec_ch, chspec_band);
908
909 } else if (chspec_bw == WL_CHANSPEC_BW_160) {
910
911 return wf_valid_160MHz_center_chan(chspec_ch, chspec_band);
912
913 } else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_240)) {
914
915 return wf_valid_240MHz_center_chan(chspec_ch, chspec_band);
916
917 } else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
918
919 return wf_valid_320MHz_center_chan(chspec_ch, chspec_band);
920
921 }
922
923 return FALSE;
924 }
925
926 /* 5G band 20MHz channel ranges with even (+4) channel spacing */
927 static const struct wf_iter_range wf_5g_iter_ranges[] = {
928 {36, 64},
929 {100, 144},
930 {149, 165}
931 };
932
933 #define RANGE_ID_INVAL 0xFFu
934 enum wf_iter_state {
935 WF_ITER_INIT = 0,
936 WF_ITER_RUN = 1,
937 WF_ITER_DONE = 2
938 };
939
940 /**
941 * @brief Initialize a chanspec iteration structure.
942 */
943 bool
wf_chanspec_iter_init(wf_chanspec_iter_t * iter,chanspec_band_t band,chanspec_bw_t bw)944 wf_chanspec_iter_init(wf_chanspec_iter_t *iter, chanspec_band_t band, chanspec_bw_t bw)
945 {
946 if (iter == NULL) {
947 return FALSE;
948 }
949
950 /* Initialize the iter structure to the "DONE" state
951 * in case the parameter validation fails.
952 * If the validation fails then the iterator will return INVCHANSPEC as the current
953 * chanspec, and wf_chanspec_iter_next() will return FALSE.
954 */
955 memset(iter, 0, sizeof(*iter));
956 iter->state = WF_ITER_DONE;
957 iter->chanspec = INVCHANSPEC;
958
959 if (band != WL_CHANSPEC_BAND_2G &&
960 band != WL_CHANSPEC_BAND_5G &&
961 band != WL_CHANSPEC_BAND_6G) {
962 ASSERT(0);
963 return FALSE;
964 }
965
966 /* make sure the BW is unspecified (INVCHANSPEC), 20/40,
967 * or (not 2g and 80/160)
968 */
969 if (!(bw == INVCHANSPEC ||
970 bw == WL_CHANSPEC_BW_20 ||
971 bw == WL_CHANSPEC_BW_40 ||
972 (band != WL_CHANSPEC_BAND_2G &&
973 (bw == WL_CHANSPEC_BW_80 ||
974 bw == WL_CHANSPEC_BW_160 ||
975 WFC_BW_EQ(bw, WL_CHANSPEC_BW_240) ||
976 WFC_BW_EQ(bw, WL_CHANSPEC_BW_320))))) {
977
978 ASSERT(0);
979 return FALSE;
980 }
981
982 /* Validation of the params is successful so move to the "INIT" state to
983 * allow the first wf_chanspec_iter_next() move the iteration to the first
984 * chanspec in the set.
985 */
986 iter->state = WF_ITER_INIT;
987 iter->band = band;
988 iter->bw = bw;
989 iter->range_id = RANGE_ID_INVAL;
990
991 return TRUE;
992 }
993
994 /**
995 * Start the iterator off from the 'init' state.
996 * The internal state is set up and advanced to the first chanspec.
997 */
998 static void
wf_chanspec_iter_firstchan(wf_chanspec_iter_t * iter)999 wf_chanspec_iter_firstchan(wf_chanspec_iter_t *iter)
1000 {
1001 chanspec_band_t band = iter->band;
1002 chanspec_bw_t bw = iter->bw;
1003 chanspec_t chspec;
1004
1005 /* if BW unspecified (INVCHANSPEC), start with 20 MHz */
1006 if (bw == INVCHANSPEC) {
1007 bw = WL_CHANSPEC_BW_20;
1008 }
1009
1010 /* calc the initial channel based on band */
1011 if (band == WL_CHANSPEC_BAND_2G) {
1012 /* 2g has overlapping 40MHz channels, so cannot just use the
1013 * wf_create_chspec_from_primary() fn.
1014 */
1015 if (bw == WL_CHANSPEC_BW_20) {
1016 chspec = wf_create_20MHz_chspec(CH_MIN_2G_CHANNEL, band);
1017 } else {
1018 chspec = (WL_CHANSPEC_BAND_2G | bw | WL_CHANSPEC_CTL_SB_L |
1019 CH_MIN_2G_40M_CHANNEL);
1020 }
1021 } else {
1022 if (band == WL_CHANSPEC_BAND_5G) {
1023 wf_chanspec_iter_next_5g_range(iter, bw);
1024 } else {
1025 wf_chanspec_iter_6g_range_init(iter, bw);
1026 }
1027 chspec = wf_create_chspec_from_primary(iter->range.start, bw, band);
1028 }
1029
1030 iter->chanspec = chspec;
1031 }
1032
1033 /**
1034 * @brief Return the current chanspec of the iteration.
1035 */
1036 chanspec_t
wf_chanspec_iter_current(wf_chanspec_iter_t * iter)1037 wf_chanspec_iter_current(wf_chanspec_iter_t *iter)
1038 {
1039 return iter->chanspec;
1040 }
1041
1042 /**
1043 * @brief Advance the iteration to the next chanspec in the set.
1044 */
1045 bool
wf_chanspec_iter_next(wf_chanspec_iter_t * iter,chanspec_t * chspec)1046 wf_chanspec_iter_next(wf_chanspec_iter_t *iter, chanspec_t *chspec)
1047 {
1048 bool ok = FALSE;
1049 chanspec_band_t band = iter->band;
1050
1051 /* Handle the INIT and DONE states. Otherwise, we are in the RUN state
1052 * and will dispatch to the 'next' function for the appropriate band.
1053 */
1054 if (iter->state == WF_ITER_INIT) {
1055 iter->state = WF_ITER_RUN;
1056 wf_chanspec_iter_firstchan(iter);
1057 ok = TRUE;
1058 } else if (iter->state == WF_ITER_DONE) {
1059 ok = FALSE;
1060 } else if (band == WL_CHANSPEC_BAND_2G) {
1061 ok = wf_chanspec_iter_next_2g(iter);
1062 } else if (band == WL_CHANSPEC_BAND_5G) {
1063 ok = wf_chanspec_iter_next_5g(iter);
1064 } else if (band == WL_CHANSPEC_BAND_6G) {
1065 ok = wf_chanspec_iter_next_6g(iter);
1066 }
1067
1068 /* Return the new chanspec if a pointer was provided.
1069 * In case the iteration is done, the return will be INVCHANSPEC.
1070 */
1071 if (chspec != NULL) {
1072 *chspec = iter->chanspec;
1073 }
1074
1075 return ok;
1076 }
1077
1078 /**
1079 * When the iterator completes a particular bandwidth, this function
1080 * returns the next BW, or INVCHANSPEC when done.
1081 *
1082 * Internal iterator helper.
1083 */
1084 static chanspec_bw_t
wf_iter_next_bw(chanspec_bw_t bw)1085 wf_iter_next_bw(chanspec_bw_t bw)
1086 {
1087 switch (bw) {
1088 case WL_CHANSPEC_BW_20:
1089 bw = WL_CHANSPEC_BW_40;
1090 break;
1091 case WL_CHANSPEC_BW_40:
1092 bw = WL_CHANSPEC_BW_80;
1093 break;
1094 case WL_CHANSPEC_BW_80:
1095 bw = WL_CHANSPEC_BW_160;
1096 break;
1097 #ifdef WL11BE
1098 case WL_CHANSPEC_BW_160:
1099 bw = WL_CHANSPEC_BW_240;
1100 break;
1101 case WL_CHANSPEC_BW_240:
1102 bw = WL_CHANSPEC_BW_320;
1103 break;
1104 #endif
1105 default:
1106 bw = INVCHANSPEC;
1107 break;
1108 }
1109 return bw;
1110 }
1111
1112 /**
1113 * This is the _iter_next() helper for 2g band chanspec iteration.
1114 */
1115 static bool
wf_chanspec_iter_next_2g(wf_chanspec_iter_t * iter)1116 wf_chanspec_iter_next_2g(wf_chanspec_iter_t *iter)
1117 {
1118 chanspec_t chspec = iter->chanspec;
1119 uint8 ch = CHSPEC_CHANNEL(chspec);
1120
1121 if (CHSPEC_IS20(chspec)) {
1122 if (ch < CH_MAX_2G_CHANNEL) {
1123 ch++;
1124 chspec = wf_create_20MHz_chspec(ch, WL_CHANSPEC_BAND_2G);
1125 } else if (iter->bw == INVCHANSPEC) {
1126 /* hit the end of 20M channels, go to 40M if bw was unspecified */
1127 ch = CH_MIN_2G_40M_CHANNEL;
1128 chspec = wf_create_40MHz_chspec(LOWER_20_SB(ch), ch, WL_CHANSPEC_BAND_2G);
1129 } else {
1130 /* done */
1131 iter->state = WF_ITER_DONE;
1132 chspec = INVCHANSPEC;
1133 }
1134 } else {
1135 /* step through low then high primary sideband, then next 40 center channel */
1136 if (CHSPEC_SB_LOWER(iter->chanspec)) {
1137 /* move from lower primary 20 to upper */
1138 chspec = wf_create_40MHz_chspec(UPPER_20_SB(ch),
1139 ch, WL_CHANSPEC_BAND_2G);
1140 } else if (ch < CH_MAX_2G_40M_CHANNEL) {
1141 /* move to next 40M center and lower primary 20 */
1142 ch++;
1143 chspec = wf_create_40MHz_chspec(LOWER_20_SB(ch),
1144 ch, WL_CHANSPEC_BAND_2G);
1145 } else {
1146 /* done */
1147 iter->state = WF_ITER_DONE;
1148 chspec = INVCHANSPEC;
1149 }
1150 }
1151
1152 iter->chanspec = chspec;
1153
1154 return (chspec != INVCHANSPEC);
1155 }
1156
1157 /**
1158 * This is the _iter_next() helper for 5g band chanspec iteration.
1159 * The 5g iterator uses ranges of primary 20MHz channels, and the current BW, to create
1160 * each chanspec in the set.
1161 * When a 5g range is exhausted, wf_chanspec_iter_next_5g_range() is called to get the next
1162 * range appropriate to the current BW.
1163 */
1164 static bool
wf_chanspec_iter_next_5g(wf_chanspec_iter_t * iter)1165 wf_chanspec_iter_next_5g(wf_chanspec_iter_t *iter)
1166 {
1167 chanspec_t chspec = iter->chanspec;
1168 chanspec_bw_t bw = CHSPEC_BW(chspec);
1169 uint8 ch = wf_chspec_primary20_chan(chspec);
1170 uint8 end = iter->range.end;
1171
1172 if (ch < end) {
1173 /* not at the end of the current range, so
1174 * step to the next 20MHz channel and create the current BW
1175 * channel with that new primary 20MHz.
1176 */
1177 ch += CH_20MHZ_APART;
1178 } else if (wf_chanspec_iter_next_5g_range(iter, bw)) {
1179 /* there was a new range in the current BW, so start at the beginning */
1180 ch = iter->range.start;
1181 } else if (iter->bw == INVCHANSPEC) {
1182 /* hit the end of current bw, so move to the next bw */
1183 bw = wf_iter_next_bw(bw);
1184 if (bw != INVCHANSPEC) {
1185 /* initialize the first range */
1186 iter->range_id = RANGE_ID_INVAL;
1187 wf_chanspec_iter_next_5g_range(iter, bw);
1188 ch = iter->range.start;
1189 } else {
1190 /* no more BWs */
1191 chspec = INVCHANSPEC;
1192 }
1193 } else {
1194 /* no more channels, ranges, or BWs */
1195 chspec = INVCHANSPEC;
1196 }
1197
1198 /* if we are not at the end of the iteration, calc the next chanspec from components */
1199 if (chspec != INVCHANSPEC) {
1200 chspec = wf_create_chspec_from_primary(ch, bw, WL_CHANSPEC_BAND_5G);
1201 }
1202
1203 iter->chanspec = chspec;
1204 if (chspec != INVCHANSPEC) {
1205 return TRUE;
1206 } else {
1207 iter->state = WF_ITER_DONE;
1208 return FALSE;
1209 }
1210 }
1211
1212 /**
1213 * Helper function to set up the next range of primary 20MHz channels to
1214 * iterate over for the current BW. This will advance
1215 * iter->range_id
1216 * and set up
1217 * iter->range.start
1218 * iter->range.end
1219 * for the new range.
1220 * Returns FALSE if there are no more ranges in the current BW.
1221 */
1222 static int
wf_chanspec_iter_next_5g_range(wf_chanspec_iter_t * iter,chanspec_bw_t bw)1223 wf_chanspec_iter_next_5g_range(wf_chanspec_iter_t *iter, chanspec_bw_t bw)
1224 {
1225 uint8 range_id = iter->range_id;
1226 const uint8 *channels;
1227 uint count;
1228
1229 if (bw == WL_CHANSPEC_BW_20) {
1230 if (range_id == RANGE_ID_INVAL) {
1231 range_id = 0;
1232 } else {
1233 range_id++;
1234 }
1235
1236 if (range_id < ARRAYSIZE(wf_5g_iter_ranges)) {
1237 iter->range_id = range_id;
1238 iter->range = wf_5g_iter_ranges[range_id];
1239 return TRUE;
1240 }
1241
1242 return FALSE;
1243 }
1244
1245 if (bw == WL_CHANSPEC_BW_40) {
1246 channels = wf_5g_40m_chans;
1247 count = WF_NUM_5G_40M_CHANS;
1248 } else if (bw == WL_CHANSPEC_BW_80) {
1249 channels = wf_5g_80m_chans;
1250 count = WF_NUM_5G_80M_CHANS;
1251 } else if (bw == WL_CHANSPEC_BW_160) {
1252 channels = wf_5g_160m_chans;
1253 count = WF_NUM_5G_160M_CHANS;
1254 } else {
1255 return FALSE;
1256 }
1257
1258 if (range_id == RANGE_ID_INVAL) {
1259 range_id = 0;
1260 } else {
1261 range_id++;
1262 }
1263 if (range_id < count) {
1264 uint8 ch = channels[range_id];
1265 uint offset = center_chan_to_edge(bw);
1266
1267 iter->range_id = range_id;
1268 iter->range.start = ch - offset;
1269 iter->range.end = ch + offset;
1270 return TRUE;
1271 }
1272
1273 return FALSE;
1274 }
1275
1276 /**
1277 * This is the _iter_next() helper for 6g band chanspec iteration.
1278 * The 6g iterator uses ranges of primary 20MHz channels, and the current BW, to create
1279 * each chanspec in the set.
1280 * Each BW in 6g has one contiguous range of primary 20MHz channels. When a range is
1281 * exhausted, the iterator moves to the next BW.
1282 */
1283 static bool
wf_chanspec_iter_next_6g(wf_chanspec_iter_t * iter)1284 wf_chanspec_iter_next_6g(wf_chanspec_iter_t *iter)
1285 {
1286 chanspec_t chspec = iter->chanspec;
1287 chanspec_bw_t bw = CHSPEC_BW(chspec);
1288 uint8 ch = wf_chspec_primary20_chan(chspec);
1289 uint8 end = iter->range.end;
1290
1291 if (ch < end) {
1292 /* not at the end of the current range, so
1293 * step to the next 20MHz channel and create the current BW
1294 * channel with that new primary 20MHz.
1295 */
1296 ch += CH_20MHZ_APART;
1297
1298 /* try to create a valid channel of the current BW
1299 * with a primary20 'ch'
1300 */
1301 chspec = wf_create_chspec_from_primary(ch, bw, WL_CHANSPEC_BAND_6G);
1302
1303 /* if chspec is INVCHANSPEC, then we hit the end
1304 * of the valid channels in the range.
1305 */
1306 } else {
1307 /* hit the end of the current range */
1308 chspec = INVCHANSPEC;
1309 }
1310
1311 /* if we are at the end of the current channel range
1312 * check if there is another BW to iterate
1313 * Note: (iter->bw == INVCHANSPEC) indicates an unspecified BW for the interation,
1314 * so it will iterate over all BWs.
1315 */
1316 if (chspec == INVCHANSPEC &&
1317 iter->bw == INVCHANSPEC &&
1318 (bw = wf_iter_next_bw(bw)) != INVCHANSPEC) {
1319 /* start the new bw with the first primary20 */
1320 ch = iter->range.start;
1321 chspec = wf_create_chspec_from_primary(ch, bw, WL_CHANSPEC_BAND_6G);
1322 }
1323
1324 iter->chanspec = chspec;
1325 if (chspec != INVCHANSPEC) {
1326 return TRUE;
1327 } else {
1328 iter->state = WF_ITER_DONE;
1329 return FALSE;
1330 }
1331 }
1332
1333 /**
1334 * Helper used by wf_chanspec_iter_firstchan() to set up the first range of
1335 * primary channels for the 6g band and for the BW being iterated.
1336 */
1337 static void
wf_chanspec_iter_6g_range_init(wf_chanspec_iter_t * iter,chanspec_bw_t bw)1338 wf_chanspec_iter_6g_range_init(wf_chanspec_iter_t *iter, chanspec_bw_t bw)
1339 {
1340 switch (bw) {
1341 case WL_CHANSPEC_BW_20:
1342 case WL_CHANSPEC_BW_40:
1343 case WL_CHANSPEC_BW_80:
1344 case WL_CHANSPEC_BW_160:
1345 #ifdef WL11BE
1346 case WL_CHANSPEC_BW_240:
1347 case WL_CHANSPEC_BW_320:
1348 #endif
1349 iter->range.start = CH_MIN_6G_CHANNEL;
1350 iter->range.end = CH_MAX_6G_CHANNEL;
1351 break;
1352 default:
1353 ASSERT(0);
1354 break;
1355 }
1356 }
1357
1358 /**
1359 * Verify that the channel is a valid 20MHz channel according to 802.11.
1360 *
1361 * @param channel 20MHz channel number to validate
1362 * @param band chanspec band
1363 *
1364 * @return Return TRUE if valid
1365 */
1366 bool
wf_valid_20MHz_chan(uint channel,chanspec_band_t band)1367 wf_valid_20MHz_chan(uint channel, chanspec_band_t band)
1368 {
1369 if (band == WL_CHANSPEC_BAND_2G) {
1370 /* simple range check for 2GHz */
1371 return (channel >= CH_MIN_2G_CHANNEL &&
1372 channel <= CH_MAX_2G_CHANNEL);
1373 } else if (band == WL_CHANSPEC_BAND_5G) {
1374 const uint8 *center_ch = wf_5g_40m_chans;
1375 uint num_ch = WF_NUM_5G_40M_CHANS;
1376 uint i;
1377
1378 /* We don't have an array of legal 20MHz 5G channels, but they are
1379 * each side of the legal 40MHz channels. Check the chanspec
1380 * channel against either side of the 40MHz channels.
1381 */
1382 for (i = 0; i < num_ch; i ++) {
1383 if (channel == (uint)LOWER_20_SB(center_ch[i]) ||
1384 channel == (uint)UPPER_20_SB(center_ch[i])) {
1385 break; /* match found */
1386 }
1387 }
1388
1389 if (i == num_ch) {
1390 /* check for channel 165 which is not the side band
1391 * of 40MHz 5G channel
1392 */
1393 if (channel == 165) {
1394 i = 0;
1395 }
1396
1397 /* check for legacy JP channels on failure */
1398 if (channel == 34 || channel == 38 ||
1399 channel == 42 || channel == 46) {
1400 i = 0;
1401 }
1402 }
1403
1404 if (i < num_ch) {
1405 /* match found */
1406 return TRUE;
1407 }
1408 }
1409
1410 else if (band == WL_CHANSPEC_BAND_6G) {
1411 /* Use the simple pattern of 6GHz 20MHz channels for validity check */
1412 if ((channel >= CH_MIN_6G_CHANNEL &&
1413 channel <= CH_MAX_6G_CHANNEL) &&
1414 ((((channel - CH_MIN_6G_CHANNEL) % 4) == 0) || // even multiple of 4
1415 channel == 2)) { // Or the oddball channel 2
1416 return TRUE;
1417 }
1418 }
1419
1420 return FALSE;
1421 }
1422
1423 /**
1424 * Verify that the center channel is a valid 40MHz center channel according to 802.11.
1425 *
1426 * @param center_channel 40MHz center channel to validate
1427 * @param band chanspec band
1428 *
1429 * @return Return TRUE if valid
1430 */
1431 bool
wf_valid_40MHz_center_chan(uint center_channel,chanspec_band_t band)1432 wf_valid_40MHz_center_chan(uint center_channel, chanspec_band_t band)
1433 {
1434 if (band == WL_CHANSPEC_BAND_2G) {
1435 /* simple range check for 2GHz */
1436 return (center_channel >= CH_MIN_2G_40M_CHANNEL &&
1437 center_channel <= CH_MAX_2G_40M_CHANNEL);
1438 } else if (band == WL_CHANSPEC_BAND_5G) {
1439 uint i;
1440
1441 /* use the 5GHz lookup of 40MHz channels */
1442 for (i = 0; i < WF_NUM_5G_40M_CHANS; i++) {
1443 if (center_channel == wf_5g_40m_chans[i]) {
1444 return TRUE;
1445 }
1446 }
1447 }
1448 else if (band == WL_CHANSPEC_BAND_6G) {
1449 /* Use the simple pattern of 6GHz center channels */
1450 if ((center_channel >= CH_MIN_6G_40M_CHANNEL &&
1451 center_channel <= CH_MAX_6G_40M_CHANNEL) &&
1452 ((center_channel - CH_MIN_6G_40M_CHANNEL) % 8) == 0) { // even multiple of 8
1453 return TRUE;
1454 }
1455 }
1456
1457 return FALSE;
1458 }
1459
1460 /**
1461 * Verify that the center channel is a valid 80MHz center channel according to 802.11.
1462 *
1463 * @param center_channel 80MHz center channel to validate
1464 * @param band chanspec band
1465 *
1466 * @return Return TRUE if valid
1467 */
1468 bool
wf_valid_80MHz_center_chan(uint center_channel,chanspec_band_t band)1469 wf_valid_80MHz_center_chan(uint center_channel, chanspec_band_t band)
1470 {
1471 if (band == WL_CHANSPEC_BAND_5G) {
1472 /* use the 80MHz ID lookup to validate the center channel */
1473 if (channel_80mhz_to_id(center_channel) >= 0) {
1474 return TRUE;
1475 }
1476 } else if (band == WL_CHANSPEC_BAND_6G) {
1477 /* use the 80MHz ID lookup to validate the center channel */
1478 if (channel_6g_80mhz_to_id(center_channel) >= 0) {
1479 return TRUE;
1480 }
1481 }
1482
1483 return FALSE;
1484 }
1485
1486 /**
1487 * Verify that the center channel is a valid 160MHz center channel according to 802.11.
1488 *
1489 * @param center_channel 160MHz center channel to validate
1490 * @param band chanspec band
1491 *
1492 * @return Return TRUE if valid
1493 */
1494 bool
wf_valid_160MHz_center_chan(uint center_channel,chanspec_band_t band)1495 wf_valid_160MHz_center_chan(uint center_channel, chanspec_band_t band)
1496 {
1497 if (band == WL_CHANSPEC_BAND_5G) {
1498 uint i;
1499
1500 /* use the 5GHz lookup of 40MHz channels */
1501 for (i = 0; i < WF_NUM_5G_160M_CHANS; i++) {
1502 if (center_channel == wf_5g_160m_chans[i]) {
1503 return TRUE;
1504 }
1505 }
1506 } else if (band == WL_CHANSPEC_BAND_6G) {
1507 /* Use the simple pattern of 6GHz center channels */
1508 if ((center_channel >= CH_MIN_6G_160M_CHANNEL &&
1509 center_channel <= CH_MAX_6G_160M_CHANNEL) &&
1510 ((center_channel - CH_MIN_6G_160M_CHANNEL) % 32) == 0) { // even multiple of 32
1511 return TRUE;
1512 }
1513 }
1514
1515 return FALSE;
1516 }
1517
1518 /**
1519 * Verify that the center channel is a valid 240MHz center channel according to 802.11.
1520 *
1521 * @param center_channel 240MHz center channel to validate
1522 * @param band chanspec band
1523 *
1524 * @return Return TRUE if valid
1525 */
1526 bool
wf_valid_240MHz_center_chan(uint center_channel,chanspec_band_t band)1527 wf_valid_240MHz_center_chan(uint center_channel, chanspec_band_t band)
1528 {
1529 if (band == WL_CHANSPEC_BAND_6G) {
1530 /* Use the simple pattern of 6GHz center channels */
1531 if ((center_channel >= CH_MIN_6G_240M_CHANNEL &&
1532 center_channel <= CH_MAX_6G_240M_CHANNEL) &&
1533 ((center_channel - CH_MIN_6G_240M_CHANNEL) % 48) == 0) { // even multiple of 48
1534 return TRUE;
1535 }
1536 }
1537
1538 return FALSE;
1539 }
1540
1541 /**
1542 * Verify that the center channel is a valid 320MHz center channel according to 802.11.
1543 *
1544 * @param center_channel 320MHz center channel to validate
1545 * @param band chanspec band
1546 *
1547 * @return Return TRUE if valid
1548 */
1549 bool
wf_valid_320MHz_center_chan(uint center_channel,chanspec_band_t band)1550 wf_valid_320MHz_center_chan(uint center_channel, chanspec_band_t band)
1551 {
1552 if (band == WL_CHANSPEC_BAND_6G) {
1553 /* Use the simple pattern of 6GHz center channels */
1554 if ((center_channel >= CH_MIN_6G_320M_CHANNEL &&
1555 center_channel <= CH_MAX_6G_320M_CHANNEL) &&
1556 ((center_channel - CH_MIN_6G_320M_CHANNEL) % 64) == 0) { // even multiple of 64
1557 return TRUE;
1558 }
1559 }
1560
1561 return FALSE;
1562 }
1563
1564 /*
1565 * This function returns TRUE if both the chanspec can co-exist in PHY.
1566 * Addition to primary20 channel, the function checks for side band for 2g 40 channels
1567 */
1568 bool
wf_chspec_coexist(chanspec_t chspec1,chanspec_t chspec2)1569 wf_chspec_coexist(chanspec_t chspec1, chanspec_t chspec2)
1570 {
1571 bool same_primary;
1572
1573 same_primary = (wf_chspec_primary20_chan(chspec1) == wf_chspec_primary20_chan(chspec2));
1574
1575 if (same_primary && CHSPEC_IS2G(chspec1)) {
1576 if (CHSPEC_IS40(chspec1) && CHSPEC_IS40(chspec2)) {
1577 return (CHSPEC_CTL_SB(chspec1) == CHSPEC_CTL_SB(chspec2));
1578 }
1579 }
1580 return same_primary;
1581 }
1582
1583 /**
1584 * Create a 20MHz chanspec for the given band.
1585 *
1586 * This function returns a 20MHz chanspec in the given band.
1587 *
1588 * @param channel 20MHz channel number
1589 * @param band a chanspec band (e.g. WL_CHANSPEC_BAND_2G)
1590 *
1591 * @return Returns a 20MHz chanspec, or IVNCHANSPEC in case of error.
1592 */
1593 chanspec_t
wf_create_20MHz_chspec(uint channel,chanspec_band_t band)1594 wf_create_20MHz_chspec(uint channel, chanspec_band_t band)
1595 {
1596 chanspec_t chspec;
1597
1598 if (channel <= WL_CHANSPEC_CHAN_MASK &&
1599 (band == WL_CHANSPEC_BAND_2G ||
1600 band == WL_CHANSPEC_BAND_5G ||
1601 band == WL_CHANSPEC_BAND_6G)) {
1602 chspec = band | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE | channel;
1603 if (!wf_chspec_valid(chspec)) {
1604 chspec = INVCHANSPEC;
1605 }
1606 } else {
1607 chspec = INVCHANSPEC;
1608 }
1609
1610 return chspec;
1611 }
1612
1613 /**
1614 * Returns the chanspec for a 40MHz channel given the primary 20MHz channel number,
1615 * the center channel number, and the band.
1616 *
1617 * @param primary_channel primary 20Mhz channel
1618 * @param center_channel center channel of the 40MHz channel
1619 * @param band band of the 40MHz channel (chanspec_band_t value)
1620 *
1621 * The center_channel can be one of the 802.11 spec valid 40MHz chenter channels
1622 * in the given band.
1623 *
1624 * @return returns a 40MHz chanspec, or INVCHANSPEC in case of error
1625 */
1626 chanspec_t
wf_create_40MHz_chspec(uint primary_channel,uint center_channel,chanspec_band_t band)1627 wf_create_40MHz_chspec(uint primary_channel, uint center_channel,
1628 chanspec_band_t band)
1629 {
1630 int sb;
1631
1632 /* Calculate the sideband value for the center and primary channel.
1633 * Will return -1 if not a valid pair for 40MHz
1634 */
1635 sb = channel_to_sb(center_channel, primary_channel, WL_CHANSPEC_BW_40);
1636
1637 /* return err if the sideband was bad or the center channel is not
1638 * valid for the given band.
1639 */
1640 if (sb < 0 || !wf_valid_40MHz_center_chan(center_channel, band)) {
1641 return INVCHANSPEC;
1642 }
1643
1644 /* othewise construct and return the valid 40MHz chanspec */
1645 return (chanspec_t)(center_channel | WL_CHANSPEC_BW_40 | band |
1646 ((uint)sb << WL_CHANSPEC_CTL_SB_SHIFT));
1647 }
1648
1649 /**
1650 * Returns the chanspec for a 40MHz channel given the primary 20MHz channel number,
1651 * the sub-band for the primary 20MHz channel, and the band.
1652 *
1653 * @param primary_channel primary 20Mhz channel
1654 * @param primary_subband sub-band of the 20MHz primary channel (chanspec_subband_t value)
1655 * @param band band of the 40MHz channel (chanspec_band_t value)
1656 *
1657 * The primary channel and sub-band should describe one of the 802.11 spec valid
1658 * 40MHz channels in the given band.
1659 *
1660 * @return returns a 40MHz chanspec, or INVCHANSPEC in case of error
1661 */
1662 chanspec_t
wf_create_40MHz_chspec_primary_sb(uint primary_channel,chanspec_subband_t primary_subband,chanspec_band_t band)1663 wf_create_40MHz_chspec_primary_sb(uint primary_channel, chanspec_subband_t primary_subband,
1664 chanspec_band_t band)
1665 {
1666 uint center_channel;
1667
1668 /* find the center channel */
1669 if (primary_subband == WL_CHANSPEC_CTL_SB_L) {
1670 center_channel = primary_channel + CH_10MHZ_APART;
1671 } else if (primary_subband == WL_CHANSPEC_CTL_SB_U) {
1672 center_channel = primary_channel - CH_10MHZ_APART;
1673 } else {
1674 return INVCHANSPEC;
1675 }
1676
1677 return wf_create_40MHz_chspec(primary_channel, center_channel, band);
1678 }
1679
1680 /**
1681 * Returns the chanspec for an 80MHz channel given the primary 20MHz channel number,
1682 * the center channel number, and the band.
1683 *
1684 * @param primary_channel primary 20Mhz channel
1685 * @param center_channel center channel of the 80MHz channel
1686 * @param band band of the 80MHz channel (chanspec_band_t value)
1687 *
1688 * The center_channel can be one of {42, 58, 106, 122, 138, 155} for 5G,
1689 * or {7 + 16*X for 0 <= X <= 13} for 6G.
1690 *
1691 * @return returns an 80MHz chanspec, or INVCHANSPEC in case of error
1692 */
1693 chanspec_t
wf_create_80MHz_chspec(uint primary_channel,uint center_channel,chanspec_band_t band)1694 wf_create_80MHz_chspec(uint primary_channel, uint center_channel,
1695 chanspec_band_t band)
1696 {
1697 int sb;
1698
1699 /* Calculate the sideband value for the center and primary channel.
1700 * Will return -1 if not a valid pair for 80MHz
1701 */
1702 sb = channel_to_sb(center_channel, primary_channel, WL_CHANSPEC_BW_80);
1703
1704 /* return err if the sideband was bad or the center channel is not
1705 * valid for the given band.
1706 */
1707 if (sb < 0 || !wf_valid_80MHz_center_chan(center_channel, band)) {
1708 return INVCHANSPEC;
1709 }
1710
1711 /* othewise construct and return the valid 80MHz chanspec */
1712 return (chanspec_t)(center_channel | WL_CHANSPEC_BW_80 | band |
1713 ((uint)sb << WL_CHANSPEC_CTL_SB_SHIFT));
1714 }
1715
1716 /**
1717 * Returns the chanspec for an 160MHz channel given the primary 20MHz channel number,
1718 * the center channel number, and the band.
1719 *
1720 * @param primary_channel primary 20Mhz channel
1721 * @param center_channel center channel of the 160MHz channel
1722 * @param band band of the 160MHz channel (chanspec_band_t value)
1723 *
1724 * The center_channel can be one of {50, 114} for 5G,
1725 * or {15 + 32*X for 0 <= X <= 7} for 6G.
1726 *
1727 * @return returns an 160MHz chanspec, or INVCHANSPEC in case of error
1728 */
1729 chanspec_t
wf_create_160MHz_chspec(uint primary_channel,uint center_channel,chanspec_band_t band)1730 wf_create_160MHz_chspec(uint primary_channel, uint center_channel, chanspec_band_t band)
1731 {
1732 int sb;
1733
1734 /* Calculate the sideband value for the center and primary channel.
1735 * Will return -1 if not a valid pair for 160MHz
1736 */
1737 sb = channel_to_sb(center_channel, primary_channel, WL_CHANSPEC_BW_160);
1738
1739 /* return err if the sideband was bad or the center channel is not
1740 * valid for the given band.
1741 */
1742 if (sb < 0 || !wf_valid_160MHz_center_chan(center_channel, band)) {
1743 return INVCHANSPEC;
1744 }
1745
1746 /* othewise construct and return the valid 160MHz chanspec */
1747 return (chanspec_t)(center_channel | WL_CHANSPEC_BW_160 | band |
1748 ((uint)sb << WL_CHANSPEC_CTL_SB_SHIFT));
1749 }
1750
1751 /**
1752 * Returns the chanspec for an 80+80MHz channel given the primary 20MHz channel number,
1753 * the center channel numbers for each frequency segment, and the band.
1754 *
1755 * @param primary_channel primary 20 Mhz channel
1756 * @param chan0 center channel number of one frequency segment
1757 * @param chan1 center channel number of the other frequency segment
1758 * @param band band of the 80+80 MHz channel (chanspec_band_t value)
1759 *
1760 * Parameters chan0 and chan1 are valid 80 MHz center channel numbers for the given band.
1761 * The primary channel must be contained in one of the 80 MHz channels. This routine
1762 * will determine which frequency segment is the primary 80 MHz segment.
1763 *
1764 * @return returns an 80+80 MHz chanspec, or INVCHANSPEC in case of error
1765 *
1766 * Refer to 802.11-2016 section 21.3.14 "Channelization".
1767 */
1768 chanspec_t
wf_create_8080MHz_chspec(uint primary_channel,uint chan0,uint chan1,chanspec_band_t band)1769 wf_create_8080MHz_chspec(uint primary_channel, uint chan0, uint chan1,
1770 chanspec_band_t band)
1771 {
1772 int sb = 0;
1773 chanspec_t chanspec = 0;
1774 int chan0_id = -1, chan1_id = -1;
1775 int seg0, seg1;
1776
1777 /* frequency segments need to be non-contiguous, so the channel separation needs
1778 * to be greater than 80MHz
1779 */
1780 if ((uint)ABS((int)(chan0 - chan1)) <= CH_80MHZ_APART) {
1781 return INVCHANSPEC;
1782 }
1783
1784 if (band == WL_CHANSPEC_BAND_5G) {
1785 chan0_id = channel_80mhz_to_id(chan0);
1786 chan1_id = channel_80mhz_to_id(chan1);
1787 } else if (band == WL_CHANSPEC_BAND_6G) {
1788 chan0_id = channel_6g_80mhz_to_id(chan0);
1789 chan1_id = channel_6g_80mhz_to_id(chan1);
1790 }
1791
1792 /* make sure the channel numbers were valid */
1793 if (chan0_id == -1 || chan1_id == -1) {
1794 return INVCHANSPEC;
1795 }
1796
1797 /* does the primary channel fit with the 1st 80MHz channel ? */
1798 sb = channel_to_sb(chan0, primary_channel, WL_CHANSPEC_BW_80);
1799 if (sb >= 0) {
1800 /* yes, so chan0 is frequency segment 0, and chan1 is seg 1 */
1801 seg0 = chan0_id;
1802 seg1 = chan1_id;
1803 } else {
1804 /* no, so does the primary channel fit with the 2nd 80MHz channel ? */
1805 sb = channel_to_sb(chan1, primary_channel, WL_CHANSPEC_BW_80);
1806 if (sb < 0) {
1807 /* no match for pri_ch to either 80MHz center channel */
1808 return INVCHANSPEC;
1809 }
1810 /* swapped, so chan1 is frequency segment 0, and chan0 is seg 1 */
1811 seg0 = chan1_id;
1812 seg1 = chan0_id;
1813 }
1814
1815 chanspec = ((seg0 << WL_CHANSPEC_CHAN0_SHIFT) |
1816 (seg1 << WL_CHANSPEC_CHAN1_SHIFT) |
1817 (sb << WL_CHANSPEC_CTL_SB_SHIFT) |
1818 WL_CHANSPEC_BW_8080 |
1819 band);
1820
1821 return chanspec;
1822 }
1823
1824 /**
1825 * Returns the chanspec for an 160+160MHz channel given the primary 20MHz channel number,
1826 * the center channel numbers for each frequency segment, and the band.
1827 *
1828 * @param primary_channel primary 20 Mhz channel
1829 * @param chan0 center channel number of one frequency segment
1830 * @param chan1 center channel number of the other frequency segment
1831 * @param band band of the 160+160 MHz channel (chanspec_band_t value)
1832 *
1833 * Parameters chan0 and chan1 are valid 160 MHz center channel numbers for the given band.
1834 * The primary channel must be contained in one of the 160 MHz channels. This routine
1835 * will determine which frequency segment is the primary 160 MHz segment.
1836 *
1837 * @return returns an 160+160 MHz chanspec, or INVCHANSPEC in case of error
1838 *
1839 * Refer to <TBD> "Channelization".
1840 */
1841 chanspec_t
wf_create_160160MHz_chspec(uint primary_channel,uint chan0,uint chan1,chanspec_band_t band)1842 wf_create_160160MHz_chspec(uint primary_channel, uint chan0, uint chan1,
1843 chanspec_band_t band)
1844 {
1845 int sb = 0;
1846 chanspec_t chanspec = 0;
1847 int chan0_id = -1, chan1_id = -1;
1848 int seg0, seg1;
1849
1850 /* frequency segments need to be non-contiguous, so the channel separation needs
1851 * to be greater than 160MHz
1852 */
1853 if ((uint)ABS((int)(chan0 - chan1)) <= CH_160MHZ_APART) {
1854 return INVCHANSPEC;
1855 }
1856
1857 if (band == WL_CHANSPEC_BAND_5G) {
1858 chan0_id = channel_5g_160mhz_to_id(chan0);
1859 chan1_id = channel_5g_160mhz_to_id(chan1);
1860 } else if (band == WL_CHANSPEC_BAND_6G) {
1861 chan0_id = channel_6g_160mhz_to_id(chan0);
1862 chan1_id = channel_6g_160mhz_to_id(chan1);
1863 }
1864
1865 /* make sure the channel numbers were valid */
1866 if (chan0_id == -1 || chan1_id == -1) {
1867 return INVCHANSPEC;
1868 }
1869
1870 /* does the primary channel fit with the 1st 160MHz channel ? */
1871 sb = channel_to_sb(chan0, primary_channel, WL_CHANSPEC_BW_160);
1872 if (sb >= 0) {
1873 /* yes, so chan0 is frequency segment 0, and chan1 is seg 1 */
1874 seg0 = chan0_id;
1875 seg1 = chan1_id;
1876 } else {
1877 /* no, so does the primary channel fit with the 2nd 160MHz channel ? */
1878 sb = channel_to_sb(chan1, primary_channel, WL_CHANSPEC_BW_160);
1879 if (sb < 0) {
1880 /* no match for pri_ch to either 160MHz center channel */
1881 return INVCHANSPEC;
1882 }
1883 /* swapped, so chan1 is frequency segment 0, and chan0 is seg 1 */
1884 seg0 = chan1_id;
1885 seg1 = chan0_id;
1886 }
1887
1888 chanspec = ((seg0 << WL_CHANSPEC_CHAN0_SHIFT) |
1889 (seg1 << WL_CHANSPEC_CHAN1_SHIFT) |
1890 (sb << WL_CHANSPEC_CTL_SB_SHIFT) |
1891 WL_CHANSPEC_BW_160160 |
1892 band);
1893
1894 return chanspec;
1895 }
1896
1897 /**
1898 * Returns the chanspec for an 240MHz channel given the primary 20MHz channel number,
1899 * the center channel number, and the band.
1900 *
1901 * @param primary_channel primary 20 Mhz channel
1902 * @param chan center channel number
1903 * @param band band of the 240 MHz channel (chanspec_band_t value)
1904 *
1905 * @return returns an 240 MHz chanspec, or INVCHANSPEC in case of error
1906 *
1907 * Refer to <TBD> "Channelization".
1908 */
1909 chanspec_t
wf_create_240MHz_chspec(uint primary_channel,uint center_channel,chanspec_band_t band)1910 wf_create_240MHz_chspec(uint primary_channel, uint center_channel, chanspec_band_t band)
1911 {
1912 int sb = 0;
1913 chanspec_t chanspec = 0;
1914 int chan_id = -1;
1915
1916 if (band == WL_CHANSPEC_BAND_6G) {
1917 chan_id = channel_6g_240mhz_to_id(center_channel);
1918 }
1919
1920 /* make sure the channel number were valid */
1921 if (chan_id == -1) {
1922 return INVCHANSPEC;
1923 }
1924
1925 /* Calculate the sideband value for the center and primary channel.
1926 * Will return -1 if not a valid pair for 240MHz
1927 */
1928 sb = channel_to_sb(center_channel, primary_channel, WL_CHANSPEC_BW_240);
1929
1930 /* return err if the sideband was bad or the center channel is not
1931 * valid for the given band.
1932 */
1933 if (sb < 0 || !wf_valid_240MHz_center_chan(center_channel, band)) {
1934 return INVCHANSPEC;
1935 }
1936
1937 chanspec = ((chan_id << WL_CHANSPEC_GE240_CHAN_SHIFT) |
1938 (sb << WL_CHANSPEC_GE240_SB_SHIFT) |
1939 WL_CHANSPEC_BW_240 |
1940 band);
1941
1942 return chanspec;
1943 }
1944
1945 /**
1946 * Returns the chanspec for an 320MHz channel given the primary 20MHz channel number,
1947 * the center channel number, and the band.
1948 *
1949 * @param primary_channel primary 20 Mhz channel
1950 * @param chan center channel number
1951 * @param band band of the 320 MHz channel (chanspec_band_t value)
1952 *
1953 * Parameters chan is valid 320 MHz center channel numbers for the given band.
1954 * The primary channel must be contained in one of the 320 MHz channels.
1955 *
1956 * @return returns an 320 MHz chanspec, or INVCHANSPEC in case of error
1957 *
1958 * Refer to <TBD> "Channelization".
1959 */
1960 chanspec_t
wf_create_320MHz_chspec(uint primary_channel,uint center_channel,chanspec_band_t band)1961 wf_create_320MHz_chspec(uint primary_channel, uint center_channel, chanspec_band_t band)
1962 {
1963 int sb = 0;
1964 chanspec_t chanspec = 0;
1965 int chan_id = -1;
1966
1967 if (band == WL_CHANSPEC_BAND_6G) {
1968 chan_id = channel_6g_320mhz_to_id(center_channel);
1969 }
1970
1971 /* make sure the channel number were valid */
1972 if (chan_id == -1) {
1973 return INVCHANSPEC;
1974 }
1975
1976 /* Calculate the sideband value for the center and primary channel.
1977 * Will return -1 if not a valid pair for 320MHz
1978 */
1979 sb = channel_to_sb(center_channel, primary_channel, WL_CHANSPEC_BW_320);
1980
1981 /* return err if the sideband was bad or the center channel is not
1982 * valid for the given band.
1983 */
1984 if (sb < 0 || !wf_valid_320MHz_center_chan(center_channel, band)) {
1985 return INVCHANSPEC;
1986 }
1987
1988 chanspec = ((chan_id << WL_CHANSPEC_GE240_CHAN_SHIFT) |
1989 (sb << WL_CHANSPEC_GE240_SB_SHIFT) |
1990 WL_CHANSPEC_BW_320 |
1991 band);
1992
1993 return chanspec;
1994 }
1995
1996 /**
1997 * Returns the chanspec given the primary 20MHz channel number,
1998 * the center channel number, channel width, and the band. The channel width
1999 * must be 20, 40, 80, 160, 240 or 320 MHz.
2000 * 80+80 or 160+160 MHz chanspec creation is not handled by this function,
2001 * use wf_create_8080MHz_chspec() or wf_create_160160MHz_chspec()instead.
2002 *
2003 * @param primary_channel primary 20Mhz channel
2004 * @param center_channel center channel of the channel
2005 * @param bw width of the channel (chanspec_bw_t)
2006 * @param band chanspec band of channel (chanspec_band_t)
2007 *
2008 * The center_channel can be one of the 802.11 spec valid center channels
2009 * for the given bandwidth in the given band.
2010 *
2011 * @return returns a chanspec, or INVCHANSPEC in case of error
2012 */
2013 chanspec_t
wf_create_chspec(uint primary_channel,uint center_channel,chanspec_bw_t bw,chanspec_band_t band)2014 wf_create_chspec(uint primary_channel, uint center_channel,
2015 chanspec_bw_t bw, chanspec_band_t band)
2016 {
2017 chanspec_t chspec = INVCHANSPEC;
2018 int sb = -1;
2019 uint sb_shift;
2020
2021 /* 20MHz channels have matching center and primary channels */
2022 if (bw == WL_CHANSPEC_BW_20 && primary_channel == center_channel) {
2023
2024 sb = 0;
2025
2026 } else if (bw == WL_CHANSPEC_BW_40 ||
2027 bw == WL_CHANSPEC_BW_80 ||
2028 bw == WL_CHANSPEC_BW_160 ||
2029 WFC_BW_EQ(bw, WL_CHANSPEC_BW_240) ||
2030 WFC_BW_EQ(bw, WL_CHANSPEC_BW_320)) {
2031
2032 /* calculate the sub-band index */
2033 sb = channel_to_sb(center_channel, primary_channel, bw);
2034 }
2035
2036 /* if we have a good sub-band, assemble the chanspec, and use wf_chspec_valid()
2037 * to check it for correctness
2038 */
2039 if (sb >= 0) {
2040 if (WFC_BW_EQ(bw, WL_CHANSPEC_BW_240)) {
2041 if (band == WL_CHANSPEC_BAND_6G) {
2042 center_channel = channel_6g_240mhz_to_id(center_channel);
2043 sb_shift = WL_CHANSPEC_GE240_SB_SHIFT;
2044 } else {
2045 return INVCHANSPEC;
2046 }
2047 } else if (WFC_BW_EQ(bw, WL_CHANSPEC_BW_320)) {
2048 if (band == WL_CHANSPEC_BAND_6G) {
2049 center_channel = channel_6g_320mhz_to_id(center_channel);
2050 sb_shift = WL_CHANSPEC_GE240_SB_SHIFT;
2051 } else {
2052 return INVCHANSPEC;
2053 }
2054 } else {
2055 sb_shift = WL_CHANSPEC_CTL_SB_SHIFT;
2056 }
2057 chspec = center_channel | band | bw |
2058 ((uint)sb << sb_shift);
2059 if (!wf_chspec_valid(chspec)) {
2060 chspec = INVCHANSPEC;
2061 }
2062 }
2063
2064 return chspec;
2065 }
2066
2067 /**
2068 * Returns the chanspec given the primary 20MHz channel number,
2069 * channel width, and the band.
2070 *
2071 * @param primary_channel primary 20Mhz channel
2072 * @param bw width of the channel (chanspec_bw_t)
2073 * @param band chanspec band of channel (chanspec_band_t)
2074 *
2075 * @return returns a chanspec, or INVCHANSPEC in case of error
2076 *
2077 * This function is a similar to wf_create_chspec() but does not require the
2078 * center_channel parameter. As a result, it can not create 40MHz channels on
2079 * the 2G band.
2080 *
2081 * This function supports creating 20MHz bandwidth chanspecs on any band.
2082 *
2083 * For the 2GHz band, 40MHz channels overlap, so two 40MHz channels may
2084 * have the same primary 20MHz channel. This function will return INVCHANSPEC
2085 * whenever called with a bandwidth of 40MHz or wider for the 2GHz band.
2086 *
2087 * 5GHz and 6GHz bands have non-overlapping 40/80/160 MHz channels, so a
2088 * 20MHz primary channel uniquely specifies a wider channel in a given band.
2089 *
2090 * 80+80MHz channels also cannot be uniquely defined. This function will return
2091 * INVCHANSPEC whenever bandwidth of WL_CHANSPEC_BW_8080.
2092 */
2093 chanspec_t
wf_create_chspec_from_primary(uint primary_channel,chanspec_bw_t bw,chanspec_band_t band)2094 wf_create_chspec_from_primary(uint primary_channel, chanspec_bw_t bw, chanspec_band_t band)
2095 {
2096 chanspec_t chspec = INVCHANSPEC;
2097
2098 if (bw == WL_CHANSPEC_BW_20) {
2099 chspec = wf_create_20MHz_chspec(primary_channel, band);
2100 } else if (band == WL_CHANSPEC_BAND_2G || band == WL_CHANSPEC_BAND_5G) {
2101 /* For 5GHz, use the lookup tables for valid 40/80/160 center channels
2102 * and search for a center channel compatible with the given primary channel.
2103 */
2104 const uint8 *center_ch = NULL;
2105 uint num_ch, i;
2106
2107 if (band == WL_CHANSPEC_BAND_2G && bw == WL_CHANSPEC_BW_40) {
2108 center_ch = wf_2g_40m_chans;
2109 num_ch = WF_NUM_2G_40M_CHANS;
2110 } else
2111 if (bw == WL_CHANSPEC_BW_40) {
2112 center_ch = wf_5g_40m_chans;
2113 num_ch = WF_NUM_5G_40M_CHANS;
2114 } else if (bw == WL_CHANSPEC_BW_80) {
2115 center_ch = wf_5g_80m_chans;
2116 num_ch = WF_NUM_5G_80M_CHANS;
2117 } else if (bw == WL_CHANSPEC_BW_160) {
2118 center_ch = wf_5g_160m_chans;
2119 num_ch = WF_NUM_5G_160M_CHANS;
2120 } else {
2121 num_ch = 0;
2122 }
2123
2124 for (i = 0; i < num_ch; i ++) {
2125 chspec = wf_create_chspec(primary_channel, center_ch[i], bw, band);
2126 if (chspec != INVCHANSPEC) {
2127 break;
2128 }
2129 }
2130 }
2131 else if (band == WL_CHANSPEC_BAND_6G) {
2132 /* For 6GHz, use a formula to calculate the valid 40/80/160 center channel from
2133 * the primary channel.
2134 */
2135 uint ch_per_block;
2136 uint mask;
2137 uint base, center;
2138
2139 if (bw == WL_CHANSPEC_BW_40) {
2140 ch_per_block = 8;
2141 } else if (bw == WL_CHANSPEC_BW_80) {
2142 ch_per_block = 16;
2143 } else if (bw == WL_CHANSPEC_BW_160) {
2144 ch_per_block = 32;
2145 } else if (WFC_BW_EQ(bw, WL_CHANSPEC_BW_240)) {
2146 ch_per_block = 48;
2147 } else if (WFC_BW_EQ(bw, WL_CHANSPEC_BW_320)) {
2148 ch_per_block = 64;
2149 } else {
2150 ch_per_block = 0;
2151 }
2152
2153 if (ch_per_block) {
2154 /* calculate the base of the block of channel numbers
2155 * covered by the given bw
2156 */
2157 mask = ~(ch_per_block - 1);
2158 base = 1 + ((primary_channel - 1) & mask);
2159
2160 /* calculate the center channel from the base channel */
2161 center = base + center_chan_to_edge(bw);
2162
2163 chspec = wf_create_chspec(primary_channel, center, bw, band);
2164 }
2165 }
2166
2167 return chspec;
2168 }
2169
2170 /**
2171 * Return the primary 20MHz channel.
2172 *
2173 * This function returns the channel number of the primary 20MHz channel. For
2174 * 20MHz channels this is just the channel number. For 40MHz or wider channels
2175 * it is the primary 20MHz channel specified by the chanspec.
2176 *
2177 * @param chspec input chanspec
2178 *
2179 * @return Returns the channel number of the primary 20MHz channel
2180 */
2181 uint8
wf_chspec_primary20_chan(chanspec_t chspec)2182 wf_chspec_primary20_chan(chanspec_t chspec)
2183 {
2184 uint center_chan = INVCHANNEL;
2185 chanspec_bw_t bw;
2186 uint sb;
2187
2188 ASSERT(!wf_chspec_malformed(chspec));
2189
2190 /* Is there a sideband ? */
2191 if (CHSPEC_IS20(chspec)) {
2192 return CHSPEC_CHANNEL(chspec);
2193 } else {
2194 if ((CHSPEC_IS240(chspec)) || (CHSPEC_IS320(chspec))) {
2195 sb = CHSPEC_GE240_SB(chspec) >> WL_CHANSPEC_GE240_SB_SHIFT;
2196 } else {
2197 sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT;
2198 }
2199
2200 if (CHSPEC_IS240(chspec)) {
2201 /* use bw 240MHz for the primary channel lookup */
2202 bw = WL_CHANSPEC_BW_240;
2203
2204 /* convert from channel index to channel number */
2205 if (CHSPEC_IS6G(chspec)) {
2206 center_chan = wf_chspec_6G_id240_to_ch(CHSPEC_GE240_CHAN(chspec));
2207 }
2208 } else if (CHSPEC_IS320(chspec)) {
2209 /* use bw 320MHz for the primary channel lookup */
2210 bw = WL_CHANSPEC_BW_320;
2211
2212 /* convert from channel index to channel number */
2213 if (CHSPEC_IS6G(chspec)) {
2214 center_chan = wf_chspec_6G_id320_to_ch(CHSPEC_GE240_CHAN(chspec));
2215 }
2216 /* What to return otherwise? */
2217 }
2218 else {
2219 bw = CHSPEC_BW(chspec);
2220 center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT;
2221 }
2222
2223 return (uint8)(channel_to_primary20_chan((uint8)center_chan, bw, sb));
2224 }
2225 }
2226
2227 /**
2228 * Return the bandwidth string for a given chanspec
2229 *
2230 * This function returns the bandwidth string for the passed chanspec.
2231 *
2232 * @param chspec input chanspec
2233 *
2234 * @return Returns the bandwidth string:
2235 * "320", "160+160", "20", "40", "80", "160", "80+80", "240"
2236 */
2237 const char *
BCMRAMFN(wf_chspec_to_bw_str)2238 BCMRAMFN(wf_chspec_to_bw_str)(chanspec_t chspec)
2239 {
2240 return wf_chspec_bw_str[(CHSPEC_BW(chspec) >> WL_CHANSPEC_BW_SHIFT)];
2241 }
2242
2243 /**
2244 * Return the primary 20MHz chanspec of a given chanspec
2245 *
2246 * This function returns the chanspec of the primary 20MHz channel. For 20MHz
2247 * channels this is just the chanspec. For 40MHz or wider channels it is the
2248 * chanspec of the primary 20MHz channel specified by the chanspec.
2249 *
2250 * @param chspec input chanspec
2251 *
2252 * @return Returns the chanspec of the primary 20MHz channel
2253 */
2254 chanspec_t
wf_chspec_primary20_chspec(chanspec_t chspec)2255 wf_chspec_primary20_chspec(chanspec_t chspec)
2256 {
2257 chanspec_t pri_chspec = chspec;
2258 uint8 pri_chan;
2259
2260 ASSERT(!wf_chspec_malformed(chspec));
2261
2262 /* Is there a sideband ? */
2263 if (!CHSPEC_IS20(chspec)) {
2264 pri_chan = wf_chspec_primary20_chan(chspec);
2265 pri_chspec = pri_chan | WL_CHANSPEC_BW_20;
2266 pri_chspec |= CHSPEC_BAND(chspec);
2267 }
2268 return pri_chspec;
2269 }
2270
2271 /* return chanspec given primary 20MHz channel and bandwidth
2272 * return 0 on error
2273 * does not support 6G
2274 */
2275 uint16
wf_channel2chspec(uint pri_ch,uint bw)2276 wf_channel2chspec(uint pri_ch, uint bw)
2277 {
2278 uint16 chspec;
2279 const uint8 *center_ch = NULL;
2280 int num_ch = 0;
2281 int sb = -1;
2282 int i = 0;
2283
2284 chspec = ((pri_ch <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
2285
2286 chspec |= bw;
2287
2288 if (bw == WL_CHANSPEC_BW_40) {
2289 if (pri_ch <= CH_MAX_2G_CHANNEL) {
2290 center_ch = wf_2g_40m_chans;
2291 num_ch = WF_NUM_2G_40M_CHANS;
2292 } else {
2293 center_ch = wf_5g_40m_chans;
2294 num_ch = WF_NUM_5G_40M_CHANS;
2295 }
2296 } else if (bw == WL_CHANSPEC_BW_80) {
2297 center_ch = wf_5g_80m_chans;
2298 num_ch = WF_NUM_5G_80M_CHANS;
2299 } else if (bw == WL_CHANSPEC_BW_160) {
2300 center_ch = wf_5g_160m_chans;
2301 num_ch = WF_NUM_5G_160M_CHANS;
2302 } else if (bw == WL_CHANSPEC_BW_20) {
2303 chspec |= pri_ch;
2304 return chspec;
2305 } else {
2306 return 0;
2307 }
2308
2309 for (i = 0; i < num_ch; i ++) {
2310 sb = channel_to_sb(center_ch[i], pri_ch, (chanspec_bw_t)bw);
2311 if (sb >= 0) {
2312 chspec |= center_ch[i];
2313 chspec |= (sb << WL_CHANSPEC_CTL_SB_SHIFT);
2314 break;
2315 }
2316 }
2317
2318 /* check for no matching sb/center */
2319 if (sb < 0) {
2320 return 0;
2321 }
2322
2323 return chspec;
2324 }
2325
2326 /**
2327 * Return the primary 40MHz chanspec or a 40MHz or wider channel
2328 *
2329 * This function returns the chanspec for the primary 40MHz of an 80MHz or wider channel.
2330 * The primary 40MHz channel is the 40MHz sub-band that contains the primary 20MHz channel.
2331 * The primary 20MHz channel of the returned 40MHz chanspec is the same as the primary 20MHz
2332 * channel of the input chanspec.
2333 *
2334 * @param chspec input chanspec
2335 *
2336 * @return Returns the chanspec of the primary 20MHz channel
2337 */
2338 chanspec_t
wf_chspec_primary40_chspec(chanspec_t chspec)2339 wf_chspec_primary40_chspec(chanspec_t chspec)
2340 {
2341 chanspec_t chspec40 = chspec;
2342 uint center_chan;
2343 uint sb;
2344
2345 ASSERT(!wf_chspec_malformed(chspec));
2346
2347 /* if the chanspec is > 80MHz, use the helper routine to find the primary 80 MHz channel */
2348 if (CHSPEC_IS160(chspec)) {
2349 chspec = wf_chspec_primary80_chspec(chspec);
2350 }
2351
2352 /* determine primary 40 MHz sub-channel of an 80 MHz chanspec */
2353 if (CHSPEC_IS80(chspec)) {
2354 center_chan = CHSPEC_CHANNEL(chspec);
2355 sb = CHSPEC_CTL_SB(chspec);
2356
2357 if (sb < WL_CHANSPEC_CTL_SB_UL) {
2358 /* Primary 40MHz is on lower side */
2359 center_chan -= CH_20MHZ_APART;
2360 /* sideband bits are the same for LL/LU and L/U */
2361 } else {
2362 /* Primary 40MHz is on upper side */
2363 center_chan += CH_20MHZ_APART;
2364 /* sideband bits need to be adjusted by UL offset */
2365 sb -= WL_CHANSPEC_CTL_SB_UL;
2366 }
2367
2368 /* Create primary 40MHz chanspec */
2369 chspec40 = (CHSPEC_BAND(chspec) | WL_CHANSPEC_BW_40 |
2370 sb | center_chan);
2371 }
2372
2373 return chspec40;
2374 }
2375
2376 /**
2377 * Return the channel number for a given frequency and base frequency.
2378 *
2379 * @param freq frequency in MHz of the channel center
2380 * @param start_factor starting base frequency in 500 KHz units
2381 *
2382 * @return Returns a channel number > 0, or -1 on error
2383 *
2384 * The returned channel number is relative to the given base frequency.
2385 *
2386 * The base frequency is specified as (start_factor * 500 kHz).
2387 * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G, and WF_CHAN_FACTOR_6_G are
2388 * defined for 2.4 GHz, 5 GHz, and 6 GHz bands.
2389 *
2390 * If the given base frequency is zero these base frequencies are assumed:
2391 *
2392 * freq (GHz) -> assumed base freq (GHz)
2393 * 2G band 2.4 - 2.5 2.407
2394 * 5G band 5.0 - 5.940 5.000
2395 * 6G band 5.940 - 7.205 5.940
2396 *
2397 * It is an error if the start_factor is zero and the freq is not in one of
2398 * these ranges.
2399 *
2400 * The returned channel will be in the range [1, 14] in the 2.4 GHz band,
2401 * [1, 253] for 6 GHz band, or [1, 200] otherwise.
2402 *
2403 * It is an error if the start_factor is WF_CHAN_FACTOR_2_4_G and the
2404 * frequency is not a 2.4 GHz channel. For any other start factor the frequency
2405 * must be an even 5 MHz multiple greater than the base frequency.
2406 *
2407 * For a start_factor WF_CHAN_FACTOR_6_G, the frequency may be up to 7.205 MHz
2408 * (channel 253). For any other start_factor, the frequence can be up to
2409 * 1 GHz from the base freqency (channel 200).
2410 *
2411 * Reference 802.11-2016, section 17.3.8.3 and section 16.3.6.3
2412 */
2413 int
wf_mhz2channel(uint freq,uint start_factor)2414 wf_mhz2channel(uint freq, uint start_factor)
2415 {
2416 int ch = -1;
2417 uint base;
2418 int offset;
2419
2420 /* take the default channel start frequency */
2421 if (start_factor == 0) {
2422 if (freq >= 2400 && freq <= 2500) {
2423 start_factor = WF_CHAN_FACTOR_2_4_G;
2424 } else if (freq >= 5000 && freq < 5935) {
2425 start_factor = WF_CHAN_FACTOR_5_G;
2426 } else if (freq >= 5935 && freq <= 7205) {
2427 start_factor = WF_CHAN_FACTOR_6_G;
2428 }
2429 }
2430
2431 if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G) {
2432 return 14;
2433 } else if (freq == 5935 && start_factor == WF_CHAN_FACTOR_6_G) {
2434 /* channel #2 is an oddball, 10MHz below chan #1 */
2435 return 2;
2436 } else if (freq == 5960 && start_factor == WF_CHAN_FACTOR_6_G) {
2437 /* do not return ch #2 for the convetional location that #2 would appear */
2438 return -1;
2439 }
2440
2441 base = start_factor / 2;
2442
2443 if (freq < base) {
2444 return -1;
2445 }
2446
2447 offset = freq - base;
2448 ch = offset / 5;
2449
2450 /* check that frequency is a 5MHz multiple from the base */
2451 if (offset != (ch * 5))
2452 return -1;
2453
2454 /* channel range checks */
2455 if (start_factor == WF_CHAN_FACTOR_2_4_G) {
2456 /* 2G should only be up to 13 here as 14 is
2457 * handled above as it is a non-5MHz offset
2458 */
2459 if (ch > 13) {
2460 ch = -1;
2461 }
2462 }
2463 else if (start_factor == WF_CHAN_FACTOR_6_G) {
2464 /* 6G has a higher channel range than 5G channelization specifies [1,200] */
2465 if ((uint)ch > CH_MAX_6G_CHANNEL) {
2466 ch = -1;
2467 }
2468 } else if (ch > 200) {
2469 ch = -1;
2470 }
2471
2472 return ch;
2473 }
2474
2475 /**
2476 * Return the center frequency in MHz of the given channel and base frequency.
2477 *
2478 * The channel number is interpreted relative to the given base frequency.
2479 *
2480 * The valid channel range is [1, 14] in the 2.4 GHz band, [1,253] in the 6 GHz
2481 * band, and [1, 200] otherwise.
2482 * The base frequency is specified as (start_factor * 500 kHz).
2483 * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G, and WF_CHAN_FACTOR_6_G are
2484 * defined for 2.4 GHz, 5 GHz, and 6 GHz bands.
2485 * The channel range of [1, 14] is only checked for a start_factor of
2486 * WF_CHAN_FACTOR_2_4_G (4814).
2487 * Odd start_factors produce channels on .5 MHz boundaries, in which case
2488 * the answer is rounded down to an integral MHz.
2489 * -1 is returned for an out of range channel.
2490 *
2491 * Reference 802.11-2016, section 17.3.8.3 and section 16.3.6.3
2492 *
2493 * @param channel input channel number
2494 * @param start_factor base frequency in 500 kHz units, e.g. 10000 for 5 GHz
2495 *
2496 * @return Returns a frequency in MHz
2497 *
2498 * @see WF_CHAN_FACTOR_2_4_G
2499 * @see WF_CHAN_FACTOR_5_G
2500 * @see WF_CHAN_FACTOR_6_G
2501 */
2502 int
wf_channel2mhz(uint ch,uint start_factor)2503 wf_channel2mhz(uint ch, uint start_factor)
2504 {
2505 int freq;
2506
2507 if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) ||
2508 (start_factor == WF_CHAN_FACTOR_6_G && (ch < 1 || ch > 253)) ||
2509 (start_factor != WF_CHAN_FACTOR_6_G && (ch < 1 || ch > 200))) {
2510 freq = -1;
2511 } else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14)) {
2512 freq = 2484;
2513 } else if ((start_factor == WF_CHAN_FACTOR_6_G) && (ch == 2)) {
2514 freq = 5935;
2515 } else {
2516 freq = ch * 5 + start_factor / 2;
2517 }
2518
2519 return freq;
2520 }
2521
2522 static const uint16 sidebands[] = {
2523 WL_CHANSPEC_CTL_SB_LLL, WL_CHANSPEC_CTL_SB_LLU,
2524 WL_CHANSPEC_CTL_SB_LUL, WL_CHANSPEC_CTL_SB_LUU,
2525 WL_CHANSPEC_CTL_SB_ULL, WL_CHANSPEC_CTL_SB_ULU,
2526 WL_CHANSPEC_CTL_SB_UUL, WL_CHANSPEC_CTL_SB_UUU
2527 };
2528
2529 /*
2530 * Returns the chanspec 80Mhz channel corresponding to the following input
2531 * parameters
2532 *
2533 * primary_channel - primary 20Mhz channel
2534 * center_channel - center frequecny of the 80Mhz channel
2535 *
2536 * The center_channel can be one of {42, 58, 106, 122, 138, 155}
2537 *
2538 * returns INVCHANSPEC in case of error
2539 *
2540 * does not support 6G
2541 */
2542 chanspec_t
wf_chspec_80(uint8 center_channel,uint8 primary_channel)2543 wf_chspec_80(uint8 center_channel, uint8 primary_channel)
2544 {
2545
2546 chanspec_t chanspec = INVCHANSPEC;
2547 chanspec_t chanspec_cur;
2548 uint i;
2549
2550 for (i = 0; i < WF_NUM_SIDEBANDS_80MHZ; i++) {
2551 chanspec_cur = CH80MHZ_CHSPEC(center_channel, sidebands[i]);
2552 if (primary_channel == wf_chspec_primary20_chan(chanspec_cur)) {
2553 chanspec = chanspec_cur;
2554 break;
2555 }
2556 }
2557 /* If the loop ended early, we are good, otherwise we did not
2558 * find a 80MHz chanspec with the given center_channel that had a primary channel
2559 *matching the given primary_channel.
2560 */
2561 return chanspec;
2562 }
2563
2564 /*
2565 * Returns the 80+80 chanspec corresponding to the following input parameters
2566 *
2567 * primary_20mhz - Primary 20 MHz channel
2568 * chan0 - center channel number of one frequency segment
2569 * chan1 - center channel number of the other frequency segment
2570 *
2571 * Parameters chan0 and chan1 are channel numbers in {42, 58, 106, 122, 138, 155}.
2572 * The primary channel must be contained in one of the 80MHz channels. This routine
2573 * will determine which frequency segment is the primary 80 MHz segment.
2574 *
2575 * Returns INVCHANSPEC in case of error.
2576 *
2577 * Refer to 802.11-2016 section 22.3.14 "Channelization".
2578 *
2579 * does not support 6G
2580 */
2581 chanspec_t
wf_chspec_get8080_chspec(uint8 primary_20mhz,uint8 chan0,uint8 chan1)2582 wf_chspec_get8080_chspec(uint8 primary_20mhz, uint8 chan0, uint8 chan1)
2583 {
2584 int sb = 0;
2585 uint16 chanspec = 0;
2586 int chan0_id = 0, chan1_id = 0;
2587 int seg0, seg1;
2588
2589 chan0_id = channel_80mhz_to_id(chan0);
2590 chan1_id = channel_80mhz_to_id(chan1);
2591
2592 /* make sure the channel numbers were valid */
2593 if (chan0_id == -1 || chan1_id == -1)
2594 return INVCHANSPEC;
2595
2596 /* does the primary channel fit with the 1st 80MHz channel ? */
2597 sb = channel_to_sb(chan0, primary_20mhz, WL_CHANSPEC_BW_80);
2598 if (sb >= 0) {
2599 /* yes, so chan0 is frequency segment 0, and chan1 is seg 1 */
2600 seg0 = chan0_id;
2601 seg1 = chan1_id;
2602 } else {
2603 /* no, so does the primary channel fit with the 2nd 80MHz channel ? */
2604 sb = channel_to_sb(chan1, primary_20mhz, WL_CHANSPEC_BW_80);
2605 if (sb < 0) {
2606 /* no match for pri_ch to either 80MHz center channel */
2607 return INVCHANSPEC;
2608 }
2609 /* swapped, so chan1 is frequency segment 0, and chan0 is seg 1 */
2610 seg0 = chan1_id;
2611 seg1 = chan0_id;
2612 }
2613
2614 chanspec = ((seg0 << WL_CHANSPEC_CHAN0_SHIFT) |
2615 (seg1 << WL_CHANSPEC_CHAN1_SHIFT) |
2616 (sb << WL_CHANSPEC_CTL_SB_SHIFT) |
2617 WL_CHANSPEC_BW_8080 |
2618 WL_CHANSPEC_BAND_5G);
2619
2620 return chanspec;
2621 }
2622
2623 /*
2624 * Returns the center channel of the primary 80 MHz sub-band of the provided chanspec
2625 */
2626 uint8
wf_chspec_primary80_channel(chanspec_t chanspec)2627 wf_chspec_primary80_channel(chanspec_t chanspec)
2628 {
2629 chanspec_t primary80_chspec;
2630 uint8 primary80_chan;
2631
2632 primary80_chspec = wf_chspec_primary80_chspec(chanspec);
2633
2634 if (primary80_chspec == INVCHANSPEC) {
2635 primary80_chan = INVCHANNEL;
2636 } else {
2637 primary80_chan = CHSPEC_CHANNEL(primary80_chspec);
2638 }
2639
2640 return primary80_chan;
2641 }
2642
2643 /*
2644 * Returns the center channel of the secondary 80 MHz sub-band of the provided chanspec
2645 */
2646 uint8
wf_chspec_secondary80_channel(chanspec_t chanspec)2647 wf_chspec_secondary80_channel(chanspec_t chanspec)
2648 {
2649 chanspec_t secondary80_chspec;
2650 uint8 secondary80_chan;
2651
2652 secondary80_chspec = wf_chspec_secondary80_chspec(chanspec);
2653
2654 if (secondary80_chspec == INVCHANSPEC) {
2655 secondary80_chan = INVCHANNEL;
2656 } else {
2657 secondary80_chan = CHSPEC_CHANNEL(secondary80_chspec);
2658 }
2659
2660 return secondary80_chan;
2661 }
2662
2663 /*
2664 * Returns the chanspec for the primary 80MHz sub-band of an 160MHz or 80+80 channel
2665 */
2666 chanspec_t
wf_chspec_primary80_chspec(chanspec_t chspec)2667 wf_chspec_primary80_chspec(chanspec_t chspec)
2668 {
2669 chanspec_t chspec80;
2670 uint center_chan;
2671 uint sb;
2672
2673 ASSERT(!wf_chspec_malformed(chspec));
2674
2675 if (CHSPEC_IS80(chspec)) {
2676 chspec80 = chspec;
2677 } else if (CHSPEC_IS160(chspec)) {
2678 center_chan = CHSPEC_CHANNEL(chspec);
2679 sb = CHSPEC_CTL_SB(chspec);
2680
2681 if (sb < WL_CHANSPEC_CTL_SB_ULL) {
2682 /* Primary 80MHz is on lower side */
2683 center_chan -= CH_40MHZ_APART;
2684 }
2685 else {
2686 /* Primary 80MHz is on upper side */
2687 center_chan += CH_40MHZ_APART;
2688 sb -= WL_CHANSPEC_CTL_SB_ULL;
2689 }
2690
2691 /* Create primary 80MHz chanspec */
2692 chspec80 = (CHSPEC_BAND(chspec) | WL_CHANSPEC_BW_80 | sb | center_chan);
2693 }
2694 else {
2695 chspec80 = INVCHANSPEC;
2696 }
2697
2698 return chspec80;
2699 }
2700
2701 /*
2702 * Returns the chanspec for the secondary 80MHz sub-band of an 160MHz or 80+80 channel
2703 */
2704 chanspec_t
wf_chspec_secondary80_chspec(chanspec_t chspec)2705 wf_chspec_secondary80_chspec(chanspec_t chspec)
2706 {
2707 chanspec_t chspec80;
2708 uint center_chan;
2709
2710 ASSERT(!wf_chspec_malformed(chspec));
2711
2712 if (CHSPEC_IS160(chspec)) {
2713 center_chan = CHSPEC_CHANNEL(chspec);
2714
2715 if (CHSPEC_CTL_SB(chspec) < WL_CHANSPEC_CTL_SB_ULL) {
2716 /* Primary 80MHz is on lower side, so the secondary is on
2717 * the upper side
2718 */
2719 center_chan += CH_40MHZ_APART;
2720 } else {
2721 /* Primary 80MHz is on upper side, so the secondary is on
2722 * the lower side
2723 */
2724 center_chan -= CH_40MHZ_APART;
2725 }
2726
2727 /* Create secondary 80MHz chanspec */
2728 chspec80 = (CHSPEC_BAND(chspec) |
2729 WL_CHANSPEC_BW_80 |
2730 WL_CHANSPEC_CTL_SB_LL |
2731 center_chan);
2732 }
2733 else {
2734 chspec80 = INVCHANSPEC;
2735 }
2736
2737 return chspec80;
2738 }
2739
2740 /*
2741 * For 160MHz or 80P80 chanspec, set ch[0]/ch[1] to be the low/high 80 Mhz channels
2742 *
2743 * For 20/40/80MHz chanspec, set ch[0] to be the center freq, and chan[1]=-1
2744 */
2745 void
wf_chspec_get_80p80_channels(chanspec_t chspec,uint8 * ch)2746 wf_chspec_get_80p80_channels(chanspec_t chspec, uint8 *ch)
2747 {
2748
2749 if (CHSPEC_IS160(chspec)) {
2750 uint8 center_chan = CHSPEC_CHANNEL(chspec);
2751 ch[0] = center_chan - CH_40MHZ_APART;
2752 ch[1] = center_chan + CH_40MHZ_APART;
2753 }
2754 else {
2755 /* for 20, 40, and 80 Mhz */
2756 ch[0] = CHSPEC_CHANNEL(chspec);
2757 ch[1] = -1;
2758 }
2759 return;
2760
2761 }
2762
2763 /*
2764 * Returns the center channel of the primary 160MHz sub-band of the provided chanspec
2765 */
2766 uint8
wf_chspec_primary160_channel(chanspec_t chanspec)2767 wf_chspec_primary160_channel(chanspec_t chanspec)
2768 {
2769 chanspec_t primary160_chspec;
2770 uint8 primary160_chan;
2771
2772 primary160_chspec = wf_chspec_primary160_chspec(chanspec);
2773
2774 if (primary160_chspec == INVCHANSPEC) {
2775 primary160_chan = INVCHANNEL;
2776 } else {
2777 primary160_chan = CHSPEC_CHANNEL(primary160_chspec);
2778 }
2779
2780 return primary160_chan;
2781 }
2782
2783 /*
2784 * Returns the chanspec for the primary 160MHz sub-band of an 240/320MHz or 160+160 channel
2785 */
2786 chanspec_t
wf_chspec_primary160_chspec(chanspec_t chspec)2787 wf_chspec_primary160_chspec(chanspec_t chspec)
2788 {
2789 chanspec_t chspec160;
2790 uint center_chan;
2791 uint sb;
2792
2793 ASSERT(!wf_chspec_malformed(chspec));
2794
2795 if (CHSPEC_IS160(chspec)) {
2796 chspec160 = chspec;
2797 }
2798 else if (CHSPEC_IS240(chspec)) {
2799 uint8 ch_id = CHSPEC_GE240_CHAN(chspec);
2800 center_chan = wf_chspec_240_id2cch(chspec);
2801 sb = CHSPEC_GE240_SB(chspec) >> WL_CHANSPEC_GE240_SB_SHIFT;
2802 /*
2803 * Identify the chanspec is of the form 160+80 or 80+160 from the channel ID.
2804 * Channel ID : even for 160+80 and odd for 80+160
2805 */
2806 if ((!(ch_id & 0x1u)) && (sb < 8u)) {
2807 /* Primary 160MHz is on lower side */
2808 center_chan -= CH_40MHZ_APART;
2809 } else if ((ch_id & 0x1u) && (sb >= 4u)) {
2810 /* Primary 160MHz is on upper side */
2811 center_chan += CH_40MHZ_APART;
2812 sb -= 4u;
2813 } else {
2814 chspec160 = INVCHANSPEC;
2815 goto done;
2816 }
2817
2818 /* Create primary 160MHz chanspec */
2819 chspec160 = (CHSPEC_BAND(chspec) |
2820 WL_CHANSPEC_BW_160 |
2821 (sb << WL_CHANSPEC_CTL_SB_SHIFT) |
2822 center_chan);
2823 } else if (CHSPEC_IS320(chspec)) {
2824 center_chan = wf_chspec_320_id2cch(chspec);
2825 sb = CHSPEC_GE240_SB(chspec) >> WL_CHANSPEC_GE240_SB_SHIFT;
2826
2827 if (sb < 8u) {
2828 /* Primary 160MHz is on lower side */
2829 center_chan -= CH_80MHZ_APART;
2830 }
2831 else {
2832 /* Primary 160MHz is on upper side */
2833 center_chan += CH_80MHZ_APART;
2834 sb -= 8u;
2835 }
2836
2837 /* Create primary 160MHz chanspec */
2838 chspec160 = (CHSPEC_BAND(chspec) |
2839 WL_CHANSPEC_BW_160 |
2840 (sb << WL_CHANSPEC_CTL_SB_SHIFT) |
2841 center_chan);
2842 }
2843 else {
2844 chspec160 = INVCHANSPEC;
2845 }
2846 done:
2847 return chspec160;
2848 }
2849
2850 /* Populates array with all 20MHz side bands of a given chanspec_t in the following order:
2851 * primary20, secondary20, two secondary40s, four secondary80s.
2852 * 'chspec' is the chanspec of interest
2853 * 'pext' must point to an uint8 array of long enough to hold all side bands of the given chspec
2854 *
2855 * Works with 20, 40, 80, and 160MHz chspec
2856 */
2857 void
wf_get_all_ext(chanspec_t chspec,uint8 * pext)2858 wf_get_all_ext(chanspec_t chspec, uint8 *pext)
2859 {
2860 chanspec_t t = (CHSPEC_IS160(chspec)) ? /* if bw > 80MHz */
2861 wf_chspec_primary80_chspec(chspec) : (chspec); /* extract primary 80 */
2862 /* primary20 channel as first element */
2863 uint8 pri_ch = (pext)[0] = wf_chspec_primary20_chan(t);
2864
2865 if (CHSPEC_IS20(chspec)) {
2866 return; /* nothing more to do since 20MHz chspec */
2867 }
2868 /* 20MHz EXT */
2869 (pext)[1] = (IS_CTL_IN_L20(t) ? pri_ch + CH_20MHZ_APART : pri_ch - CH_20MHZ_APART);
2870
2871 if (CHSPEC_IS40(chspec)) {
2872 return; /* nothing more to do since 40MHz chspec */
2873 }
2874 /* center 40MHz EXT */
2875 t = wf_channel2chspec((IS_CTL_IN_L40(chspec) ?
2876 pri_ch + CH_40MHZ_APART : pri_ch - CH_40MHZ_APART), WL_CHANSPEC_BW_40);
2877 GET_ALL_SB(t, &((pext)[2])); /* get the 20MHz side bands in 40MHz EXT */
2878
2879 if (CHSPEC_IS80(chspec)) {
2880 return; /* nothing more to do since 80MHz chspec */
2881 }
2882 t = CH80MHZ_CHSPEC(wf_chspec_secondary80_channel(chspec), WL_CHANSPEC_CTL_SB_LLL);
2883 /* get the 20MHz side bands in 80MHz EXT (secondary) */
2884 GET_ALL_SB(t, &((pext)[4]));
2885 }
2886
2887 /*
2888 * Given two chanspecs, returns true if they overlap.
2889 * (Overlap: At least one 20MHz subband is common between the two chanspecs provided)
2890 */
wf_chspec_overlap(chanspec_t chspec0,chanspec_t chspec1)2891 bool wf_chspec_overlap(chanspec_t chspec0, chanspec_t chspec1)
2892 {
2893 uint8 ch0, ch1;
2894
2895 if (CHSPEC_BAND(chspec0) != CHSPEC_BAND(chspec1)) {
2896 return FALSE;
2897 }
2898
2899 FOREACH_20_SB(chspec0, ch0) {
2900 FOREACH_20_SB(chspec1, ch1) {
2901 if ((uint)ABS(ch0 - ch1) < CH_20MHZ_APART) {
2902 return TRUE;
2903 }
2904 }
2905 }
2906
2907 return FALSE;
2908 }
2909
2910 uint8
channel_bw_to_width(chanspec_t chspec)2911 channel_bw_to_width(chanspec_t chspec)
2912 {
2913 uint8 channel_width;
2914
2915 if (CHSPEC_IS80(chspec))
2916 channel_width = VHT_OP_CHAN_WIDTH_80;
2917 else if (CHSPEC_IS160(chspec))
2918 channel_width = VHT_OP_CHAN_WIDTH_160;
2919 else
2920 channel_width = VHT_OP_CHAN_WIDTH_20_40;
2921
2922 return channel_width;
2923 }
2924
wf_chspec_first_20_sb(chanspec_t chspec)2925 uint wf_chspec_first_20_sb(chanspec_t chspec)
2926 {
2927 #if defined(WL_BW160MHZ)
2928 if (CHSPEC_IS160(chspec)) {
2929 return LLL_20_SB_160(CHSPEC_CHANNEL(chspec));
2930 } else
2931 #endif
2932 if (CHSPEC_IS80(chspec)) {
2933 return LL_20_SB(CHSPEC_CHANNEL(chspec));
2934 } else if (CHSPEC_IS40(chspec)) {
2935 return LOWER_20_SB(CHSPEC_CHANNEL(chspec));
2936 } else {
2937 return CHSPEC_CHANNEL(chspec);
2938 }
2939 }
2940
2941 chanspec_t
wf_create_chspec_sb(uint sb,uint center_channel,chanspec_bw_t bw,chanspec_band_t band)2942 wf_create_chspec_sb(uint sb, uint center_channel, chanspec_bw_t bw, chanspec_band_t band)
2943 {
2944 chanspec_t chspec;
2945 if (sb > (WL_CHANSPEC_CTL_SB_MASK >> WL_CHANSPEC_CTL_SB_SHIFT)) {
2946 return INVCHANSPEC;
2947 }
2948 chspec = center_channel | band | bw | ((uint)sb << WL_CHANSPEC_CTL_SB_SHIFT);
2949 return wf_chspec_valid(chspec) ? chspec : INVCHANSPEC;
2950 }
2951
2952 chanspec_t
wf_create_160160MHz_chspec_sb(uint sb,uint chan0,uint chan1,chanspec_band_t band)2953 wf_create_160160MHz_chspec_sb(uint sb, uint chan0, uint chan1, chanspec_band_t band)
2954 {
2955 int chan0_id, chan1_id, seg0, seg1;
2956 chanspec_t chspec;
2957
2958 if (sb > (WL_CHANSPEC_CTL_SB_UUU >> WL_CHANSPEC_CTL_SB_SHIFT)) {
2959 return INVCHANSPEC;
2960 }
2961 /* From here on sb is not an index, but value for SB field */
2962 sb <<= WL_CHANSPEC_CTL_SB_SHIFT;
2963
2964 /* frequency segments need to be non-contiguous, so the channel
2965 * separation needs to be greater than 160MHz
2966 */
2967 if ((uint)ABS((int)(chan0 - chan1)) <= CH_160MHZ_APART) {
2968 return INVCHANSPEC;
2969 }
2970
2971 if (band == WL_CHANSPEC_BAND_5G) {
2972 chan0_id = channel_5g_160mhz_to_id(chan0);
2973 chan1_id = channel_5g_160mhz_to_id(chan1);
2974 } else if (band == WL_CHANSPEC_BAND_6G) {
2975 chan0_id = channel_6g_160mhz_to_id(chan0);
2976 chan1_id = channel_6g_160mhz_to_id(chan1);
2977 } else {
2978 return INVCHANSPEC;
2979 }
2980
2981 /* make sure the channel numbers were valid */
2982 if ((chan0_id == -1) || (chan1_id == -1)) {
2983 return INVCHANSPEC;
2984 }
2985 /* Optionally swapping channel IDs to make sure that control subchannel
2986 * is in chan0
2987 */
2988 if (sb < WL_CHANSPEC_CTL_SB_ULL) {
2989 seg0 = chan0_id;
2990 seg1 = chan1_id;
2991 } else {
2992 seg0 = chan1_id;
2993 seg1 = chan0_id;
2994 sb -= WL_CHANSPEC_CTL_SB_ULL;
2995 }
2996 chspec = ((seg0 << WL_CHANSPEC_CHAN0_SHIFT) |
2997 (seg1 << WL_CHANSPEC_CHAN1_SHIFT) |
2998 sb | WL_CHANSPEC_BW_160160 | band);
2999 return wf_chspec_valid(chspec) ? chspec : INVCHANSPEC;
3000 }
3001