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) 2010, 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,v 1.31 2009/09/21 16:10:13 Exp $ 20 */ 21 22#include <typedefs.h> 23 24#ifdef BCMDRIVER 25#include <osl.h> 26#include <bcmutils.h> 27#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) 28#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) 29#else 30#include <stdio.h> 31#include <stdlib.h> 32#include <ctype.h> 33#ifndef ASSERT 34#define ASSERT(exp) 35#endif 36#endif /* BCMDRIVER */ 37#include <bcmwifi.h> 38 39#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL)) 40#include <bcmstdlib.h> /* For wl/exe/GNUmakefile.brcm_wlu and GNUmakefile.wlm_dll */ 41#endif 42 43/* Chanspec ASCII representation: 44 * <channel><band><bandwidth><ctl-sideband> 45 * digit [AB] [N] [UL] 46 * 47 * <channel>: channel number of the 10MHz or 20MHz channel, 48 * or control sideband channel of 40MHz channel. 49 * <band>: A for 5GHz, B for 2.4GHz 50 * <bandwidth>: N for 10MHz, nothing for 20MHz or 40MHz 51 * (ctl-sideband spec implies 40MHz) 52 * <ctl-sideband>: U for upper, L for lower 53 * 54 * <band> may be omitted on input, and will be assumed to be 55 * 2.4GHz if channel number <= 14. 56 * 57 * Examples: 58 * 8 -> 2.4GHz channel 8, 20MHz 59 * 8b -> 2.4GHz channel 8, 20MHz 60 * 8l -> 2.4GHz channel 8, 40MHz, lower ctl sideband 61 * 8a -> 5GHz channel 8 (low 5 GHz band), 20MHz 62 * 36 -> 5GHz channel 36, 20MHz 63 * 36l -> 5GHz channel 36, 40MHz, lower ctl sideband 64 * 40u -> 5GHz channel 40, 40MHz, upper ctl sideband 65 * 180n -> channel 180, 10MHz 66 */ 67 68 69/* given a chanspec and a string buffer, format the chanspec as a 70 * string, and return the original pointer a. 71 * Min buffer length must be CHANSPEC_STR_LEN. 72 * On error return NULL 73 */ 74char * 75wf_chspec_ntoa(chanspec_t chspec, char *buf) 76{ 77 const char *band, *bw, *sb; 78 uint channel; 79 80 band = ""; 81 bw = ""; 82 sb = ""; 83 channel = CHSPEC_CHANNEL(chspec); 84 /* check for non-default band spec */ 85 if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) || 86 (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL)) 87 band = (CHSPEC_IS2G(chspec)) ? "b" : "a"; 88 if (CHSPEC_IS40(chspec)) { 89 if (CHSPEC_SB_UPPER(chspec)) { 90 sb = "u"; 91 channel += CH_10MHZ_APART; 92 } else { 93 sb = "l"; 94 channel -= CH_10MHZ_APART; 95 } 96 } else if (CHSPEC_IS10(chspec)) { 97 bw = "n"; 98 } 99 100 /* Outputs a max of 6 chars including '\0' */ 101 snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb); 102 return (buf); 103} 104 105/* given a chanspec string, convert to a chanspec. 106 * On error return 0 107 */ 108chanspec_t 109wf_chspec_aton(char *a) 110{ 111 char *endp = NULL; 112 uint channel, band, bw, ctl_sb; 113 char c; 114 115 channel = strtoul(a, &endp, 10); 116 117 /* check for no digits parsed */ 118 if (endp == a) 119 return 0; 120 121 if (channel > MAXCHANNEL) 122 return 0; 123 124 band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); 125 bw = WL_CHANSPEC_BW_20; 126 ctl_sb = WL_CHANSPEC_CTL_SB_NONE; 127 128 a = endp; 129 130 c = tolower(a[0]); 131 if (c == '\0') 132 goto done; 133 134 /* parse the optional ['A' | 'B'] band spec */ 135 if (c == 'a' || c == 'b') { 136 band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G; 137 a++; 138 c = tolower(a[0]); 139 if (c == '\0') 140 goto done; 141 } 142 143 /* parse bandwidth 'N' (10MHz) or 40MHz ctl sideband ['L' | 'U'] */ 144 if (c == 'n') { 145 bw = WL_CHANSPEC_BW_10; 146 } else if (c == 'l') { 147 bw = WL_CHANSPEC_BW_40; 148 ctl_sb = WL_CHANSPEC_CTL_SB_LOWER; 149 /* adjust channel to center of 40MHz band */ 150 if (channel <= (MAXCHANNEL - CH_20MHZ_APART)) 151 channel += CH_10MHZ_APART; 152 else 153 return 0; 154 } else if (c == 'u') { 155 bw = WL_CHANSPEC_BW_40; 156 ctl_sb = WL_CHANSPEC_CTL_SB_UPPER; 157 /* adjust channel to center of 40MHz band */ 158 if (channel > CH_20MHZ_APART) 159 channel -= CH_10MHZ_APART; 160 else 161 return 0; 162 } else { 163 return 0; 164 } 165 166done: 167 return (channel | band | bw | ctl_sb); 168} 169 170/* 171 * Verify the chanspec is using a legal set of parameters, i.e. that the 172 * chanspec specified a band, bw, ctl_sb and channel and that the 173 * combination could be legal given any set of circumstances. 174 * RETURNS: TRUE is the chanspec is malformed, false if it looks good. 175 */ 176bool 177wf_chspec_malformed(chanspec_t chanspec) 178{ 179 /* must be 2G or 5G band */ 180 if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) 181 return TRUE; 182 /* must be 20 or 40 bandwidth */ 183 if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) 184 return TRUE; 185 186 /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ 187 if (CHSPEC_IS20(chanspec)) { 188 if (!CHSPEC_SB_NONE(chanspec)) 189 return TRUE; 190 } else { 191 if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) 192 return TRUE; 193 } 194 195 return FALSE; 196} 197 198/* 199 * This function returns the channel number that control traffic is being sent on, for legacy 200 * channels this is just the channel number, for 40MHZ channels it is the upper or lowre 20MHZ 201 * sideband depending on the chanspec selected 202 */ 203uint8 204wf_chspec_ctlchan(chanspec_t chspec) 205{ 206 uint8 ctl_chan; 207 208 /* Is there a sideband ? */ 209 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { 210 return CHSPEC_CHANNEL(chspec); 211 } else { 212 /* we only support 40MHZ with sidebands */ 213 ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40); 214 /* chanspec channel holds the centre frequency, use that and the 215 * side band information to reconstruct the control channel number 216 */ 217 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { 218 /* control chan is the upper 20 MHZ SB of the 40MHZ channel */ 219 ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); 220 } else { 221 ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER); 222 /* control chan is the lower 20 MHZ SB of the 40MHZ channel */ 223 ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); 224 } 225 } 226 227 return ctl_chan; 228} 229 230chanspec_t 231wf_chspec_ctlchspec(chanspec_t chspec) 232{ 233 chanspec_t ctl_chspec = 0; 234 uint8 channel; 235 236 ASSERT(!wf_chspec_malformed(chspec)); 237 238 /* Is there a sideband ? */ 239 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { 240 return chspec; 241 } else { 242 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { 243 channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); 244 } else { 245 channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); 246 } 247 ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE; 248 ctl_chspec |= CHSPEC_BAND(chspec); 249 } 250 return ctl_chspec; 251} 252 253/* 254 * Return the channel number for a given frequency and base frequency. 255 * The returned channel number is relative to the given base frequency. 256 * If the given base frequency is zero, a base frequency of 5 GHz is assumed for 257 * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz. 258 * 259 * Frequency is specified in MHz. 260 * The base frequency is specified as (start_factor * 500 kHz). 261 * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for 262 * 2.4 GHz and 5 GHz bands. 263 * 264 * The returned channel will be in the range [1, 14] in the 2.4 GHz band 265 * and [0, 200] otherwise. 266 * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the 267 * frequency is not a 2.4 GHz channel, or if the frequency is not and even 268 * multiple of 5 MHz from the base frequency to the base plus 1 GHz. 269 * 270 * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 271 */ 272int 273wf_mhz2channel(uint freq, uint start_factor) 274{ 275 int ch = -1; 276 uint base; 277 int offset; 278 279 /* take the default channel start frequency */ 280 if (start_factor == 0) { 281 if (freq >= 2400 && freq <= 2500) 282 start_factor = WF_CHAN_FACTOR_2_4_G; 283 else if (freq >= 5000 && freq <= 6000) 284 start_factor = WF_CHAN_FACTOR_5_G; 285 } 286 287 if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G) 288 return 14; 289 290 base = start_factor / 2; 291 292 /* check that the frequency is in 1GHz range of the base */ 293 if ((freq < base) || (freq > base + 1000)) 294 return -1; 295 296 offset = freq - base; 297 ch = offset / 5; 298 299 /* check that frequency is a 5MHz multiple from the base */ 300 if (offset != (ch * 5)) 301 return -1; 302 303 /* restricted channel range check for 2.4G */ 304 if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13)) 305 return -1; 306 307 return ch; 308} 309 310/* 311 * Return the center frequency in MHz of the given channel and base frequency. 312 * The channel number is interpreted relative to the given base frequency. 313 * 314 * The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise. 315 * The base frequency is specified as (start_factor * 500 kHz). 316 * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_4_G, and WF_CHAN_FACTOR_5_G 317 * are defined for 2.4 GHz, 4 GHz, and 5 GHz bands. 318 * The channel range of [1, 14] is only checked for a start_factor of 319 * WF_CHAN_FACTOR_2_4_G (4814 = 2407 * 2). 320 * Odd start_factors produce channels on .5 MHz boundaries, in which case 321 * the answer is rounded down to an integral MHz. 322 * -1 is returned for an out of range channel. 323 * 324 * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 325 */ 326int 327wf_channel2mhz(uint ch, uint start_factor) 328{ 329 int freq; 330 331 if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) || 332 (ch > 200)) 333 freq = -1; 334 else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14)) 335 freq = 2484; 336 else 337 freq = ch * 5 + start_factor / 2; 338 339 return freq; 340} 341