CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
orangepi-xunlong

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: orangepi-xunlong/orangepi-build
Path: blob/next/external/cache/sources/wl/shared/bcmwifi_channels.c
Views: 3959
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 Open Broadcom Corporation$
7
* $Id: bcmwifi_channels.c 309193 2012-01-19 00:03:57Z stafford $
8
*/
9
10
#include <bcm_cfg.h>
11
#include <typedefs.h>
12
#include <bcmutils.h>
13
14
#ifdef BCMDRIVER
15
#include <osl.h>
16
#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
17
#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
18
#elif defined(__IOPOS__)
19
#include <bcmutils.h>
20
#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
21
#define tolower(c) (bcm_tolower((c)))
22
/* XXX Why isn't ASSERT always available as part of a non BCMDRIVER build?
23
* It seems like there ought to be a non BCMDRIVER osl.
24
*/
25
#ifndef ASSERT
26
#define ASSERT(exp)
27
#endif
28
#else
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <ctype.h>
32
/* XXX Why isn't ASSERT always available as part of a non BCMDRIVER build?
33
* It seems like there ought to be a non BCMDRIVER osl.
34
*/
35
#ifndef ASSERT
36
#define ASSERT(exp)
37
#endif
38
#endif /* BCMDRIVER */
39
40
#ifdef _bcmwifi_c_
41
/* temporary for transitional compatibility */
42
#include <bcmwifi.h>
43
#else
44
#include <bcmwifi_channels.h>
45
#endif
46
47
#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL))
48
#include <bcmstdlib.h> /* For wl/exe/GNUmakefile.brcm_wlu and GNUmakefile.wlm_dll */
49
#endif
50
51
#ifndef D11AC_IOTYPES
52
53
/* Definitions for legacy Chanspec type */
54
55
/* Chanspec ASCII representation:
56
* <channel><band><bandwidth><ctl-sideband>
57
* digit [AB] [N] [UL]
58
*
59
* <channel>: channel number of the 10MHz or 20MHz channel,
60
* or control sideband channel of 40MHz channel.
61
* <band>: A for 5GHz, B for 2.4GHz
62
* <bandwidth>: N for 10MHz, nothing for 20MHz or 40MHz
63
* (ctl-sideband spec implies 40MHz)
64
* <ctl-sideband>: U for upper, L for lower
65
*
66
* <band> may be omitted on input, and will be assumed to be
67
* 2.4GHz if channel number <= 14.
68
*
69
* Examples:
70
* 8 -> 2.4GHz channel 8, 20MHz
71
* 8b -> 2.4GHz channel 8, 20MHz
72
* 8l -> 2.4GHz channel 8, 40MHz, lower ctl sideband
73
* 8a -> 5GHz channel 8 (low 5 GHz band), 20MHz
74
* 36 -> 5GHz channel 36, 20MHz
75
* 36l -> 5GHz channel 36, 40MHz, lower ctl sideband
76
* 40u -> 5GHz channel 40, 40MHz, upper ctl sideband
77
* 180n -> channel 180, 10MHz
78
*/
79
80
81
/* given a chanspec and a string buffer, format the chanspec as a
82
* string, and return the original pointer a.
83
* Min buffer length must be CHANSPEC_STR_LEN.
84
* On error return NULL
85
*/
86
char *
87
wf_chspec_ntoa(chanspec_t chspec, char *buf)
88
{
89
const char *band, *bw, *sb;
90
uint channel;
91
92
band = "";
93
bw = "";
94
sb = "";
95
channel = CHSPEC_CHANNEL(chspec);
96
/* check for non-default band spec */
97
if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) ||
98
(CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL))
99
band = (CHSPEC_IS2G(chspec)) ? "b" : "a";
100
if (CHSPEC_IS40(chspec)) {
101
if (CHSPEC_SB_UPPER(chspec)) {
102
sb = "u";
103
channel += CH_10MHZ_APART;
104
} else {
105
sb = "l";
106
channel -= CH_10MHZ_APART;
107
}
108
} else if (CHSPEC_IS10(chspec)) {
109
bw = "n";
110
}
111
112
/* Outputs a max of 6 chars including '\0' */
113
snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb);
114
return (buf);
115
}
116
117
/* given a chanspec string, convert to a chanspec.
118
* On error return 0
119
*/
120
chanspec_t
121
wf_chspec_aton(const char *a)
122
{
123
char *endp = NULL;
124
uint channel, band, bw, ctl_sb;
125
char c;
126
127
channel = strtoul(a, &endp, 10);
128
129
/* check for no digits parsed */
130
if (endp == a)
131
return 0;
132
133
if (channel > MAXCHANNEL)
134
return 0;
135
136
band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
137
bw = WL_CHANSPEC_BW_20;
138
ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
139
140
a = endp;
141
142
c = tolower(a[0]);
143
if (c == '\0')
144
goto done;
145
146
/* parse the optional ['A' | 'B'] band spec */
147
if (c == 'a' || c == 'b') {
148
band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;
149
a++;
150
c = tolower(a[0]);
151
if (c == '\0')
152
goto done;
153
}
154
155
/* parse bandwidth 'N' (10MHz) or 40MHz ctl sideband ['L' | 'U'] */
156
if (c == 'n') {
157
bw = WL_CHANSPEC_BW_10;
158
} else if (c == 'l') {
159
bw = WL_CHANSPEC_BW_40;
160
ctl_sb = WL_CHANSPEC_CTL_SB_LOWER;
161
/* adjust channel to center of 40MHz band */
162
if (channel <= (MAXCHANNEL - CH_20MHZ_APART))
163
channel += CH_10MHZ_APART;
164
else
165
return 0;
166
} else if (c == 'u') {
167
bw = WL_CHANSPEC_BW_40;
168
ctl_sb = WL_CHANSPEC_CTL_SB_UPPER;
169
/* adjust channel to center of 40MHz band */
170
if (channel > CH_20MHZ_APART)
171
channel -= CH_10MHZ_APART;
172
else
173
return 0;
174
} else {
175
return 0;
176
}
177
178
done:
179
return (channel | band | bw | ctl_sb);
180
}
181
182
/*
183
* Verify the chanspec is using a legal set of parameters, i.e. that the
184
* chanspec specified a band, bw, ctl_sb and channel and that the
185
* combination could be legal given any set of circumstances.
186
* RETURNS: TRUE is the chanspec is malformed, false if it looks good.
187
*/
188
bool
189
wf_chspec_malformed(chanspec_t chanspec)
190
{
191
/* must be 2G or 5G band */
192
if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))
193
return TRUE;
194
/* must be 20 or 40 bandwidth */
195
if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))
196
return TRUE;
197
198
/* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */
199
if (CHSPEC_IS20(chanspec)) {
200
if (!CHSPEC_SB_NONE(chanspec))
201
return TRUE;
202
} else {
203
if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec))
204
return TRUE;
205
}
206
207
return FALSE;
208
}
209
210
/*
211
* This function returns the channel number that control traffic is being sent on, for legacy
212
* channels this is just the channel number, for 40MHZ channels it is the upper or lower 20MHZ
213
* sideband depending on the chanspec selected
214
*/
215
uint8
216
wf_chspec_ctlchan(chanspec_t chspec)
217
{
218
uint8 ctl_chan;
219
220
/* Is there a sideband ? */
221
if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) {
222
return CHSPEC_CHANNEL(chspec);
223
} else {
224
/* we only support 40MHZ with sidebands */
225
ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40);
226
/* chanspec channel holds the centre frequency, use that and the
227
* side band information to reconstruct the control channel number
228
*/
229
if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) {
230
/* control chan is the upper 20 MHZ SB of the 40MHZ channel */
231
ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec));
232
} else {
233
ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER);
234
/* control chan is the lower 20 MHZ SB of the 40MHZ channel */
235
ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec));
236
}
237
}
238
239
return ctl_chan;
240
}
241
242
chanspec_t
243
wf_chspec_ctlchspec(chanspec_t chspec)
244
{
245
chanspec_t ctl_chspec = 0;
246
uint8 channel;
247
248
ASSERT(!wf_chspec_malformed(chspec));
249
250
/* Is there a sideband ? */
251
if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) {
252
return chspec;
253
} else {
254
if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) {
255
channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec));
256
} else {
257
channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec));
258
}
259
ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
260
ctl_chspec |= CHSPEC_BAND(chspec);
261
}
262
return ctl_chspec;
263
}
264
265
#else /* D11AC_IOTYPES */
266
267
/* Definitions for D11AC capable Chanspec type */
268
269
/* Chanspec ASCII representation with 802.11ac capability:
270
* [<band> 'g'] <channel> ['/'<bandwidth> [<ctl-sideband>]['/'<1st80channel>'-'<2nd80channel>]]
271
*
272
* <band>:
273
* (optional) 2, 3, 4, 5 for 2.4GHz, 3GHz, 4GHz, and 5GHz respectively.
274
* Default value is 2g if channel <= 14, otherwise 5g.
275
* <channel>:
276
* channel number of the 5MHz, 10MHz, 20MHz channel,
277
* or primary channel of 40MHz, 80MHz, 160MHz, or 80+80MHz channel.
278
* <bandwidth>:
279
* (optional) 5, 10, 20, 40, 80, 160, or 80+80. Default value is 20.
280
* <primary-sideband>:
281
* (only for 2.4GHz band 40MHz) U for upper sideband primary, L for lower.
282
*
283
* For 2.4GHz band 40MHz channels, the same primary channel may be the
284
* upper sideband for one 40MHz channel, and the lower sideband for an
285
* overlapping 40MHz channel. The U/L disambiguates which 40MHz channel
286
* is being specified.
287
*
288
* For 40MHz in the 5GHz band and all channel bandwidths greater than
289
* 40MHz, the U/L specificaion is not allowed since the channels are
290
* non-overlapping and the primary sub-band is derived from its
291
* position in the wide bandwidth channel.
292
*
293
* <1st80Channel>:
294
* <2nd80Channel>:
295
* Required for 80+80, otherwise not allowed.
296
* Specifies the center channel of the first and second 80MHz band.
297
*
298
* In its simplest form, it is a 20MHz channel number, with the implied band
299
* of 2.4GHz if channel number <= 14, and 5GHz otherwise.
300
*
301
* To allow for backward compatibility with scripts, the old form for
302
* 40MHz channels is also allowed: <channel><ctl-sideband>
303
*
304
* <channel>:
305
* primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz
306
* <ctl-sideband>:
307
* "U" for upper, "L" for lower (or lower case "u" "l")
308
*
309
* 5 GHz Examples:
310
* Chanspec BW Center Ch Channel Range Primary Ch
311
* 5g8 20MHz 8 - -
312
* 52 20MHz 52 - -
313
* 52/40 40MHz 54 52-56 52
314
* 56/40 40MHz 54 52-56 56
315
* 52/80 80MHz 58 52-64 52
316
* 56/80 80MHz 58 52-64 56
317
* 60/80 80MHz 58 52-64 60
318
* 64/80 80MHz 58 52-64 64
319
* 52/160 160MHz 50 36-64 52
320
* 36/160 160MGz 50 36-64 36
321
* 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36
322
*
323
* 2 GHz Examples:
324
* Chanspec BW Center Ch Channel Range Primary Ch
325
* 2g8 20MHz 8 - -
326
* 8 20MHz 8 - -
327
* 6 20MHz 6 - -
328
* 6/40l 40MHz 8 6-10 6
329
* 6l 40MHz 8 6-10 6
330
* 6/40u 40MHz 4 2-6 6
331
* 6u 40MHz 4 2-6 6
332
*/
333
334
/* bandwidth ASCII string */
335
static const char *wf_chspec_bw_str[] =
336
{
337
"5",
338
"10",
339
"20",
340
"40",
341
"80",
342
"160",
343
"80+80",
344
"na"
345
};
346
347
static const uint8 wf_chspec_bw_mhz[] =
348
{5, 10, 20, 40, 80, 160, 160};
349
350
#define WF_NUM_BW \
351
(sizeof(wf_chspec_bw_mhz)/sizeof(uint8))
352
353
/* 40MHz channels in 5GHz band */
354
static const uint8 wf_5g_40m_chans[] =
355
{38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159};
356
#define WF_NUM_5G_40M_CHANS \
357
(sizeof(wf_5g_40m_chans)/sizeof(uint8))
358
359
/* 80MHz channels in 5GHz band */
360
static const uint8 wf_5g_80m_chans[] =
361
{42, 58, 106, 122, 138, 155};
362
#define WF_NUM_5G_80M_CHANS \
363
(sizeof(wf_5g_80m_chans)/sizeof(uint8))
364
365
/* 160MHz channels in 5GHz band */
366
static const uint8 wf_5g_160m_chans[] =
367
{50, 114};
368
#define WF_NUM_5G_160M_CHANS \
369
(sizeof(wf_5g_160m_chans)/sizeof(uint8))
370
371
372
/* convert bandwidth from chanspec to MHz */
373
static uint
374
bw_chspec_to_mhz(chanspec_t chspec)
375
{
376
uint bw;
377
378
bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT;
379
return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]);
380
}
381
382
/* bw in MHz, return the channel count from the center channel to the
383
* the channel at the edge of the band
384
*/
385
static uint8
386
center_chan_to_edge(uint bw)
387
{
388
/* edge channels separated by BW - 10MHz on each side
389
* delta from cf to edge is half of that,
390
* MHz to channel num conversion is 5MHz/channel
391
*/
392
return (uint8)(((bw - 20) / 2) / 5);
393
}
394
395
/* return channel number of the low edge of the band
396
* given the center channel and BW
397
*/
398
static uint8
399
channel_low_edge(uint center_ch, uint bw)
400
{
401
return (uint8)(center_ch - center_chan_to_edge(bw));
402
}
403
404
/* return side band number given center channel and control channel
405
* return -1 on error
406
*/
407
static int
408
channel_to_sb(uint center_ch, uint ctl_ch, uint bw)
409
{
410
uint lowest = channel_low_edge(center_ch, bw);
411
uint sb;
412
413
if ((ctl_ch - lowest) % 4) {
414
/* bad ctl channel, not mult 4 */
415
return -1;
416
}
417
418
sb = ((ctl_ch - lowest) / 4);
419
420
/* sb must be a index to a 20MHz channel in range */
421
if (sb >= (bw / 20)) {
422
/* ctl_ch must have been too high for the center_ch */
423
return -1;
424
}
425
426
return sb;
427
}
428
429
/* return control channel given center channel and side band */
430
static uint8
431
channel_to_ctl_chan(uint center_ch, uint bw, uint sb)
432
{
433
return (uint8)(channel_low_edge(center_ch, bw) + sb * 4);
434
}
435
436
/* return index of 80MHz channel from channel number
437
* return -1 on error
438
*/
439
static int
440
channel_80mhz_to_id(uint ch)
441
{
442
uint i;
443
for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) {
444
if (ch == wf_5g_80m_chans[i])
445
return i;
446
}
447
448
return -1;
449
}
450
451
/* given a chanspec and a string buffer, format the chanspec as a
452
* string, and return the original pointer a.
453
* Min buffer length must be CHANSPEC_STR_LEN.
454
* On error return NULL
455
*/
456
char *
457
wf_chspec_ntoa(chanspec_t chspec, char *buf)
458
{
459
const char *band;
460
uint ctl_chan;
461
462
if (wf_chspec_malformed(chspec))
463
return NULL;
464
465
band = "";
466
467
/* check for non-default band spec */
468
if ((CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) ||
469
(CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL))
470
band = (CHSPEC_IS2G(chspec)) ? "2g" : "5g";
471
472
/* ctl channel */
473
ctl_chan = wf_chspec_ctlchan(chspec);
474
475
/* bandwidth and ctl sideband */
476
if (CHSPEC_IS20(chspec)) {
477
snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, ctl_chan);
478
} else if (!CHSPEC_IS8080(chspec)) {
479
const char *bw;
480
const char *sb = "";
481
482
bw = wf_chspec_bw_str[(chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT];
483
484
#ifdef CHANSPEC_NEW_40MHZ_FORMAT
485
/* ctl sideband string if needed for 2g 40MHz */
486
if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) {
487
sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
488
}
489
490
snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, ctl_chan, bw, sb);
491
#else
492
/* ctl sideband string instead of BW for 40MHz */
493
if (CHSPEC_IS40(chspec)) {
494
sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
495
snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, ctl_chan, sb);
496
} else {
497
snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, ctl_chan, bw);
498
}
499
#endif /* CHANSPEC_NEW_40MHZ_FORMAT */
500
501
} else {
502
/* 80+80 */
503
uint chan1 = (chspec & WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT;
504
uint chan2 = (chspec & WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT;
505
506
/* convert to channel number */
507
chan1 = (chan1 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan1] : 0;
508
chan2 = (chan2 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan2] : 0;
509
510
/* Outputs a max of CHANSPEC_STR_LEN chars including '\0' */
511
snprintf(buf, CHANSPEC_STR_LEN, "%d/80+80/%d-%d", ctl_chan, chan1, chan2);
512
}
513
514
return (buf);
515
}
516
517
static int
518
read_uint(const char **p, unsigned int *num)
519
{
520
unsigned long val;
521
char *endp = NULL;
522
523
val = strtoul(*p, &endp, 10);
524
/* if endp is the initial pointer value, then a number was not read */
525
if (endp == *p)
526
return 0;
527
528
/* advance the buffer pointer to the end of the integer string */
529
*p = endp;
530
/* return the parsed integer */
531
*num = (unsigned int)val;
532
533
return 1;
534
}
535
536
/* given a chanspec string, convert to a chanspec.
537
* On error return 0
538
*/
539
chanspec_t
540
wf_chspec_aton(const char *a)
541
{
542
chanspec_t chspec;
543
uint chspec_ch, chspec_band, bw, chspec_bw, chspec_sb;
544
uint num, ctl_ch;
545
uint ch1, ch2;
546
char c, sb_ul = '\0';
547
int i;
548
549
bw = 20;
550
chspec_sb = 0;
551
chspec_ch = ch1 = ch2 = 0;
552
553
/* parse channel num or band */
554
if (!read_uint(&a, &num))
555
return 0;
556
557
/* if we are looking at a 'g', then the first number was a band */
558
c = tolower((int)a[0]);
559
if (c == 'g') {
560
a ++; /* consume the char */
561
562
/* band must be "2" or "5" */
563
if (num == 2)
564
chspec_band = WL_CHANSPEC_BAND_2G;
565
else if (num == 5)
566
chspec_band = WL_CHANSPEC_BAND_5G;
567
else
568
return 0;
569
570
/* read the channel number */
571
if (!read_uint(&a, &ctl_ch))
572
return 0;
573
574
c = tolower((int)a[0]);
575
}
576
else {
577
/* first number is channel, use default for band */
578
ctl_ch = num;
579
chspec_band = ((ctl_ch <= CH_MAX_2G_CHANNEL) ?
580
WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
581
}
582
583
if (c == '\0') {
584
/* default BW of 20MHz */
585
chspec_bw = WL_CHANSPEC_BW_20;
586
goto done_read;
587
}
588
589
a ++; /* consume the 'u','l', or '/' */
590
591
/* check 'u'/'l' */
592
if (c == 'u' || c == 'l') {
593
sb_ul = c;
594
chspec_bw = WL_CHANSPEC_BW_40;
595
goto done_read;
596
}
597
598
/* next letter must be '/' */
599
if (c != '/')
600
return 0;
601
602
/* read bandwidth */
603
if (!read_uint(&a, &bw))
604
return 0;
605
606
/* convert to chspec value */
607
if (bw == 20) {
608
chspec_bw = WL_CHANSPEC_BW_20;
609
} else if (bw == 40) {
610
chspec_bw = WL_CHANSPEC_BW_40;
611
} else if (bw == 80) {
612
chspec_bw = WL_CHANSPEC_BW_80;
613
} else if (bw == 160) {
614
chspec_bw = WL_CHANSPEC_BW_160;
615
} else {
616
return 0;
617
}
618
619
/* So far we have <band>g<chan>/<bw>
620
* Can now be followed by u/l if bw = 40,
621
* or '+80' if bw = 80, to make '80+80' bw.
622
*/
623
624
c = tolower((int)a[0]);
625
626
/* if we have a 2g/40 channel, we should have a l/u spec now */
627
if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) {
628
if (c == 'u' || c == 'l') {
629
a ++; /* consume the u/l char */
630
sb_ul = c;
631
goto done_read;
632
}
633
}
634
635
/* check for 80+80 */
636
if (c == '+') {
637
/* 80+80 */
638
static const char *plus80 = "80/";
639
640
/* must be looking at '+80/'
641
* check and consume this string.
642
*/
643
chspec_bw = WL_CHANSPEC_BW_8080;
644
645
a ++; /* consume the char '+' */
646
647
/* consume the '80/' string */
648
for (i = 0; i < 3; i++) {
649
if (*a++ != *plus80++) {
650
return 0;
651
}
652
}
653
654
/* read primary 80MHz channel */
655
if (!read_uint(&a, &ch1))
656
return 0;
657
658
/* must followed by '-' */
659
if (a[0] != '-')
660
return 0;
661
a ++; /* consume the char */
662
663
/* read secondary 80MHz channel */
664
if (!read_uint(&a, &ch2))
665
return 0;
666
}
667
668
done_read:
669
/* skip trailing white space */
670
while (a[0] == ' ') {
671
a ++;
672
}
673
674
/* must be end of string */
675
if (a[0] != '\0')
676
return 0;
677
678
/* Now have all the chanspec string parts read;
679
* chspec_band, ctl_ch, chspec_bw, sb_ul, ch1, ch2.
680
* chspec_band and chspec_bw are chanspec values.
681
* Need to convert ctl_ch, sb_ul, and ch1,ch2 into
682
* a center channel (or two) and sideband.
683
*/
684
685
/* if a sb u/l string was given, just use that,
686
* guaranteed to be bw = 40 by sting parse.
687
*/
688
if (sb_ul != '\0') {
689
if (sb_ul == 'l') {
690
chspec_ch = UPPER_20_SB(ctl_ch);
691
chspec_sb = WL_CHANSPEC_CTL_SB_LLL;
692
} else if (sb_ul == 'u') {
693
chspec_ch = LOWER_20_SB(ctl_ch);
694
chspec_sb = WL_CHANSPEC_CTL_SB_LLU;
695
}
696
}
697
/* if the bw is 20, center and sideband are trivial */
698
else if (chspec_bw == WL_CHANSPEC_BW_20) {
699
chspec_ch = ctl_ch;
700
chspec_sb = 0;
701
}
702
/* if the bw is 40/80/160, not 80+80, a single method
703
* can be used to to find the center and sideband
704
*/
705
else if (chspec_bw != WL_CHANSPEC_BW_8080) {
706
/* figure out ctl sideband based on ctl channel and bandwidth */
707
const uint8 *center_ch = NULL;
708
int num_ch = 0;
709
int sb = -1;
710
711
if (chspec_bw == WL_CHANSPEC_BW_40) {
712
center_ch = wf_5g_40m_chans;
713
num_ch = WF_NUM_5G_40M_CHANS;
714
} else if (chspec_bw == WL_CHANSPEC_BW_80) {
715
center_ch = wf_5g_80m_chans;
716
num_ch = WF_NUM_5G_80M_CHANS;
717
} else if (chspec_bw == WL_CHANSPEC_BW_160) {
718
center_ch = wf_5g_160m_chans;
719
num_ch = WF_NUM_5G_160M_CHANS;
720
} else {
721
return 0;
722
}
723
724
for (i = 0; i < num_ch; i ++) {
725
sb = channel_to_sb(center_ch[i], ctl_ch, bw);
726
if (sb >= 0) {
727
chspec_ch = center_ch[i];
728
chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT;
729
break;
730
}
731
}
732
733
/* check for no matching sb/center */
734
if (sb < 0) {
735
return 0;
736
}
737
}
738
/* Otherwise, bw is 80+80. Figure out channel pair and sb */
739
else {
740
int ch1_id = 0, ch2_id = 0;
741
int sb;
742
743
ch1_id = channel_80mhz_to_id(ch1);
744
ch2_id = channel_80mhz_to_id(ch2);
745
746
/* validate channels */
747
if (ch1 >= ch2 || ch1_id < 0 || ch2_id < 0)
748
return 0;
749
750
/* combined channel in chspec */
751
chspec_ch = (((uint16)ch1_id << WL_CHANSPEC_CHAN1_SHIFT) |
752
((uint16)ch2_id << WL_CHANSPEC_CHAN2_SHIFT));
753
754
/* figure out ctl sideband */
755
756
/* does the primary channel fit with the 1st 80MHz channel ? */
757
sb = channel_to_sb(ch1, ctl_ch, bw);
758
if (sb < 0) {
759
/* no, so does the primary channel fit with the 2nd 80MHz channel ? */
760
sb = channel_to_sb(ch2, ctl_ch, bw);
761
if (sb < 0) {
762
/* no match for ctl_ch to either 80MHz center channel */
763
return 0;
764
}
765
/* sb index is 0-3 for the low 80MHz channel, and 4-7 for
766
* the high 80MHz channel. Add 4 to to shift to high set.
767
*/
768
sb += 4;
769
}
770
771
chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT;
772
}
773
774
chspec = (chspec_ch | chspec_band | chspec_bw | chspec_sb);
775
776
if (wf_chspec_malformed(chspec))
777
return 0;
778
779
return chspec;
780
}
781
782
/*
783
* Verify the chanspec is using a legal set of parameters, i.e. that the
784
* chanspec specified a band, bw, ctl_sb and channel and that the
785
* combination could be legal given any set of circumstances.
786
* RETURNS: TRUE is the chanspec is malformed, false if it looks good.
787
*/
788
bool
789
wf_chspec_malformed(chanspec_t chanspec)
790
{
791
uint chspec_bw = CHSPEC_BW(chanspec);
792
uint chspec_ch = CHSPEC_CHANNEL(chanspec);
793
794
/* must be 2G or 5G band */
795
if (CHSPEC_IS2G(chanspec)) {
796
/* must be valid bandwidth */
797
if (chspec_bw != WL_CHANSPEC_BW_20 &&
798
chspec_bw != WL_CHANSPEC_BW_40) {
799
return TRUE;
800
}
801
} else if (CHSPEC_IS5G(chanspec)) {
802
if (chspec_bw == WL_CHANSPEC_BW_8080) {
803
uint ch1_id, ch2_id;
804
805
/* channel number in 80+80 must be in range */
806
ch1_id = CHSPEC_CHAN1(chanspec);
807
ch2_id = CHSPEC_CHAN2(chanspec);
808
if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS)
809
return TRUE;
810
811
/* ch2 must be above ch1 for the chanspec */
812
if (ch2_id <= ch1_id)
813
return TRUE;
814
} else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 ||
815
chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) {
816
817
if (chspec_ch > MAXCHANNEL) {
818
return TRUE;
819
}
820
} else {
821
/* invalid bandwidth */
822
return TRUE;
823
}
824
} else {
825
/* must be 2G or 5G band */
826
return TRUE;
827
}
828
829
/* side band needs to be consistent with bandwidth */
830
if (chspec_bw == WL_CHANSPEC_BW_20) {
831
if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL)
832
return TRUE;
833
} else if (chspec_bw == WL_CHANSPEC_BW_40) {
834
if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU)
835
return TRUE;
836
} else if (chspec_bw == WL_CHANSPEC_BW_80) {
837
if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU)
838
return TRUE;
839
}
840
841
return FALSE;
842
}
843
844
/*
845
* Verify the chanspec specifies a valid channel according to 802.11.
846
* RETURNS: TRUE if the chanspec is a valid 802.11 channel
847
*/
848
bool
849
wf_chspec_valid(chanspec_t chanspec)
850
{
851
uint chspec_bw = CHSPEC_BW(chanspec);
852
uint chspec_ch = CHSPEC_CHANNEL(chanspec);
853
854
if (wf_chspec_malformed(chanspec))
855
return FALSE;
856
857
if (CHSPEC_IS2G(chanspec)) {
858
/* must be valid bandwidth and channel range */
859
if (chspec_bw == WL_CHANSPEC_BW_20) {
860
if (chspec_ch >= 1 && chspec_ch <= 14)
861
return TRUE;
862
} else if (chspec_bw == WL_CHANSPEC_BW_40) {
863
if (chspec_ch >= 3 && chspec_ch <= 11)
864
return TRUE;
865
}
866
} else if (CHSPEC_IS5G(chanspec)) {
867
if (chspec_bw == WL_CHANSPEC_BW_8080) {
868
uint16 ch1, ch2;
869
870
ch1 = wf_5g_80m_chans[CHSPEC_CHAN1(chanspec)];
871
ch2 = wf_5g_80m_chans[CHSPEC_CHAN2(chanspec)];
872
873
/* the two channels must be separated by more than 80MHz by VHT req,
874
* and ch2 above ch1 for the chanspec
875
*/
876
if (ch2 > ch1 + CH_80MHZ_APART)
877
return TRUE;
878
} else {
879
const uint8 *center_ch;
880
uint num_ch, i;
881
882
if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40) {
883
center_ch = wf_5g_40m_chans;
884
num_ch = WF_NUM_5G_40M_CHANS;
885
} else if (chspec_bw == WL_CHANSPEC_BW_80) {
886
center_ch = wf_5g_80m_chans;
887
num_ch = WF_NUM_5G_80M_CHANS;
888
} else if (chspec_bw == WL_CHANSPEC_BW_160) {
889
center_ch = wf_5g_160m_chans;
890
num_ch = WF_NUM_5G_160M_CHANS;
891
} else {
892
/* invalid bandwidth */
893
return FALSE;
894
}
895
896
/* check for a valid center channel */
897
if (chspec_bw == WL_CHANSPEC_BW_20) {
898
/* We don't have an array of legal 20MHz 5G channels, but they are
899
* each side of the legal 40MHz channels. Check the chanspec
900
* channel against either side of the 40MHz channels.
901
*/
902
for (i = 0; i < num_ch; i ++) {
903
if (chspec_ch == (uint)LOWER_20_SB(center_ch[i]) ||
904
chspec_ch == (uint)UPPER_20_SB(center_ch[i]))
905
break; /* match found */
906
}
907
908
if (i == num_ch) {
909
/* check for legacy JP channels on failure */
910
if (chspec_ch == 34 || chspec_ch == 38 ||
911
chspec_ch == 42 || chspec_ch == 46)
912
i = 0;
913
}
914
} else {
915
/* check the chanspec channel to each legal channel */
916
for (i = 0; i < num_ch; i ++) {
917
if (chspec_ch == center_ch[i])
918
break; /* match found */
919
}
920
}
921
922
if (i < num_ch) {
923
/* match found */
924
return TRUE;
925
}
926
}
927
}
928
929
return FALSE;
930
}
931
932
/*
933
* This function returns the channel number that control traffic is being sent on, for 20MHz
934
* channels this is just the channel number, for 40MHZ, 80MHz, 160MHz channels it is the 20MHZ
935
* sideband depending on the chanspec selected
936
*/
937
uint8
938
wf_chspec_ctlchan(chanspec_t chspec)
939
{
940
uint center_chan;
941
uint bw_mhz;
942
uint sb;
943
944
ASSERT(!wf_chspec_malformed(chspec));
945
946
/* Is there a sideband ? */
947
if (CHSPEC_IS20(chspec)) {
948
return CHSPEC_CHANNEL(chspec);
949
} else {
950
sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT;
951
952
if (CHSPEC_IS8080(chspec)) {
953
bw_mhz = 80;
954
955
if (sb < 4) {
956
center_chan = CHSPEC_CHAN1(chspec);
957
}
958
else {
959
center_chan = CHSPEC_CHAN2(chspec);
960
sb -= 4;
961
}
962
963
/* convert from channel index to channel number */
964
center_chan = wf_5g_80m_chans[center_chan];
965
}
966
else {
967
bw_mhz = bw_chspec_to_mhz(chspec);
968
center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT;
969
}
970
971
return (channel_to_ctl_chan(center_chan, bw_mhz, sb));
972
}
973
}
974
975
/*
976
* This function returns the chanspec of the control channel of a given chanspec
977
*/
978
chanspec_t
979
wf_chspec_ctlchspec(chanspec_t chspec)
980
{
981
chanspec_t ctl_chspec = chspec;
982
uint8 ctl_chan;
983
984
ASSERT(!wf_chspec_malformed(chspec));
985
986
/* Is there a sideband ? */
987
if (!CHSPEC_IS20(chspec)) {
988
ctl_chan = wf_chspec_ctlchan(chspec);
989
ctl_chspec = ctl_chan | WL_CHANSPEC_BW_20;
990
ctl_chspec |= CHSPEC_BAND(chspec);
991
}
992
return ctl_chspec;
993
}
994
995
/* return chanspec given control channel and bandwidth
996
* return 0 on error
997
*/
998
uint16
999
wf_channel2chspec(uint ctl_ch, uint bw)
1000
{
1001
uint16 chspec;
1002
const uint8 *center_ch = NULL;
1003
int num_ch = 0;
1004
int sb = -1;
1005
int i = 0;
1006
1007
chspec = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
1008
1009
chspec |= bw;
1010
1011
if (bw == WL_CHANSPEC_BW_40) {
1012
center_ch = wf_5g_40m_chans;
1013
num_ch = WF_NUM_5G_40M_CHANS;
1014
bw = 40;
1015
} else if (bw == WL_CHANSPEC_BW_80) {
1016
center_ch = wf_5g_80m_chans;
1017
num_ch = WF_NUM_5G_80M_CHANS;
1018
bw = 80;
1019
} else if (bw == WL_CHANSPEC_BW_160) {
1020
center_ch = wf_5g_160m_chans;
1021
num_ch = WF_NUM_5G_160M_CHANS;
1022
bw = 160;
1023
} else if (bw == WL_CHANSPEC_BW_20) {
1024
chspec |= ctl_ch;
1025
return chspec;
1026
} else {
1027
return 0;
1028
}
1029
1030
for (i = 0; i < num_ch; i ++) {
1031
sb = channel_to_sb(center_ch[i], ctl_ch, bw);
1032
if (sb >= 0) {
1033
chspec |= center_ch[i];
1034
chspec |= (sb << WL_CHANSPEC_CTL_SB_SHIFT);
1035
break;
1036
}
1037
}
1038
1039
/* check for no matching sb/center */
1040
if (sb < 0) {
1041
return 0;
1042
}
1043
1044
return chspec;
1045
}
1046
1047
#endif /* D11AC_IOTYPES */
1048
1049
/*
1050
* This function returns the chanspec for the primary 40MHz of an 80MHz channel.
1051
* The control sideband specifies the same 20MHz channel that the 80MHz channel is using
1052
* as the primary 20MHz channel.
1053
*/
1054
extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec)
1055
{
1056
chanspec_t chspec40 = chspec;
1057
#ifdef D11AC_IOTYPES
1058
uint center_chan;
1059
uint sb;
1060
ASSERT(!wf_chspec_malformed(chspec));
1061
1062
if (CHSPEC_IS80(chspec)) {
1063
center_chan = CHSPEC_CHANNEL(chspec);
1064
sb = CHSPEC_CTL_SB(chspec);
1065
1066
if (sb == WL_CHANSPEC_CTL_SB_UL) {
1067
/* Primary 40MHz is on upper side */
1068
sb = WL_CHANSPEC_CTL_SB_L;
1069
center_chan += CH_20MHZ_APART;
1070
} else if (sb == WL_CHANSPEC_CTL_SB_UU) {
1071
/* Primary 40MHz is on upper side */
1072
sb = WL_CHANSPEC_CTL_SB_U;
1073
center_chan += CH_20MHZ_APART;
1074
} else {
1075
/* Primary 40MHz is on lower side */
1076
/* sideband bits are the same for LL/LU and L/U */
1077
center_chan -= CH_20MHZ_APART;
1078
}
1079
1080
/* Create primary 40MHz chanspec */
1081
chspec40 = (WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_40 |
1082
sb | center_chan);
1083
}
1084
#endif
1085
return chspec40;
1086
}
1087
1088
/*
1089
* Return the channel number for a given frequency and base frequency.
1090
* The returned channel number is relative to the given base frequency.
1091
* If the given base frequency is zero, a base frequency of 5 GHz is assumed for
1092
* frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz.
1093
*
1094
* Frequency is specified in MHz.
1095
* The base frequency is specified as (start_factor * 500 kHz).
1096
* Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for
1097
* 2.4 GHz and 5 GHz bands.
1098
*
1099
* The returned channel will be in the range [1, 14] in the 2.4 GHz band
1100
* and [0, 200] otherwise.
1101
* -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the
1102
* frequency is not a 2.4 GHz channel, or if the frequency is not and even
1103
* multiple of 5 MHz from the base frequency to the base plus 1 GHz.
1104
*
1105
* Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2
1106
*/
1107
int
1108
wf_mhz2channel(uint freq, uint start_factor)
1109
{
1110
int ch = -1;
1111
uint base;
1112
int offset;
1113
1114
/* take the default channel start frequency */
1115
if (start_factor == 0) {
1116
if (freq >= 2400 && freq <= 2500)
1117
start_factor = WF_CHAN_FACTOR_2_4_G;
1118
else if (freq >= 5000 && freq <= 6000)
1119
start_factor = WF_CHAN_FACTOR_5_G;
1120
}
1121
1122
if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G)
1123
return 14;
1124
1125
base = start_factor / 2;
1126
1127
/* check that the frequency is in 1GHz range of the base */
1128
if ((freq < base) || (freq > base + 1000))
1129
return -1;
1130
1131
offset = freq - base;
1132
ch = offset / 5;
1133
1134
/* check that frequency is a 5MHz multiple from the base */
1135
if (offset != (ch * 5))
1136
return -1;
1137
1138
/* restricted channel range check for 2.4G */
1139
if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13))
1140
return -1;
1141
1142
return ch;
1143
}
1144
1145
/*
1146
* Return the center frequency in MHz of the given channel and base frequency.
1147
* The channel number is interpreted relative to the given base frequency.
1148
*
1149
* The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise.
1150
* The base frequency is specified as (start_factor * 500 kHz).
1151
* Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_4_G, and WF_CHAN_FACTOR_5_G
1152
* are defined for 2.4 GHz, 4 GHz, and 5 GHz bands.
1153
* The channel range of [1, 14] is only checked for a start_factor of
1154
* WF_CHAN_FACTOR_2_4_G (4814 = 2407 * 2).
1155
* Odd start_factors produce channels on .5 MHz boundaries, in which case
1156
* the answer is rounded down to an integral MHz.
1157
* -1 is returned for an out of range channel.
1158
*
1159
* Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2
1160
*/
1161
int
1162
wf_channel2mhz(uint ch, uint start_factor)
1163
{
1164
int freq;
1165
1166
if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) ||
1167
(ch > 200))
1168
freq = -1;
1169
else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14))
1170
freq = 2484;
1171
else
1172
freq = ch * 5 + start_factor / 2;
1173
1174
return freq;
1175
}
1176
1177