Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/next/external/cache/sources/wl/shared/bcmwifi_channels.c
Views: 3959
/*1* Misc utility routines used by kernel or app-level.2* Contents are wifi-specific, used by any kernel or app-level3* software that might want wifi things as it grows.4*5* $Copyright Open Broadcom Corporation$6* $Id: bcmwifi_channels.c 309193 2012-01-19 00:03:57Z stafford $7*/89#include <bcm_cfg.h>10#include <typedefs.h>11#include <bcmutils.h>1213#ifdef BCMDRIVER14#include <osl.h>15#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))16#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))17#elif defined(__IOPOS__)18#include <bcmutils.h>19#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))20#define tolower(c) (bcm_tolower((c)))21/* XXX Why isn't ASSERT always available as part of a non BCMDRIVER build?22* It seems like there ought to be a non BCMDRIVER osl.23*/24#ifndef ASSERT25#define ASSERT(exp)26#endif27#else28#include <stdio.h>29#include <stdlib.h>30#include <ctype.h>31/* XXX Why isn't ASSERT always available as part of a non BCMDRIVER build?32* It seems like there ought to be a non BCMDRIVER osl.33*/34#ifndef ASSERT35#define ASSERT(exp)36#endif37#endif /* BCMDRIVER */3839#ifdef _bcmwifi_c_40/* temporary for transitional compatibility */41#include <bcmwifi.h>42#else43#include <bcmwifi_channels.h>44#endif4546#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL))47#include <bcmstdlib.h> /* For wl/exe/GNUmakefile.brcm_wlu and GNUmakefile.wlm_dll */48#endif4950#ifndef D11AC_IOTYPES5152/* Definitions for legacy Chanspec type */5354/* Chanspec ASCII representation:55* <channel><band><bandwidth><ctl-sideband>56* digit [AB] [N] [UL]57*58* <channel>: channel number of the 10MHz or 20MHz channel,59* or control sideband channel of 40MHz channel.60* <band>: A for 5GHz, B for 2.4GHz61* <bandwidth>: N for 10MHz, nothing for 20MHz or 40MHz62* (ctl-sideband spec implies 40MHz)63* <ctl-sideband>: U for upper, L for lower64*65* <band> may be omitted on input, and will be assumed to be66* 2.4GHz if channel number <= 14.67*68* Examples:69* 8 -> 2.4GHz channel 8, 20MHz70* 8b -> 2.4GHz channel 8, 20MHz71* 8l -> 2.4GHz channel 8, 40MHz, lower ctl sideband72* 8a -> 5GHz channel 8 (low 5 GHz band), 20MHz73* 36 -> 5GHz channel 36, 20MHz74* 36l -> 5GHz channel 36, 40MHz, lower ctl sideband75* 40u -> 5GHz channel 40, 40MHz, upper ctl sideband76* 180n -> channel 180, 10MHz77*/787980/* given a chanspec and a string buffer, format the chanspec as a81* string, and return the original pointer a.82* Min buffer length must be CHANSPEC_STR_LEN.83* On error return NULL84*/85char *86wf_chspec_ntoa(chanspec_t chspec, char *buf)87{88const char *band, *bw, *sb;89uint channel;9091band = "";92bw = "";93sb = "";94channel = CHSPEC_CHANNEL(chspec);95/* check for non-default band spec */96if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) ||97(CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL))98band = (CHSPEC_IS2G(chspec)) ? "b" : "a";99if (CHSPEC_IS40(chspec)) {100if (CHSPEC_SB_UPPER(chspec)) {101sb = "u";102channel += CH_10MHZ_APART;103} else {104sb = "l";105channel -= CH_10MHZ_APART;106}107} else if (CHSPEC_IS10(chspec)) {108bw = "n";109}110111/* Outputs a max of 6 chars including '\0' */112snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb);113return (buf);114}115116/* given a chanspec string, convert to a chanspec.117* On error return 0118*/119chanspec_t120wf_chspec_aton(const char *a)121{122char *endp = NULL;123uint channel, band, bw, ctl_sb;124char c;125126channel = strtoul(a, &endp, 10);127128/* check for no digits parsed */129if (endp == a)130return 0;131132if (channel > MAXCHANNEL)133return 0;134135band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);136bw = WL_CHANSPEC_BW_20;137ctl_sb = WL_CHANSPEC_CTL_SB_NONE;138139a = endp;140141c = tolower(a[0]);142if (c == '\0')143goto done;144145/* parse the optional ['A' | 'B'] band spec */146if (c == 'a' || c == 'b') {147band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;148a++;149c = tolower(a[0]);150if (c == '\0')151goto done;152}153154/* parse bandwidth 'N' (10MHz) or 40MHz ctl sideband ['L' | 'U'] */155if (c == 'n') {156bw = WL_CHANSPEC_BW_10;157} else if (c == 'l') {158bw = WL_CHANSPEC_BW_40;159ctl_sb = WL_CHANSPEC_CTL_SB_LOWER;160/* adjust channel to center of 40MHz band */161if (channel <= (MAXCHANNEL - CH_20MHZ_APART))162channel += CH_10MHZ_APART;163else164return 0;165} else if (c == 'u') {166bw = WL_CHANSPEC_BW_40;167ctl_sb = WL_CHANSPEC_CTL_SB_UPPER;168/* adjust channel to center of 40MHz band */169if (channel > CH_20MHZ_APART)170channel -= CH_10MHZ_APART;171else172return 0;173} else {174return 0;175}176177done:178return (channel | band | bw | ctl_sb);179}180181/*182* Verify the chanspec is using a legal set of parameters, i.e. that the183* chanspec specified a band, bw, ctl_sb and channel and that the184* combination could be legal given any set of circumstances.185* RETURNS: TRUE is the chanspec is malformed, false if it looks good.186*/187bool188wf_chspec_malformed(chanspec_t chanspec)189{190/* must be 2G or 5G band */191if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))192return TRUE;193/* must be 20 or 40 bandwidth */194if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))195return TRUE;196197/* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */198if (CHSPEC_IS20(chanspec)) {199if (!CHSPEC_SB_NONE(chanspec))200return TRUE;201} else {202if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec))203return TRUE;204}205206return FALSE;207}208209/*210* This function returns the channel number that control traffic is being sent on, for legacy211* channels this is just the channel number, for 40MHZ channels it is the upper or lower 20MHZ212* sideband depending on the chanspec selected213*/214uint8215wf_chspec_ctlchan(chanspec_t chspec)216{217uint8 ctl_chan;218219/* Is there a sideband ? */220if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) {221return CHSPEC_CHANNEL(chspec);222} else {223/* we only support 40MHZ with sidebands */224ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40);225/* chanspec channel holds the centre frequency, use that and the226* side band information to reconstruct the control channel number227*/228if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) {229/* control chan is the upper 20 MHZ SB of the 40MHZ channel */230ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec));231} else {232ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER);233/* control chan is the lower 20 MHZ SB of the 40MHZ channel */234ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec));235}236}237238return ctl_chan;239}240241chanspec_t242wf_chspec_ctlchspec(chanspec_t chspec)243{244chanspec_t ctl_chspec = 0;245uint8 channel;246247ASSERT(!wf_chspec_malformed(chspec));248249/* Is there a sideband ? */250if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) {251return chspec;252} else {253if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) {254channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec));255} else {256channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec));257}258ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;259ctl_chspec |= CHSPEC_BAND(chspec);260}261return ctl_chspec;262}263264#else /* D11AC_IOTYPES */265266/* Definitions for D11AC capable Chanspec type */267268/* Chanspec ASCII representation with 802.11ac capability:269* [<band> 'g'] <channel> ['/'<bandwidth> [<ctl-sideband>]['/'<1st80channel>'-'<2nd80channel>]]270*271* <band>:272* (optional) 2, 3, 4, 5 for 2.4GHz, 3GHz, 4GHz, and 5GHz respectively.273* Default value is 2g if channel <= 14, otherwise 5g.274* <channel>:275* channel number of the 5MHz, 10MHz, 20MHz channel,276* or primary channel of 40MHz, 80MHz, 160MHz, or 80+80MHz channel.277* <bandwidth>:278* (optional) 5, 10, 20, 40, 80, 160, or 80+80. Default value is 20.279* <primary-sideband>:280* (only for 2.4GHz band 40MHz) U for upper sideband primary, L for lower.281*282* For 2.4GHz band 40MHz channels, the same primary channel may be the283* upper sideband for one 40MHz channel, and the lower sideband for an284* overlapping 40MHz channel. The U/L disambiguates which 40MHz channel285* is being specified.286*287* For 40MHz in the 5GHz band and all channel bandwidths greater than288* 40MHz, the U/L specificaion is not allowed since the channels are289* non-overlapping and the primary sub-band is derived from its290* position in the wide bandwidth channel.291*292* <1st80Channel>:293* <2nd80Channel>:294* Required for 80+80, otherwise not allowed.295* Specifies the center channel of the first and second 80MHz band.296*297* In its simplest form, it is a 20MHz channel number, with the implied band298* of 2.4GHz if channel number <= 14, and 5GHz otherwise.299*300* To allow for backward compatibility with scripts, the old form for301* 40MHz channels is also allowed: <channel><ctl-sideband>302*303* <channel>:304* primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz305* <ctl-sideband>:306* "U" for upper, "L" for lower (or lower case "u" "l")307*308* 5 GHz Examples:309* Chanspec BW Center Ch Channel Range Primary Ch310* 5g8 20MHz 8 - -311* 52 20MHz 52 - -312* 52/40 40MHz 54 52-56 52313* 56/40 40MHz 54 52-56 56314* 52/80 80MHz 58 52-64 52315* 56/80 80MHz 58 52-64 56316* 60/80 80MHz 58 52-64 60317* 64/80 80MHz 58 52-64 64318* 52/160 160MHz 50 36-64 52319* 36/160 160MGz 50 36-64 36320* 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36321*322* 2 GHz Examples:323* Chanspec BW Center Ch Channel Range Primary Ch324* 2g8 20MHz 8 - -325* 8 20MHz 8 - -326* 6 20MHz 6 - -327* 6/40l 40MHz 8 6-10 6328* 6l 40MHz 8 6-10 6329* 6/40u 40MHz 4 2-6 6330* 6u 40MHz 4 2-6 6331*/332333/* bandwidth ASCII string */334static const char *wf_chspec_bw_str[] =335{336"5",337"10",338"20",339"40",340"80",341"160",342"80+80",343"na"344};345346static const uint8 wf_chspec_bw_mhz[] =347{5, 10, 20, 40, 80, 160, 160};348349#define WF_NUM_BW \350(sizeof(wf_chspec_bw_mhz)/sizeof(uint8))351352/* 40MHz channels in 5GHz band */353static const uint8 wf_5g_40m_chans[] =354{38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159};355#define WF_NUM_5G_40M_CHANS \356(sizeof(wf_5g_40m_chans)/sizeof(uint8))357358/* 80MHz channels in 5GHz band */359static const uint8 wf_5g_80m_chans[] =360{42, 58, 106, 122, 138, 155};361#define WF_NUM_5G_80M_CHANS \362(sizeof(wf_5g_80m_chans)/sizeof(uint8))363364/* 160MHz channels in 5GHz band */365static const uint8 wf_5g_160m_chans[] =366{50, 114};367#define WF_NUM_5G_160M_CHANS \368(sizeof(wf_5g_160m_chans)/sizeof(uint8))369370371/* convert bandwidth from chanspec to MHz */372static uint373bw_chspec_to_mhz(chanspec_t chspec)374{375uint bw;376377bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT;378return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]);379}380381/* bw in MHz, return the channel count from the center channel to the382* the channel at the edge of the band383*/384static uint8385center_chan_to_edge(uint bw)386{387/* edge channels separated by BW - 10MHz on each side388* delta from cf to edge is half of that,389* MHz to channel num conversion is 5MHz/channel390*/391return (uint8)(((bw - 20) / 2) / 5);392}393394/* return channel number of the low edge of the band395* given the center channel and BW396*/397static uint8398channel_low_edge(uint center_ch, uint bw)399{400return (uint8)(center_ch - center_chan_to_edge(bw));401}402403/* return side band number given center channel and control channel404* return -1 on error405*/406static int407channel_to_sb(uint center_ch, uint ctl_ch, uint bw)408{409uint lowest = channel_low_edge(center_ch, bw);410uint sb;411412if ((ctl_ch - lowest) % 4) {413/* bad ctl channel, not mult 4 */414return -1;415}416417sb = ((ctl_ch - lowest) / 4);418419/* sb must be a index to a 20MHz channel in range */420if (sb >= (bw / 20)) {421/* ctl_ch must have been too high for the center_ch */422return -1;423}424425return sb;426}427428/* return control channel given center channel and side band */429static uint8430channel_to_ctl_chan(uint center_ch, uint bw, uint sb)431{432return (uint8)(channel_low_edge(center_ch, bw) + sb * 4);433}434435/* return index of 80MHz channel from channel number436* return -1 on error437*/438static int439channel_80mhz_to_id(uint ch)440{441uint i;442for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) {443if (ch == wf_5g_80m_chans[i])444return i;445}446447return -1;448}449450/* given a chanspec and a string buffer, format the chanspec as a451* string, and return the original pointer a.452* Min buffer length must be CHANSPEC_STR_LEN.453* On error return NULL454*/455char *456wf_chspec_ntoa(chanspec_t chspec, char *buf)457{458const char *band;459uint ctl_chan;460461if (wf_chspec_malformed(chspec))462return NULL;463464band = "";465466/* check for non-default band spec */467if ((CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) ||468(CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL))469band = (CHSPEC_IS2G(chspec)) ? "2g" : "5g";470471/* ctl channel */472ctl_chan = wf_chspec_ctlchan(chspec);473474/* bandwidth and ctl sideband */475if (CHSPEC_IS20(chspec)) {476snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, ctl_chan);477} else if (!CHSPEC_IS8080(chspec)) {478const char *bw;479const char *sb = "";480481bw = wf_chspec_bw_str[(chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT];482483#ifdef CHANSPEC_NEW_40MHZ_FORMAT484/* ctl sideband string if needed for 2g 40MHz */485if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) {486sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";487}488489snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, ctl_chan, bw, sb);490#else491/* ctl sideband string instead of BW for 40MHz */492if (CHSPEC_IS40(chspec)) {493sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l";494snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, ctl_chan, sb);495} else {496snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, ctl_chan, bw);497}498#endif /* CHANSPEC_NEW_40MHZ_FORMAT */499500} else {501/* 80+80 */502uint chan1 = (chspec & WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT;503uint chan2 = (chspec & WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT;504505/* convert to channel number */506chan1 = (chan1 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan1] : 0;507chan2 = (chan2 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan2] : 0;508509/* Outputs a max of CHANSPEC_STR_LEN chars including '\0' */510snprintf(buf, CHANSPEC_STR_LEN, "%d/80+80/%d-%d", ctl_chan, chan1, chan2);511}512513return (buf);514}515516static int517read_uint(const char **p, unsigned int *num)518{519unsigned long val;520char *endp = NULL;521522val = strtoul(*p, &endp, 10);523/* if endp is the initial pointer value, then a number was not read */524if (endp == *p)525return 0;526527/* advance the buffer pointer to the end of the integer string */528*p = endp;529/* return the parsed integer */530*num = (unsigned int)val;531532return 1;533}534535/* given a chanspec string, convert to a chanspec.536* On error return 0537*/538chanspec_t539wf_chspec_aton(const char *a)540{541chanspec_t chspec;542uint chspec_ch, chspec_band, bw, chspec_bw, chspec_sb;543uint num, ctl_ch;544uint ch1, ch2;545char c, sb_ul = '\0';546int i;547548bw = 20;549chspec_sb = 0;550chspec_ch = ch1 = ch2 = 0;551552/* parse channel num or band */553if (!read_uint(&a, &num))554return 0;555556/* if we are looking at a 'g', then the first number was a band */557c = tolower((int)a[0]);558if (c == 'g') {559a ++; /* consume the char */560561/* band must be "2" or "5" */562if (num == 2)563chspec_band = WL_CHANSPEC_BAND_2G;564else if (num == 5)565chspec_band = WL_CHANSPEC_BAND_5G;566else567return 0;568569/* read the channel number */570if (!read_uint(&a, &ctl_ch))571return 0;572573c = tolower((int)a[0]);574}575else {576/* first number is channel, use default for band */577ctl_ch = num;578chspec_band = ((ctl_ch <= CH_MAX_2G_CHANNEL) ?579WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);580}581582if (c == '\0') {583/* default BW of 20MHz */584chspec_bw = WL_CHANSPEC_BW_20;585goto done_read;586}587588a ++; /* consume the 'u','l', or '/' */589590/* check 'u'/'l' */591if (c == 'u' || c == 'l') {592sb_ul = c;593chspec_bw = WL_CHANSPEC_BW_40;594goto done_read;595}596597/* next letter must be '/' */598if (c != '/')599return 0;600601/* read bandwidth */602if (!read_uint(&a, &bw))603return 0;604605/* convert to chspec value */606if (bw == 20) {607chspec_bw = WL_CHANSPEC_BW_20;608} else if (bw == 40) {609chspec_bw = WL_CHANSPEC_BW_40;610} else if (bw == 80) {611chspec_bw = WL_CHANSPEC_BW_80;612} else if (bw == 160) {613chspec_bw = WL_CHANSPEC_BW_160;614} else {615return 0;616}617618/* So far we have <band>g<chan>/<bw>619* Can now be followed by u/l if bw = 40,620* or '+80' if bw = 80, to make '80+80' bw.621*/622623c = tolower((int)a[0]);624625/* if we have a 2g/40 channel, we should have a l/u spec now */626if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) {627if (c == 'u' || c == 'l') {628a ++; /* consume the u/l char */629sb_ul = c;630goto done_read;631}632}633634/* check for 80+80 */635if (c == '+') {636/* 80+80 */637static const char *plus80 = "80/";638639/* must be looking at '+80/'640* check and consume this string.641*/642chspec_bw = WL_CHANSPEC_BW_8080;643644a ++; /* consume the char '+' */645646/* consume the '80/' string */647for (i = 0; i < 3; i++) {648if (*a++ != *plus80++) {649return 0;650}651}652653/* read primary 80MHz channel */654if (!read_uint(&a, &ch1))655return 0;656657/* must followed by '-' */658if (a[0] != '-')659return 0;660a ++; /* consume the char */661662/* read secondary 80MHz channel */663if (!read_uint(&a, &ch2))664return 0;665}666667done_read:668/* skip trailing white space */669while (a[0] == ' ') {670a ++;671}672673/* must be end of string */674if (a[0] != '\0')675return 0;676677/* Now have all the chanspec string parts read;678* chspec_band, ctl_ch, chspec_bw, sb_ul, ch1, ch2.679* chspec_band and chspec_bw are chanspec values.680* Need to convert ctl_ch, sb_ul, and ch1,ch2 into681* a center channel (or two) and sideband.682*/683684/* if a sb u/l string was given, just use that,685* guaranteed to be bw = 40 by sting parse.686*/687if (sb_ul != '\0') {688if (sb_ul == 'l') {689chspec_ch = UPPER_20_SB(ctl_ch);690chspec_sb = WL_CHANSPEC_CTL_SB_LLL;691} else if (sb_ul == 'u') {692chspec_ch = LOWER_20_SB(ctl_ch);693chspec_sb = WL_CHANSPEC_CTL_SB_LLU;694}695}696/* if the bw is 20, center and sideband are trivial */697else if (chspec_bw == WL_CHANSPEC_BW_20) {698chspec_ch = ctl_ch;699chspec_sb = 0;700}701/* if the bw is 40/80/160, not 80+80, a single method702* can be used to to find the center and sideband703*/704else if (chspec_bw != WL_CHANSPEC_BW_8080) {705/* figure out ctl sideband based on ctl channel and bandwidth */706const uint8 *center_ch = NULL;707int num_ch = 0;708int sb = -1;709710if (chspec_bw == WL_CHANSPEC_BW_40) {711center_ch = wf_5g_40m_chans;712num_ch = WF_NUM_5G_40M_CHANS;713} else if (chspec_bw == WL_CHANSPEC_BW_80) {714center_ch = wf_5g_80m_chans;715num_ch = WF_NUM_5G_80M_CHANS;716} else if (chspec_bw == WL_CHANSPEC_BW_160) {717center_ch = wf_5g_160m_chans;718num_ch = WF_NUM_5G_160M_CHANS;719} else {720return 0;721}722723for (i = 0; i < num_ch; i ++) {724sb = channel_to_sb(center_ch[i], ctl_ch, bw);725if (sb >= 0) {726chspec_ch = center_ch[i];727chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT;728break;729}730}731732/* check for no matching sb/center */733if (sb < 0) {734return 0;735}736}737/* Otherwise, bw is 80+80. Figure out channel pair and sb */738else {739int ch1_id = 0, ch2_id = 0;740int sb;741742ch1_id = channel_80mhz_to_id(ch1);743ch2_id = channel_80mhz_to_id(ch2);744745/* validate channels */746if (ch1 >= ch2 || ch1_id < 0 || ch2_id < 0)747return 0;748749/* combined channel in chspec */750chspec_ch = (((uint16)ch1_id << WL_CHANSPEC_CHAN1_SHIFT) |751((uint16)ch2_id << WL_CHANSPEC_CHAN2_SHIFT));752753/* figure out ctl sideband */754755/* does the primary channel fit with the 1st 80MHz channel ? */756sb = channel_to_sb(ch1, ctl_ch, bw);757if (sb < 0) {758/* no, so does the primary channel fit with the 2nd 80MHz channel ? */759sb = channel_to_sb(ch2, ctl_ch, bw);760if (sb < 0) {761/* no match for ctl_ch to either 80MHz center channel */762return 0;763}764/* sb index is 0-3 for the low 80MHz channel, and 4-7 for765* the high 80MHz channel. Add 4 to to shift to high set.766*/767sb += 4;768}769770chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT;771}772773chspec = (chspec_ch | chspec_band | chspec_bw | chspec_sb);774775if (wf_chspec_malformed(chspec))776return 0;777778return chspec;779}780781/*782* Verify the chanspec is using a legal set of parameters, i.e. that the783* chanspec specified a band, bw, ctl_sb and channel and that the784* combination could be legal given any set of circumstances.785* RETURNS: TRUE is the chanspec is malformed, false if it looks good.786*/787bool788wf_chspec_malformed(chanspec_t chanspec)789{790uint chspec_bw = CHSPEC_BW(chanspec);791uint chspec_ch = CHSPEC_CHANNEL(chanspec);792793/* must be 2G or 5G band */794if (CHSPEC_IS2G(chanspec)) {795/* must be valid bandwidth */796if (chspec_bw != WL_CHANSPEC_BW_20 &&797chspec_bw != WL_CHANSPEC_BW_40) {798return TRUE;799}800} else if (CHSPEC_IS5G(chanspec)) {801if (chspec_bw == WL_CHANSPEC_BW_8080) {802uint ch1_id, ch2_id;803804/* channel number in 80+80 must be in range */805ch1_id = CHSPEC_CHAN1(chanspec);806ch2_id = CHSPEC_CHAN2(chanspec);807if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS)808return TRUE;809810/* ch2 must be above ch1 for the chanspec */811if (ch2_id <= ch1_id)812return TRUE;813} else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 ||814chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) {815816if (chspec_ch > MAXCHANNEL) {817return TRUE;818}819} else {820/* invalid bandwidth */821return TRUE;822}823} else {824/* must be 2G or 5G band */825return TRUE;826}827828/* side band needs to be consistent with bandwidth */829if (chspec_bw == WL_CHANSPEC_BW_20) {830if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL)831return TRUE;832} else if (chspec_bw == WL_CHANSPEC_BW_40) {833if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU)834return TRUE;835} else if (chspec_bw == WL_CHANSPEC_BW_80) {836if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU)837return TRUE;838}839840return FALSE;841}842843/*844* Verify the chanspec specifies a valid channel according to 802.11.845* RETURNS: TRUE if the chanspec is a valid 802.11 channel846*/847bool848wf_chspec_valid(chanspec_t chanspec)849{850uint chspec_bw = CHSPEC_BW(chanspec);851uint chspec_ch = CHSPEC_CHANNEL(chanspec);852853if (wf_chspec_malformed(chanspec))854return FALSE;855856if (CHSPEC_IS2G(chanspec)) {857/* must be valid bandwidth and channel range */858if (chspec_bw == WL_CHANSPEC_BW_20) {859if (chspec_ch >= 1 && chspec_ch <= 14)860return TRUE;861} else if (chspec_bw == WL_CHANSPEC_BW_40) {862if (chspec_ch >= 3 && chspec_ch <= 11)863return TRUE;864}865} else if (CHSPEC_IS5G(chanspec)) {866if (chspec_bw == WL_CHANSPEC_BW_8080) {867uint16 ch1, ch2;868869ch1 = wf_5g_80m_chans[CHSPEC_CHAN1(chanspec)];870ch2 = wf_5g_80m_chans[CHSPEC_CHAN2(chanspec)];871872/* the two channels must be separated by more than 80MHz by VHT req,873* and ch2 above ch1 for the chanspec874*/875if (ch2 > ch1 + CH_80MHZ_APART)876return TRUE;877} else {878const uint8 *center_ch;879uint num_ch, i;880881if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40) {882center_ch = wf_5g_40m_chans;883num_ch = WF_NUM_5G_40M_CHANS;884} else if (chspec_bw == WL_CHANSPEC_BW_80) {885center_ch = wf_5g_80m_chans;886num_ch = WF_NUM_5G_80M_CHANS;887} else if (chspec_bw == WL_CHANSPEC_BW_160) {888center_ch = wf_5g_160m_chans;889num_ch = WF_NUM_5G_160M_CHANS;890} else {891/* invalid bandwidth */892return FALSE;893}894895/* check for a valid center channel */896if (chspec_bw == WL_CHANSPEC_BW_20) {897/* We don't have an array of legal 20MHz 5G channels, but they are898* each side of the legal 40MHz channels. Check the chanspec899* channel against either side of the 40MHz channels.900*/901for (i = 0; i < num_ch; i ++) {902if (chspec_ch == (uint)LOWER_20_SB(center_ch[i]) ||903chspec_ch == (uint)UPPER_20_SB(center_ch[i]))904break; /* match found */905}906907if (i == num_ch) {908/* check for legacy JP channels on failure */909if (chspec_ch == 34 || chspec_ch == 38 ||910chspec_ch == 42 || chspec_ch == 46)911i = 0;912}913} else {914/* check the chanspec channel to each legal channel */915for (i = 0; i < num_ch; i ++) {916if (chspec_ch == center_ch[i])917break; /* match found */918}919}920921if (i < num_ch) {922/* match found */923return TRUE;924}925}926}927928return FALSE;929}930931/*932* This function returns the channel number that control traffic is being sent on, for 20MHz933* channels this is just the channel number, for 40MHZ, 80MHz, 160MHz channels it is the 20MHZ934* sideband depending on the chanspec selected935*/936uint8937wf_chspec_ctlchan(chanspec_t chspec)938{939uint center_chan;940uint bw_mhz;941uint sb;942943ASSERT(!wf_chspec_malformed(chspec));944945/* Is there a sideband ? */946if (CHSPEC_IS20(chspec)) {947return CHSPEC_CHANNEL(chspec);948} else {949sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT;950951if (CHSPEC_IS8080(chspec)) {952bw_mhz = 80;953954if (sb < 4) {955center_chan = CHSPEC_CHAN1(chspec);956}957else {958center_chan = CHSPEC_CHAN2(chspec);959sb -= 4;960}961962/* convert from channel index to channel number */963center_chan = wf_5g_80m_chans[center_chan];964}965else {966bw_mhz = bw_chspec_to_mhz(chspec);967center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT;968}969970return (channel_to_ctl_chan(center_chan, bw_mhz, sb));971}972}973974/*975* This function returns the chanspec of the control channel of a given chanspec976*/977chanspec_t978wf_chspec_ctlchspec(chanspec_t chspec)979{980chanspec_t ctl_chspec = chspec;981uint8 ctl_chan;982983ASSERT(!wf_chspec_malformed(chspec));984985/* Is there a sideband ? */986if (!CHSPEC_IS20(chspec)) {987ctl_chan = wf_chspec_ctlchan(chspec);988ctl_chspec = ctl_chan | WL_CHANSPEC_BW_20;989ctl_chspec |= CHSPEC_BAND(chspec);990}991return ctl_chspec;992}993994/* return chanspec given control channel and bandwidth995* return 0 on error996*/997uint16998wf_channel2chspec(uint ctl_ch, uint bw)999{1000uint16 chspec;1001const uint8 *center_ch = NULL;1002int num_ch = 0;1003int sb = -1;1004int i = 0;10051006chspec = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);10071008chspec |= bw;10091010if (bw == WL_CHANSPEC_BW_40) {1011center_ch = wf_5g_40m_chans;1012num_ch = WF_NUM_5G_40M_CHANS;1013bw = 40;1014} else if (bw == WL_CHANSPEC_BW_80) {1015center_ch = wf_5g_80m_chans;1016num_ch = WF_NUM_5G_80M_CHANS;1017bw = 80;1018} else if (bw == WL_CHANSPEC_BW_160) {1019center_ch = wf_5g_160m_chans;1020num_ch = WF_NUM_5G_160M_CHANS;1021bw = 160;1022} else if (bw == WL_CHANSPEC_BW_20) {1023chspec |= ctl_ch;1024return chspec;1025} else {1026return 0;1027}10281029for (i = 0; i < num_ch; i ++) {1030sb = channel_to_sb(center_ch[i], ctl_ch, bw);1031if (sb >= 0) {1032chspec |= center_ch[i];1033chspec |= (sb << WL_CHANSPEC_CTL_SB_SHIFT);1034break;1035}1036}10371038/* check for no matching sb/center */1039if (sb < 0) {1040return 0;1041}10421043return chspec;1044}10451046#endif /* D11AC_IOTYPES */10471048/*1049* This function returns the chanspec for the primary 40MHz of an 80MHz channel.1050* The control sideband specifies the same 20MHz channel that the 80MHz channel is using1051* as the primary 20MHz channel.1052*/1053extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec)1054{1055chanspec_t chspec40 = chspec;1056#ifdef D11AC_IOTYPES1057uint center_chan;1058uint sb;1059ASSERT(!wf_chspec_malformed(chspec));10601061if (CHSPEC_IS80(chspec)) {1062center_chan = CHSPEC_CHANNEL(chspec);1063sb = CHSPEC_CTL_SB(chspec);10641065if (sb == WL_CHANSPEC_CTL_SB_UL) {1066/* Primary 40MHz is on upper side */1067sb = WL_CHANSPEC_CTL_SB_L;1068center_chan += CH_20MHZ_APART;1069} else if (sb == WL_CHANSPEC_CTL_SB_UU) {1070/* Primary 40MHz is on upper side */1071sb = WL_CHANSPEC_CTL_SB_U;1072center_chan += CH_20MHZ_APART;1073} else {1074/* Primary 40MHz is on lower side */1075/* sideband bits are the same for LL/LU and L/U */1076center_chan -= CH_20MHZ_APART;1077}10781079/* Create primary 40MHz chanspec */1080chspec40 = (WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_40 |1081sb | center_chan);1082}1083#endif1084return chspec40;1085}10861087/*1088* Return the channel number for a given frequency and base frequency.1089* The returned channel number is relative to the given base frequency.1090* If the given base frequency is zero, a base frequency of 5 GHz is assumed for1091* frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz.1092*1093* Frequency is specified in MHz.1094* The base frequency is specified as (start_factor * 500 kHz).1095* Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for1096* 2.4 GHz and 5 GHz bands.1097*1098* The returned channel will be in the range [1, 14] in the 2.4 GHz band1099* and [0, 200] otherwise.1100* -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the1101* frequency is not a 2.4 GHz channel, or if the frequency is not and even1102* multiple of 5 MHz from the base frequency to the base plus 1 GHz.1103*1104* Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.21105*/1106int1107wf_mhz2channel(uint freq, uint start_factor)1108{1109int ch = -1;1110uint base;1111int offset;11121113/* take the default channel start frequency */1114if (start_factor == 0) {1115if (freq >= 2400 && freq <= 2500)1116start_factor = WF_CHAN_FACTOR_2_4_G;1117else if (freq >= 5000 && freq <= 6000)1118start_factor = WF_CHAN_FACTOR_5_G;1119}11201121if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G)1122return 14;11231124base = start_factor / 2;11251126/* check that the frequency is in 1GHz range of the base */1127if ((freq < base) || (freq > base + 1000))1128return -1;11291130offset = freq - base;1131ch = offset / 5;11321133/* check that frequency is a 5MHz multiple from the base */1134if (offset != (ch * 5))1135return -1;11361137/* restricted channel range check for 2.4G */1138if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13))1139return -1;11401141return ch;1142}11431144/*1145* Return the center frequency in MHz of the given channel and base frequency.1146* The channel number is interpreted relative to the given base frequency.1147*1148* The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise.1149* The base frequency is specified as (start_factor * 500 kHz).1150* Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_4_G, and WF_CHAN_FACTOR_5_G1151* are defined for 2.4 GHz, 4 GHz, and 5 GHz bands.1152* The channel range of [1, 14] is only checked for a start_factor of1153* WF_CHAN_FACTOR_2_4_G (4814 = 2407 * 2).1154* Odd start_factors produce channels on .5 MHz boundaries, in which case1155* the answer is rounded down to an integral MHz.1156* -1 is returned for an out of range channel.1157*1158* Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.21159*/1160int1161wf_channel2mhz(uint ch, uint start_factor)1162{1163int freq;11641165if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) ||1166(ch > 200))1167freq = -1;1168else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14))1169freq = 2484;1170else1171freq = ch * 5 + start_factor / 2;11721173return freq;1174}117511761177