-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbcmwifi_channels.c
3312 lines (2944 loc) · 91.4 KB
/
bcmwifi_channels.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Misc utility routines used by kernel or app-level.
* Contents are wifi-specific, used by any kernel or app-level
* software that might want wifi things as it grows.
*
* Copyright (C) 2023, Broadcom.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
*
* <<Broadcom-WL-IPTag/Dual:>>
*/
#include <typedefs.h>
#include <bcmutils.h>
#include <bcmdefs.h>
#ifdef BCMDRIVER
#include <osl.h>
#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef ASSERT
#define ASSERT(exp)
#endif
#ifndef ASSERT_FP
#define ASSERT_FP(exp)
#endif
#endif /* BCMDRIVER */
#include <bcmwifi_channels.h>
#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL) || defined(_CONSOLE))
#include <bcmstdlib.h> /* For wlexe/Makefile.wlm_dll */
#endif
#include <802.11.h>
#include <802.11ac.h>
/* Definitions for D11AC capable (80MHz+) Chanspec type */
/* Chanspec ASCII representation:
*
* [<band>'g']<channel>['/'<bandwidth>[<primary-sideband>]
* ['/'<1st-channel-segment>'-'<2nd-channel-segment>]]
*
* <band>:
* (optional) 2, 4, 5, 6 for 2.4GHz, 4GHz, 5GHz, and 6GHz respectively.
* Default value is 2g if channel <= 14, otherwise 5g.
* <channel>:
* channel number of the 20MHz channel,
* or primary 20 MHz channel of 40MHz, 80MHz, 160MHz, 80+80MHz,
* 320MHz, or 160+160MHz channels.
* <bandwidth>:
* (optional) 20, 40, 80, 160, 80+80, 320, or 160+160. Default value is 20.
* <primary-sideband>:
* 'u' or 'l' (only for 2.4GHz band 40MHz)
*
* For 2.4GHz band 40MHz channels, the same primary channel may be the
* upper sideband for one 40MHz channel, and the lower sideband for an
* overlapping 40MHz channel. The {u: upper, l: lower} primary sideband
* indication disambiguates which 40MHz channel is being specified.
*
* For 40MHz in the 5GHz or 6GHz band and all channel bandwidths greater than
* 40MHz, the U/L specification is not necessary or allowed since the channels are
* non-overlapping and the primary 20MHz channel position is derived from its
* position in the wide bandwidth channel.
* <1st-channel-segment>
* <2nd-channel-segment>:
* Required for 80+80 or 160+160, otherwise not allowed.
* These fields specify the center channel of the first and the second 80MHz
* or 160MHz channels.
*
* In its simplest form, it is a 20MHz channel number, with the implied band
* of 2.4GHz if channel number <= 14, and 5GHz otherwise.
*
* To allow for backward compatibility with scripts, the old form for
* 40MHz channels is also allowed: <channel><primary-sideband>
*
* <channel>:
* primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz
* <primary-sideband>:
* "U" for upper, "L" for lower (or lower case "u" "l")
*
* 5 GHz Examples:
* Chanspec BW Center Ch Channel Range Primary Ch
* 5g8 20MHz 8 - -
* 52 20MHz 52 - -
* 52/40 40MHz 54 52-56 52
* 56/40 40MHz 54 52-56 56
* 52/80 80MHz 58 52-64 52
* 56/80 80MHz 58 52-64 56
* 60/80 80MHz 58 52-64 60
* 64/80 80MHz 58 52-64 64
* 52/160 160MHz 50 36-64 52
* 36/160 160MGz 50 36-64 36
* 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36
*
* 2 GHz Examples:
* Chanspec BW Center Ch Channel Range Primary Ch
* 2g8 20MHz 8 - -
* 8 20MHz 8 - -
* 6 20MHz 6 - -
* 6/40l 40MHz 8 6-10 6
* 6l 40MHz 8 6-10 6
* 6/40u 40MHz 4 2-6 6
* 6u 40MHz 4 2-6 6
*/
/* bandwidth ASCII string */
static const char *wf_chspec_bw_str[] =
{
"320",
"160+160",
"20",
"40",
"80",
"160",
"80+80"
};
static const uint16 wf_chspec_bw_mhz[] = {
320, 320, 20, 40, 80, 160, 160
};
#define WF_NUM_BW ARRAYSIZE(wf_chspec_bw_mhz)
/* 40MHz channels in 5GHz band */
static const uint8 wf_5g_40m_chans[] = {
38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159, 167, 175
};
#define WF_NUM_5G_40M_CHANS ARRAYSIZE(wf_5g_40m_chans)
/* 80MHz channels in 5GHz band */
static const uint8 wf_5g_80m_chans[] = {
42, 58, 106, 122, 138, 155, 171
};
#define WF_NUM_5G_80M_CHANS ARRAYSIZE(wf_5g_80m_chans)
/* 160MHz channels in 5GHz band */
static const uint8 wf_5g_160m_chans[] = {
50, 114, 163
};
#define WF_NUM_5G_160M_CHANS ARRAYSIZE(wf_5g_160m_chans)
/** 80MHz channels in 6GHz band */
#define WF_NUM_6G_80M_CHANS 14
/** 160MHz channels in 6GHz band */
#define WF_NUM_6G_160M_CHANS 7 /* TBD */
/** 320MHz channels in 6GHz band */
#define WF_NUM_6G_320M_CHANS 6
/* valid channels: 0,1,2,4,5,6, channel 3 invalid as given in
* wf_chspec_6G_id320_to_ch
*/
#define WF_NUM_6G_320M_CHAN_ID_MIN 0
#define WF_NUM_6G_320M_CHAN_ID_MAX 6
/* 320Mhz Center chan to Chan Id map */
static const int8 map_320m_cc_chanid[] = {
0, /* CC 31 */
1, /* CC 95 */
2, /* CC 159 */
};
/* 320Mhz Chan Id to Center Channel map */
static const uint8 BCMPOST_TRAP_RODATA(map_320m_chanid_cc)[] = {
31, /* CC 31 */
95, /* CC 63 */
159, /* CC 95 */
0, /* INVALCHAN */
63, /* CC 127 */
127, /* CC 159 */
191, /* CC 191 */
};
typedef struct {
uint8 start;
uint8 end;
uint8 center;
uint8 pad;
} wf_6g_320m_chan_range_t;
static const wf_6g_320m_chan_range_t wf_6g_320m_ch_set[] =
{
{1, 61, 31, 0}, {65, 125, 95, 0}, {129, 189, 159, 0}
};
static const wf_6g_320m_chan_range_t wf_6g_320m_ch_ol_set[] =
{
{33, 93, 63, 0}, {97, 157, 127, 0}, {161, 221, 191, 0}
};
static const uint ch_per_blk_map[] = {
64, /* WL_CHANSPEC_BW_320 */
64, /* WL_CHANSPEC_BW_320 */
4, /* WL_CHANSPEC_BW_20 */
8, /* WL_CHANSPEC_BW_40 */
16, /* WL_CHANSPEC_BW_80 */
32, /* WL_CHANSPEC_BW_160 */
32, /* WL_CHANSPEC_BW_160 */
};
/* Define the conditional macro to help with reducing the code size bloat
* in other branches and in trunk targets that don't need 11BE features...
*/
#define WFC_2VALS_EQ(var, val) ((var) == (val))
/* compare bandwidth unconditionally for 320Mhz related stuff */
#if defined(WL11BE) || defined(WL_BW320MHZ)
#define WFC_BW_EQ(bw, val) WFC_2VALS_EQ(bw, val)
#else
#define WFC_BW_EQ(bw, val) (FALSE)
#endif /* WL11BE || WL_BW320MHZ */
/* compare bandwidth based on WFC_NON_CONT_CHAN */
#ifdef WFC_NON_CONT_CHAN
#define WFC_NCBW_EQ(bw, val) WFC_2VALS_EQ(bw, val)
#else
#define WFC_NCBW_EQ(bw, val) (FALSE)
#endif
static void wf_chanspec_iter_firstchan(wf_chanspec_iter_t *iter);
static chanspec_bw_t wf_iter_next_bw(chanspec_bw_t bw);
static bool wf_chanspec_iter_next_2g(wf_chanspec_iter_t *iter);
static bool wf_chanspec_iter_next_5g(wf_chanspec_iter_t *iter);
static int wf_chanspec_iter_next_5g_range(wf_chanspec_iter_t *iter, chanspec_bw_t bw);
static bool wf_chanspec_iter_6g_range_init(wf_chanspec_iter_t *iter, chanspec_bw_t bw);
static bool wf_chanspec_iter_next_6g(wf_chanspec_iter_t *iter);
static uint wf_6g_get_center_chan_from_primary(uint primary_channel, chanspec_bw_t bw,
bool overlapped320);
/**
* Return the chanspec bandwidth in MHz
* Bandwidth of 160 MHz will be returned for 80+80MHz chanspecs.
*
* @param chspec chanspec_t
*
* @return bandwidth of chspec in MHz units
*/
uint
wf_bw_chspec_to_mhz(chanspec_t chspec)
{
uint bw;
bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT;
return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]);
}
/* bw in MHz, return the channel count from the center channel to the
* the channel at the edge of the band
*/
static uint
center_chan_to_edge(chanspec_bw_t bw)
{
uint delta = 0;
/* edge channels separated by BW - 10MHz on each side
* delta from cf to edge is half of that,
*/
if (bw == WL_CHANSPEC_BW_40) {
/* 10 MHz */
delta = 2;
} else if (bw == WL_CHANSPEC_BW_80) {
/* 30 MHz */
delta = 6;
} else if (bw == WL_CHANSPEC_BW_160) {
/* 70 MHz */
delta = 14;
} else if (WFC_BW_EQ(bw, WL_CHANSPEC_BW_320)) {
/* 150 MHz */
delta = 30;
}
return delta;
}
/* return channel number of the low edge of the band
* given the center channel and BW
*/
static uint
channel_low_edge(uint center_ch, chanspec_bw_t bw)
{
return (center_ch - center_chan_to_edge(bw));
}
/* return side band number given center channel and primary20 channel
* return -1 on error
*/
static int
channel_to_sb(uint center_ch, uint primary_ch, chanspec_bw_t bw)
{
uint lowest = channel_low_edge(center_ch, bw);
uint sb;
if (primary_ch < lowest ||
(primary_ch - lowest) % 4) {
/* bad primary channel lower than the low edge of the channel,
* or not mult 4.
*/
return -1;
}
sb = ((primary_ch - lowest) / 4);
/* sb must be a index to a 20MHz channel in range */
if ((bw == WL_CHANSPEC_BW_20 && sb >= 1) ||
(bw == WL_CHANSPEC_BW_40 && sb >= 2) ||
(bw == WL_CHANSPEC_BW_80 && sb >= 4) ||
(bw == WL_CHANSPEC_BW_160 && sb >= 8) ||
(WFC_BW_EQ(bw, WL_CHANSPEC_BW_320) && sb >= 16)) {
/* primary_ch must have been too high for the center_ch */
return -1;
}
return sb;
}
/* return primary20 channel given center channel and side band */
static uint
channel_to_primary20_chan(uint center_ch, chanspec_bw_t bw, uint sb)
{
return (channel_low_edge(center_ch, bw) + sb * 4);
}
/* return index of 80MHz channel from channel number
* return -1 on error
*/
static int
channel_80mhz_to_id(uint ch)
{
uint i;
for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) {
if (ch == wf_5g_80m_chans[i])
return i;
}
return -1;
}
/* return index of the 6G 80MHz channel from channel number
* return -1 on error
*/
static int
channel_6g_80mhz_to_id(uint ch)
{
/* The 6GHz center channels start at 7, and have a spacing of 16 */
if (ch >= CH_MIN_6G_80M_CHANNEL &&
ch <= CH_MAX_6G_80M_CHANNEL &&
((ch - CH_MIN_6G_80M_CHANNEL) % 16) == 0) { // even multiple of 16
return (ch - CH_MIN_6G_80M_CHANNEL) / 16;
}
return -1;
}
/* return index of the 5G 160MHz channel from channel number
* return -1 on error
*/
int
channel_5g_160mhz_to_id(uint ch)
{
uint i;
for (i = 0; i < WF_NUM_5G_160M_CHANS; i ++) {
if (ch == wf_5g_160m_chans[i]) {
return i;
}
}
return -1;
}
/* return index of the 6G 160MHz channel from channel number
* return -1 on error
*/
int
channel_6g_160mhz_to_id(uint ch)
{
/* The 6GHz center channels start at 15, and have a spacing of 32 */
if (ch >= CH_MIN_6G_160M_CHANNEL &&
ch <= CH_MAX_6G_160M_CHANNEL &&
((ch - CH_MIN_6G_160M_CHANNEL) % 32) == 0) {
return (ch - CH_MIN_6G_160M_CHANNEL) / 32;
}
return -1;
}
/* return index of the 6G 320MHz channel from channel number
* return -1 on error
*/
int
channel_6g_320mhz_to_id(uint ch)
{
int id;
/* The 6GHz center channels start at 31, and have a spacing of 64 */
if (ch >= CH_MIN_6G_320M_CHANNEL &&
ch <= CH_MAX_6G_320M_CHANNEL &&
((((ch - CH_MIN_6G_320M_CHANNEL) %
CH_6G_320M_CNTR_FREQ_SPACING) == 0) ||
(ch >= CH_MIN_6G_320M_OL_CHANNEL &&
((ch - CH_MIN_6G_320M_OL_CHANNEL) % CH_6G_320M_CNTR_FREQ_SPACING == 0)))) {
uint8 pos = ch / CH_6G_320M_CNTR_FREQ_SPACING;
if (pos < ARRAYSIZE(map_320m_cc_chanid)) {
id = map_320m_cc_chanid[pos];
if ((ch - CH_MIN_6G_320M_CHANNEL) % CH_6G_320M_CNTR_FREQ_SPACING == 0) {
return id;
} else {
return (id + 4);
}
}
}
return -1;
}
/**
* This function returns the the 5GHz 80MHz center channel for the given chanspec 80MHz ID
*
* @param chan_80MHz_id 80MHz chanspec ID
*
* @return Return the center channel number, or 0 on error.
*
*/
static uint8
wf_chspec_5G_id80_to_ch(uint8 chan_80MHz_id)
{
if (chan_80MHz_id < WF_NUM_5G_80M_CHANS) {
return wf_5g_80m_chans[chan_80MHz_id];
}
return 0;
}
/**
* This function returns the the 6GHz 80MHz center channel for the given chanspec 80MHz ID
*
* @param chan_80MHz_id 80MHz chanspec ID
*
* @return Return the center channel number, or 0 on error.
*
*/
static uint8
wf_chspec_6G_id80_to_ch(uint8 chan_80MHz_id)
{
uint8 ch = 0;
if (chan_80MHz_id < WF_NUM_6G_80M_CHANS) {
/* The 6GHz center channels have a spacing of 16
* starting from the first 80MHz center
*/
ch = CH_MIN_6G_80M_CHANNEL + (chan_80MHz_id * 16);
}
return ch;
}
/**
* This function returns the the 6GHz 320MHz center channel for the given chanspec 320MHz ID
*
* @param chan_320MHz_id 320MHz chanspec ID
*
* @return Return the center channel number, or 0 on error.
*
*/
static uint8
BCMPOSTTRAPFN(wf_chspec_6G_id320_to_ch)(uint8 chan_320MHz_id)
{
uint8 ch = 0;
if (chan_320MHz_id <= WF_NUM_6G_320M_CHAN_ID_MAX) {
/* The 6GHz center channels have a spacing of 64
* starting from the first 320MHz center
*/
if (chan_320MHz_id < ARRAYSIZE(map_320m_chanid_cc)) {
return map_320m_chanid_cc[chan_320MHz_id];
}
}
return ch;
}
/* Retrive the chan_id and convert it to center channel */
uint8
BCMPOSTTRAPFN(wf_chspec_320_id2cch)(chanspec_t chanspec)
{
if (CHSPEC_BAND(chanspec) == WL_CHANSPEC_BAND_6G &&
CHSPEC_BW(chanspec) == WL_CHANSPEC_BW_320) {
uint8 ch_id = CHSPEC_320_CHAN(chanspec);
return wf_chspec_6G_id320_to_ch(ch_id);
}
return 0;
}
/**
* Convert chanspec to ascii string, or formats hex of an invalid chanspec.
*
* @param chspec chanspec to format
* @param buf pointer to buf with room for at least CHANSPEC_STR_LEN bytes
*
* @return Returns pointer to passed in buf. The buffer will have the ascii
* representation of the given chspec, or "invalid 0xHHHH" where
* 0xHHHH is the hex representation of the invalid chanspec.
*
* @see CHANSPEC_STR_LEN
*
* Wrapper function for wf_chspec_ntoa. In case of an error it puts
* the original chanspec in the output buffer, prepended with "invalid".
* Can be directly used in print routines as it takes care of null
*/
char *
wf_chspec_ntoa_ex(chanspec_t chspec, char *buf)
{
if (wf_chspec_ntoa(chspec, buf) == NULL)
snprintf(buf, CHANSPEC_STR_LEN, "invalid 0x%04x", chspec);
return buf;
}
/**
* Convert chanspec to ascii string, or return NULL on error.
*
* @param chspec chanspec to format
* @param buf pointer to buf with room for at least CHANSPEC_STR_LEN bytes
*
* @return Returns pointer to passed in buf or NULL on error. On sucess, the buffer
* will have the ascii representation of the given chspec.
*
* @see CHANSPEC_STR_LEN
*
* Given a chanspec and a string buffer, format the chanspec as a
* string, and return the original pointer buf.
* Min buffer length must be CHANSPEC_STR_LEN.
* On error return NULL.
*/
char *
wf_chspec_ntoa(chanspec_t chspec, char *buf)
{
const char *band;
uint pri_chan;
if (wf_chspec_malformed(chspec))
return NULL;
band = "";
/* primary20 channel */
pri_chan = wf_chspec_primary20_chan(chspec);
/* check for non-default band spec */
if (CHSPEC_IS2G(chspec) && pri_chan > CH_MAX_2G_CHANNEL) {
band = "2g";
} else if (CHSPEC_IS5G(chspec) && pri_chan <= CH_MAX_2G_CHANNEL) {
band = "5g";
} else if (CHSPEC_IS6G(chspec)) {
band = "6g";
}
/* bandwidth and primary20 sideband */
if (CHSPEC_IS20(chspec)) {
snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, pri_chan);
} else if (CHSPEC_IS8080(chspec)) {
/* 80+80 */
uint ch0;
uint ch1;
/* get the center channels for each frequency segment */
if (CHSPEC_IS5G(chspec)) {
ch0 = wf_chspec_5G_id80_to_ch(CHSPEC_CHAN0(chspec));
ch1 = wf_chspec_5G_id80_to_ch(CHSPEC_CHAN1(chspec));
} else if (CHSPEC_IS6G(chspec)) {
ch0 = wf_chspec_6G_id80_to_ch(CHSPEC_CHAN0(chspec));
ch1 = wf_chspec_6G_id80_to_ch(CHSPEC_CHAN1(chspec));
} else {
return NULL;
}
/* Outputs a max of CHANSPEC_STR_LEN chars including '\0' */
snprintf(buf, CHANSPEC_STR_LEN, "%s%d/80+80/%d-%d", band, pri_chan, ch0, ch1);
} else if (CHSPEC_IS320(chspec)) {
/* 320 */
const char *bw;
const char *ol = "";
bw = wf_chspec_to_bw_str(chspec);
ol = CHSPEC_320_CNTR_FREQ_OVERLAPPED(chspec) ? "o" : "";
snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, pri_chan, bw, ol);
} else {
const char *bw;
const char *sb = "";
bw = wf_chspec_to_bw_str(chspec);
#ifdef CHANSPEC_NEW_40MHZ_FORMAT
/* primary20 sideband string if needed for 2g 40MHz */
if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) {
sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
}
snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, pri_chan, bw, sb);
#else
/* primary20 sideband string instead of BW for 40MHz */
if (CHSPEC_IS40(chspec) && !CHSPEC_IS6G(chspec)) {
sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";
snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, pri_chan, sb);
} else {
snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, pri_chan, bw);
}
#endif /* CHANSPEC_NEW_40MHZ_FORMAT */
}
return (buf);
}
static int
read_uint(const char **p, unsigned int *num)
{
unsigned long val;
char *endp = NULL;
val = strtoul(*p, &endp, 10);
/* if endp is the initial pointer value, then a number was not read */
if (endp == *p)
return 0;
/* advance the buffer pointer to the end of the integer string */
*p = endp;
/* return the parsed integer */
*num = (unsigned int)val;
return 1;
}
/**
* Convert ascii string to chanspec
*
* @param a pointer to input string
*
* @return Return > 0 if successful or 0 otherwise
*/
chanspec_t
wf_chspec_aton(const char *a)
{
chanspec_t chspec;
chanspec_band_t chspec_band;
chanspec_subband_t chspec_sb;
chanspec_bw_t chspec_bw;
uint bw;
uint num, pri_ch;
char c, sb_ul = '\0', overlap = '\0';
bw = 20;
chspec_sb = 0;
/* parse channel num or band */
if (!read_uint(&a, &num))
return 0;
/* if we are looking at a 'g', then the first number was a band */
c = tolower((int)a[0]);
if (c == 'g') {
a++; /* consume the char */
/* band must be "2", "5", or "6" */
if (num == 2)
chspec_band = WL_CHANSPEC_BAND_2G;
else if (num == 5)
chspec_band = WL_CHANSPEC_BAND_5G;
else if (num == 6)
chspec_band = WL_CHANSPEC_BAND_6G;
else
return 0;
/* read the channel number */
if (!read_uint(&a, &pri_ch))
return 0;
c = tolower((int)a[0]);
} else {
/* first number is channel, use default for band */
pri_ch = num;
chspec_band = ((pri_ch <= CH_MAX_2G_CHANNEL) ?
WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
}
if (c == '\0') {
/* default BW of 20MHz */
chspec_bw = WL_CHANSPEC_BW_20;
goto done_read;
}
a ++; /* consume the 'u','l', or '/' */
/* check 'u'/'l' */
if (c == 'u' || c == 'l') {
sb_ul = c;
chspec_bw = WL_CHANSPEC_BW_40;
goto done_read;
}
/* next letter must be '/' */
if (c != '/')
return 0;
/* read bandwidth */
if (!read_uint(&a, &bw))
return 0;
/* convert to chspec value */
if (bw == 20) {
chspec_bw = WL_CHANSPEC_BW_20;
} else if (bw == 40) {
chspec_bw = WL_CHANSPEC_BW_40;
} else if (bw == 80) {
chspec_bw = WL_CHANSPEC_BW_80;
} else if (bw == 160) {
chspec_bw = WL_CHANSPEC_BW_160;
} else if (WFC_BW_EQ(bw, 320)) {
chspec_bw = WL_CHANSPEC_BW_320;
} else {
return 0;
}
/* So far we have <band>g<chan>/<bw>
* Can now be followed by u/l if bw = 40,
*/
c = tolower((int)a[0]);
/* if we have a 2g/40 channel, we should have a l/u spec now */
if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) {
if (c == 'u' || c == 'l') {
a ++; /* consume the u/l char */
sb_ul = c;
goto done_read;
}
}
/* check for 80+80 or 160+160 */
if (c == '+') {
return 0;
}
/* if we have a 6g/320 channel, we should have a 'o' spec now for
* overlapped { 63, 127, 191 } channel.
*/
if (chspec_band == WL_CHANSPEC_BAND_6G && bw == 320) {
if (c == 'o') {
a ++; /* consume the 'o' char */
overlap = c;
goto done_read;
}
}
done_read:
/* skip trailing white space */
while (a[0] == ' ') {
a ++;
}
/* must be end of string */
if (a[0] != '\0')
return 0;
/* Now have all the chanspec string parts read;
* chspec_band, pri_ch, chspec_bw, sb_ul.
* chspec_band and chspec_bw are chanspec values.
* Need to convert pri_ch, and sb_ul into
* a center channel (or two) and sideband.
*/
/* if a sb u/l string was given, just use that,
* guaranteed to be bw = 40 by string parse.
*/
if (chspec_band != WL_CHANSPEC_BAND_6G && sb_ul != '\0') {
if (sb_ul == 'l') {
chspec_sb = WL_CHANSPEC_CTL_SB_LLL;
} else if (sb_ul == 'u') {
chspec_sb = WL_CHANSPEC_CTL_SB_LLU;
}
chspec = wf_create_40MHz_chspec_primary_sb(pri_ch, chspec_sb, chspec_band);
} else if (chspec_bw == WL_CHANSPEC_BW_20) {
/* if the bw is 20, only need the primary channel and band */
chspec = wf_create_20MHz_chspec(pri_ch, chspec_band);
} else {
uint16 flags = 0;
/* If the bw is 40/80/160 (and not 40MHz 2G), the channels are
* non-overlapping in 5G or 6G bands. Each primary channel is contained
* in only one higher bandwidth channel. The wf_create_chspec_from_primary()
* will create the chanspec. 2G 40MHz is handled just above, assuming a {u,l}
* sub-band spec was given.
* However 6g 320Mhz channels are overlapping and in case they are belongs to
* center channel set { 63, 127, 191 }, overlap should come with 'o'.
*/
if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320) && overlap == 'o') {
flags |= WF_CHANSPEC_FLAG_OVERLAPPED320;
}
chspec = wf_create_chspec_from_primary(pri_ch, chspec_bw, chspec_band,
flags);
}
if (wf_chspec_malformed(chspec))
return 0;
return chspec;
}
/**
* Verify the chanspec is using a legal set of parameters, i.e. that the
* chanspec specified a band, bw, pri_sb and channel and that the
* combination could be legal given any set of circumstances.
*
* @param chanspec the chanspec to check
*
* @return Returns TRUE if the chanspec is malformed, FALSE if it looks good.
*/
bool
BCMPOSTTRAPFASTPATH(wf_chspec_malformed)(chanspec_t chanspec)
{
uint chspec_bw = CHSPEC_BW(chanspec);
uint chspec_sb;
if (CHSPEC_IS2G(chanspec)) {
/* must be valid bandwidth for 2G */
if (!BW_LE40(chspec_bw)) {
return TRUE;
}
/* check for invalid channel number */
if (wf_chspec_center_channel(chanspec) == INVCHANNEL) {
return TRUE;
}
} else if (CHSPEC_IS5G(chanspec) || CHSPEC_IS6G(chanspec)) {
if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
uint ch_id;
ch_id = CHSPEC_320_CHAN(chanspec);
/* channel IDs in 320 must be in range */
if (CHSPEC_IS6G(chanspec)) {
if (ch_id > WF_NUM_6G_320M_CHAN_ID_MAX) {
/* bad 320MHz channel ID for the band */
return TRUE;
}
} else {
return TRUE;
}
} else if (WFC_NCBW_EQ(chspec_bw, WL_CHANSPEC_BW_8080)) {
uint ch0_id, ch1_id;
ch0_id = CHSPEC_CHAN0(chanspec);
ch1_id = CHSPEC_CHAN1(chanspec);
/* channel IDs in 80+80 must be in range */
if (CHSPEC_IS5G(chanspec) &&
(ch0_id >= WF_NUM_5G_80M_CHANS || ch1_id >= WF_NUM_5G_80M_CHANS)) {
/* bad 80MHz channel ID for the band */
return TRUE;
}
if (CHSPEC_IS6G(chanspec) &&
(ch0_id >= WF_NUM_6G_80M_CHANS || ch1_id >= WF_NUM_6G_80M_CHANS)) {
/* bad 80MHz channel ID for the band */
return TRUE;
}
} else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 ||
chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) {
/* check for invalid channel number */
if (wf_chspec_center_channel(chanspec) == INVCHANNEL) {
return TRUE;
}
} else {
/* invalid bandwidth */
return TRUE;
}
} else {
/* must be a valid band */
return TRUE;
}
/* retrive sideband */
if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
chspec_sb = CHSPEC_320_SB(chanspec);
} else {
chspec_sb = CHSPEC_CTL_SB(chanspec);
}
/* side band needs to be consistent with bandwidth */
if (chspec_bw == WL_CHANSPEC_BW_20) {
if (chspec_sb != WL_CHANSPEC_CTL_SB_LLL)
return TRUE;
} else if (chspec_bw == WL_CHANSPEC_BW_40) {
if (chspec_sb > WL_CHANSPEC_CTL_SB_LLU)
return TRUE;
} else if (chspec_bw == WL_CHANSPEC_BW_80 ||
WFC_NCBW_EQ(chspec_bw, WL_CHANSPEC_BW_8080)) {
/* both 80MHz and 80+80MHz use 80MHz side bands.
* 80+80 SB info is relative to the primary 80MHz sub-band.
*/
if (chspec_sb > WL_CHANSPEC_CTL_SB_LUU)
return TRUE;
} else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
/* FIXME: define the max sideband index */
ASSERT_FP((chspec_sb >> WL_CHANSPEC_320_SB_SHIFT) <= 15);
}
return FALSE;
}
/**
* Verify the chanspec specifies a valid channel according to 802.11.
*
* @param chanspec the chanspec to check
*
* @return Returns TRUE if the chanspec is a valid 802.11 channel
*/
bool
wf_chspec_valid(chanspec_t chanspec)
{
chanspec_band_t chspec_band = CHSPEC_BAND(chanspec);
chanspec_bw_t chspec_bw = CHSPEC_BW(chanspec);
uint chspec_ch = -1;
if (wf_chspec_malformed(chanspec)) {
return FALSE;
}
chspec_ch = wf_chspec_center_channel(chanspec);
/* After the malformed check, we know that we have
* a valid band field,
* a valid bandwidth for the band,
* and a valid sub-band value for the bandwidth.
*
* Since all sub-band specs are valid for any channel, the only thing remaining to
* check is that
* the 20MHz channel,
* or the center channel for higher BW,
* or both center channels for an 80+80MHz channel,
* are valid for the specified band.
* Also, 80+80MHz channels need to be non-contiguous.
*/
if (chspec_bw == WL_CHANSPEC_BW_20) {
return wf_valid_20MHz_chan(chspec_ch, chspec_band);
} else if (chspec_bw == WL_CHANSPEC_BW_40) {
return wf_valid_40MHz_center_chan(chspec_ch, chspec_band);
} else if (chspec_bw == WL_CHANSPEC_BW_80) {
return wf_valid_80MHz_center_chan(chspec_ch, chspec_band);
} else if (chspec_bw == WL_CHANSPEC_BW_160) {
return wf_valid_160MHz_center_chan(chspec_ch, chspec_band);
} else if (WFC_BW_EQ(chspec_bw, WL_CHANSPEC_BW_320)) {
return wf_valid_320MHz_center_chan(chspec_ch, chspec_band);
} else if (WFC_NCBW_EQ(chspec_bw, WL_CHANSPEC_BW_8080)) {
uint16 ch0 = 0;
uint16 ch1 = 0;
/* get the center channels for each frequency segment */
if (CHSPEC_IS5G(chanspec)) {
ch0 = wf_chspec_5G_id80_to_ch(CHSPEC_CHAN0(chanspec));
ch1 = wf_chspec_5G_id80_to_ch(CHSPEC_CHAN1(chanspec));
} else if (CHSPEC_IS6G(chanspec)) {
ch0 = wf_chspec_6G_id80_to_ch(CHSPEC_CHAN0(chanspec));
ch1 = wf_chspec_6G_id80_to_ch(CHSPEC_CHAN1(chanspec));
} else {
return FALSE;
}
/* the two channels must be separated by more than 80MHz by VHT req */
if ((ch1 > ch0 + CH_80MHZ_APART) ||
(ch0 > ch1 + CH_80MHZ_APART))
return TRUE;
}
return FALSE;