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