xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/bcmwifi_channels.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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