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