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