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