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