ieee80211.c revision 148290
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3139530Ssam * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting 4116742Ssam * All rights reserved. 5116742Ssam * 6116742Ssam * Redistribution and use in source and binary forms, with or without 7116742Ssam * modification, are permitted provided that the following conditions 8116742Ssam * are met: 9116742Ssam * 1. Redistributions of source code must retain the above copyright 10116904Ssam * notice, this list of conditions and the following disclaimer. 11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 12116904Ssam * notice, this list of conditions and the following disclaimer in the 13116904Ssam * documentation and/or other materials provided with the distribution. 14116904Ssam * 3. The name of the author may not be used to endorse or promote products 15116904Ssam * derived from this software without specific prior written permission. 16116742Ssam * 17116742Ssam * Alternatively, this software may be distributed under the terms of the 18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free 19116742Ssam * Software Foundation. 20116742Ssam * 21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31116742Ssam */ 32116742Ssam 33116742Ssam#include <sys/cdefs.h> 34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211.c 148290 2005-07-22 16:50:18Z sam $"); 35116742Ssam 36116742Ssam/* 37116742Ssam * IEEE 802.11 generic handler 38116742Ssam */ 39116742Ssam 40116742Ssam#include <sys/param.h> 41116742Ssam#include <sys/systm.h> 42116742Ssam#include <sys/kernel.h> 43138568Ssam 44116742Ssam#include <sys/socket.h> 45116742Ssam 46116742Ssam#include <net/if.h> 47116742Ssam#include <net/if_media.h> 48116742Ssam#include <net/ethernet.h> 49116742Ssam 50116742Ssam#include <net80211/ieee80211_var.h> 51116742Ssam 52116742Ssam#include <net/bpf.h> 53116742Ssam 54139502Ssamconst char *ieee80211_phymode_name[] = { 55116742Ssam "auto", /* IEEE80211_MODE_AUTO */ 56116742Ssam "11a", /* IEEE80211_MODE_11A */ 57116742Ssam "11b", /* IEEE80211_MODE_11B */ 58116742Ssam "11g", /* IEEE80211_MODE_11G */ 59124543Sonoe "FH", /* IEEE80211_MODE_FH */ 60138568Ssam "turboA", /* IEEE80211_MODE_TURBO_A */ 61138568Ssam "turboG", /* IEEE80211_MODE_TURBO_G */ 62116742Ssam}; 63116742Ssam 64138568Ssam/* list of all instances */ 65138568SsamSLIST_HEAD(ieee80211_list, ieee80211com); 66138568Ssamstatic struct ieee80211_list ieee80211_list = 67138568Ssam SLIST_HEAD_INITIALIZER(ieee80211_list); 68138568Ssamstatic u_int8_t ieee80211_vapmap[32]; /* enough for 256 */ 69138568Ssamstatic struct mtx ieee80211_vap_mtx; 70138568SsamMTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF); 71138568Ssam 72138568Ssamstatic void 73138568Ssamieee80211_add_vap(struct ieee80211com *ic) 74138568Ssam{ 75138568Ssam#define N(a) (sizeof(a)/sizeof(a[0])) 76138568Ssam int i; 77138568Ssam u_int8_t b; 78138568Ssam 79138568Ssam mtx_lock(&ieee80211_vap_mtx); 80138568Ssam ic->ic_vap = 0; 81138568Ssam for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++) 82138568Ssam ic->ic_vap += NBBY; 83138568Ssam if (i == N(ieee80211_vapmap)) 84138568Ssam panic("vap table full"); 85138568Ssam for (b = ieee80211_vapmap[i]; b & 1; b >>= 1) 86138568Ssam ic->ic_vap++; 87138568Ssam setbit(ieee80211_vapmap, ic->ic_vap); 88138568Ssam SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); 89138568Ssam mtx_unlock(&ieee80211_vap_mtx); 90138568Ssam#undef N 91138568Ssam} 92138568Ssam 93138568Ssamstatic void 94138568Ssamieee80211_remove_vap(struct ieee80211com *ic) 95138568Ssam{ 96138568Ssam mtx_lock(&ieee80211_vap_mtx); 97138568Ssam SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); 98138568Ssam KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY, 99138568Ssam ("invalid vap id %d", ic->ic_vap)); 100138568Ssam KASSERT(isset(ieee80211_vapmap, ic->ic_vap), 101138568Ssam ("vap id %d not allocated", ic->ic_vap)); 102138568Ssam clrbit(ieee80211_vapmap, ic->ic_vap); 103138568Ssam mtx_unlock(&ieee80211_vap_mtx); 104138568Ssam} 105138568Ssam 106140915Ssam/* 107140915Ssam * Default reset method for use with the ioctl support. This 108140915Ssam * method is invoked after any state change in the 802.11 109140915Ssam * layer that should be propagated to the hardware but not 110140915Ssam * require re-initialization of the 802.11 state machine (e.g 111140915Ssam * rescanning for an ap). We always return ENETRESET which 112140915Ssam * should cause the driver to re-initialize the device. Drivers 113140915Ssam * can override this method to implement more optimized support. 114140915Ssam */ 115140915Ssamstatic int 116140915Ssamieee80211_default_reset(struct ifnet *ifp) 117140915Ssam{ 118140915Ssam return ENETRESET; 119140915Ssam} 120140915Ssam 121116742Ssamvoid 122138568Ssamieee80211_ifattach(struct ieee80211com *ic) 123116742Ssam{ 124138568Ssam struct ifnet *ifp = ic->ic_ifp; 125116742Ssam struct ieee80211_channel *c; 126116742Ssam int i; 127116742Ssam 128116742Ssam ether_ifattach(ifp, ic->ic_myaddr); 129116742Ssam bpfattach2(ifp, DLT_IEEE802_11, 130116742Ssam sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); 131116742Ssam 132138568Ssam ieee80211_crypto_attach(ic); 133138568Ssam 134116742Ssam /* 135116742Ssam * Fill in 802.11 available channel set, mark 136116742Ssam * all available channels as active, and pick 137116742Ssam * a default channel if not already specified. 138116742Ssam */ 139116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 140116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO; 141116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 142116742Ssam c = &ic->ic_channels[i]; 143116742Ssam if (c->ic_flags) { 144116742Ssam /* 145116742Ssam * Verify driver passed us valid data. 146116742Ssam */ 147116742Ssam if (i != ieee80211_chan2ieee(ic, c)) { 148116742Ssam if_printf(ifp, "bad channel ignored; " 149116742Ssam "freq %u flags %x number %u\n", 150116742Ssam c->ic_freq, c->ic_flags, i); 151116742Ssam c->ic_flags = 0; /* NB: remove */ 152116742Ssam continue; 153116742Ssam } 154116742Ssam setbit(ic->ic_chan_avail, i); 155116742Ssam /* 156116742Ssam * Identify mode capabilities. 157116742Ssam */ 158116742Ssam if (IEEE80211_IS_CHAN_A(c)) 159116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11A; 160116742Ssam if (IEEE80211_IS_CHAN_B(c)) 161116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11B; 162116742Ssam if (IEEE80211_IS_CHAN_PUREG(c)) 163116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; 164124543Sonoe if (IEEE80211_IS_CHAN_FHSS(c)) 165124543Sonoe ic->ic_modecaps |= 1<<IEEE80211_MODE_FH; 166116742Ssam if (IEEE80211_IS_CHAN_T(c)) 167138568Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A; 168138568Ssam if (IEEE80211_IS_CHAN_108G(c)) 169138568Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G; 170116742Ssam } 171116742Ssam } 172116742Ssam /* validate ic->ic_curmode */ 173116742Ssam if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0) 174116742Ssam ic->ic_curmode = IEEE80211_MODE_AUTO; 175127760Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 176139526Ssam#if 0 177138568Ssam /* 178138568Ssam * Enable WME by default if we're capable. 179138568Ssam */ 180138568Ssam if (ic->ic_caps & IEEE80211_C_WME) 181138568Ssam ic->ic_flags |= IEEE80211_F_WME; 182139526Ssam#endif 183116742Ssam (void) ieee80211_setmode(ic, ic->ic_curmode); 184116742Ssam 185116742Ssam if (ic->ic_lintval == 0) 186138568Ssam ic->ic_lintval = IEEE80211_BINTVAL_DEFAULT; 187116742Ssam ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ 188138568Ssam ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; 189138568Ssam IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); 190116742Ssam 191138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 192138568Ssam 193138568Ssam ieee80211_node_attach(ic); 194138568Ssam ieee80211_proto_attach(ic); 195138568Ssam 196138568Ssam ieee80211_add_vap(ic); 197138568Ssam 198138568Ssam ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ 199140915Ssam 200140915Ssam /* 201140915Ssam * Install a default reset method for the ioctl support. 202140915Ssam * The driver is expected to fill this in before calling us. 203140915Ssam */ 204140915Ssam if (ic->ic_reset == NULL) 205140915Ssam ic->ic_reset = ieee80211_default_reset; 206116742Ssam} 207116742Ssam 208116742Ssamvoid 209138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 210116742Ssam{ 211138568Ssam struct ifnet *ifp = ic->ic_ifp; 212116742Ssam 213138568Ssam ieee80211_remove_vap(ic); 214138568Ssam 215138568Ssam ieee80211_sysctl_detach(ic); 216138568Ssam ieee80211_proto_detach(ic); 217138568Ssam ieee80211_crypto_detach(ic); 218138568Ssam ieee80211_node_detach(ic); 219116742Ssam ifmedia_removeall(&ic->ic_media); 220138568Ssam 221138568Ssam IEEE80211_BEACON_LOCK_DESTROY(ic); 222138568Ssam 223116742Ssam bpfdetach(ifp); 224116742Ssam ether_ifdetach(ifp); 225116742Ssam} 226116742Ssam 227116742Ssam/* 228116742Ssam * Convert MHz frequency to IEEE channel number. 229116742Ssam */ 230116742Ssamu_int 231116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 232116742Ssam{ 233116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 234116742Ssam if (freq == 2484) 235116742Ssam return 14; 236116742Ssam if (freq < 2484) 237116742Ssam return (freq - 2407) / 5; 238116742Ssam else 239116742Ssam return 15 + ((freq - 2512) / 20); 240116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 241116742Ssam return (freq - 5000) / 5; 242116742Ssam } else { /* either, guess */ 243116742Ssam if (freq == 2484) 244116742Ssam return 14; 245116742Ssam if (freq < 2484) 246116742Ssam return (freq - 2407) / 5; 247116742Ssam if (freq < 5000) 248116742Ssam return 15 + ((freq - 2512) / 20); 249116742Ssam return (freq - 5000) / 5; 250116742Ssam } 251116742Ssam} 252116742Ssam 253116742Ssam/* 254116742Ssam * Convert channel to IEEE channel number. 255116742Ssam */ 256116742Ssamu_int 257116742Ssamieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) 258116742Ssam{ 259116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 260116742Ssam return c - ic->ic_channels; 261116742Ssam else if (c == IEEE80211_CHAN_ANYC) 262116742Ssam return IEEE80211_CHAN_ANY; 263117039Ssam else if (c != NULL) { 264138568Ssam if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", 265116742Ssam c->ic_freq, c->ic_flags); 266116742Ssam return 0; /* XXX */ 267117039Ssam } else { 268138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 269117039Ssam return 0; /* XXX */ 270116742Ssam } 271116742Ssam} 272116742Ssam 273116742Ssam/* 274116742Ssam * Convert IEEE channel number to MHz frequency. 275116742Ssam */ 276116742Ssamu_int 277116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 278116742Ssam{ 279116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 280116742Ssam if (chan == 14) 281116742Ssam return 2484; 282116742Ssam if (chan < 14) 283116742Ssam return 2407 + chan*5; 284116742Ssam else 285116742Ssam return 2512 + ((chan-15)*20); 286116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 287116742Ssam return 5000 + (chan*5); 288116742Ssam } else { /* either, guess */ 289116742Ssam if (chan == 14) 290116742Ssam return 2484; 291116742Ssam if (chan < 14) /* 0-13 */ 292116742Ssam return 2407 + chan*5; 293116742Ssam if (chan < 27) /* 15-26 */ 294116742Ssam return 2512 + ((chan-15)*20); 295116742Ssam return 5000 + (chan*5); 296116742Ssam } 297116742Ssam} 298116742Ssam 299116742Ssam/* 300116742Ssam * Setup the media data structures according to the channel and 301116742Ssam * rate tables. This must be called by the driver after 302116742Ssam * ieee80211_attach and before most anything else. 303116742Ssam */ 304116742Ssamvoid 305138568Ssamieee80211_media_init(struct ieee80211com *ic, 306116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 307116742Ssam{ 308116742Ssam#define ADD(_ic, _s, _o) \ 309116742Ssam ifmedia_add(&(_ic)->ic_media, \ 310116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 311138568Ssam struct ifnet *ifp = ic->ic_ifp; 312116742Ssam struct ifmediareq imr; 313116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 314116742Ssam struct ieee80211_rateset *rs; 315116742Ssam struct ieee80211_rateset allrates; 316116742Ssam 317116742Ssam /* 318118887Ssam * Do late attach work that must wait for any subclass 319118887Ssam * (i.e. driver) work such as overriding methods. 320118887Ssam */ 321138568Ssam ieee80211_node_lateattach(ic); 322118887Ssam 323118887Ssam /* 324116742Ssam * Fill in media characteristics. 325116742Ssam */ 326116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 327116742Ssam maxrate = 0; 328116742Ssam memset(&allrates, 0, sizeof(allrates)); 329116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 330116742Ssam static const u_int mopts[] = { 331116742Ssam IFM_AUTO, 332124543Sonoe IFM_IEEE80211_11A, 333124543Sonoe IFM_IEEE80211_11B, 334124543Sonoe IFM_IEEE80211_11G, 335124543Sonoe IFM_IEEE80211_FH, 336124543Sonoe IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 337138568Ssam IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, 338116742Ssam }; 339116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 340116742Ssam continue; 341116742Ssam mopt = mopts[mode]; 342116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 343116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 344116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 345116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 346116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 347116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 348116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 349117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 350117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 351116742Ssam if (mode == IEEE80211_MODE_AUTO) 352116742Ssam continue; 353116742Ssam rs = &ic->ic_sup_rates[mode]; 354116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 355116742Ssam rate = rs->rs_rates[i]; 356116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 357116742Ssam if (mword == 0) 358116742Ssam continue; 359116742Ssam ADD(ic, mword, mopt); 360116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 361116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 362116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 363116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 364116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 365116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 366117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 367117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 368116742Ssam /* 369116742Ssam * Add rate to the collection of all rates. 370116742Ssam */ 371116742Ssam r = rate & IEEE80211_RATE_VAL; 372116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 373116742Ssam if (allrates.rs_rates[j] == r) 374116742Ssam break; 375116742Ssam if (j == allrates.rs_nrates) { 376116742Ssam /* unique, add to the set */ 377116742Ssam allrates.rs_rates[j] = r; 378116742Ssam allrates.rs_nrates++; 379116742Ssam } 380116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 381116742Ssam if (rate > maxrate) 382116742Ssam maxrate = rate; 383116742Ssam } 384116742Ssam } 385116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 386116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 387116742Ssam IEEE80211_MODE_AUTO); 388116742Ssam if (mword == 0) 389116742Ssam continue; 390116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 391116742Ssam ADD(ic, mword, 0); 392116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 393116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 394116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 395116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 396116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 397116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 398117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 399117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 400116742Ssam } 401116742Ssam ieee80211_media_status(ifp, &imr); 402116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 403116742Ssam 404116742Ssam if (maxrate) 405116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 406116742Ssam#undef ADD 407116742Ssam} 408116742Ssam 409138568Ssamvoid 410138568Ssamieee80211_announce(struct ieee80211com *ic) 411138568Ssam{ 412138568Ssam struct ifnet *ifp = ic->ic_ifp; 413138568Ssam int i, mode, rate, mword; 414138568Ssam struct ieee80211_rateset *rs; 415138568Ssam 416138568Ssam for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { 417138568Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 418138568Ssam continue; 419138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 420138568Ssam rs = &ic->ic_sup_rates[mode]; 421138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 422138568Ssam rate = rs->rs_rates[i]; 423138568Ssam mword = ieee80211_rate2media(ic, rate, mode); 424138568Ssam if (mword == 0) 425138568Ssam continue; 426138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 427138568Ssam (rate & IEEE80211_RATE_VAL) / 2, 428138568Ssam ((rate & 0x1) != 0 ? ".5" : "")); 429138568Ssam } 430138568Ssam printf("\n"); 431138568Ssam } 432138568Ssam} 433138568Ssam 434116742Ssamstatic int 435116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 436116742Ssam{ 437116742Ssam#define IEEERATE(_ic,_m,_i) \ 438116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 439116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 440116742Ssam for (i = 0; i < nrates; i++) 441116742Ssam if (IEEERATE(ic, mode, i) == rate) 442116742Ssam return i; 443116742Ssam return -1; 444116742Ssam#undef IEEERATE 445116742Ssam} 446116742Ssam 447116742Ssam/* 448138568Ssam * Find an instance by it's mac address. 449138568Ssam */ 450138568Ssamstruct ieee80211com * 451138568Ssamieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) 452138568Ssam{ 453138568Ssam struct ieee80211com *ic; 454138568Ssam 455138568Ssam /* XXX lock */ 456138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 457138568Ssam if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) 458138568Ssam return ic; 459138568Ssam return NULL; 460138568Ssam} 461138568Ssam 462138568Ssamstatic struct ieee80211com * 463138568Ssamieee80211_find_instance(struct ifnet *ifp) 464138568Ssam{ 465138568Ssam struct ieee80211com *ic; 466138568Ssam 467138568Ssam /* XXX lock */ 468138568Ssam /* XXX not right for multiple instances but works for now */ 469138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 470138568Ssam if (ic->ic_ifp == ifp) 471138568Ssam return ic; 472138568Ssam return NULL; 473138568Ssam} 474138568Ssam 475138568Ssam/* 476116742Ssam * Handle a media change request. 477116742Ssam */ 478116742Ssamint 479116742Ssamieee80211_media_change(struct ifnet *ifp) 480116742Ssam{ 481138568Ssam struct ieee80211com *ic; 482116742Ssam struct ifmedia_entry *ime; 483116742Ssam enum ieee80211_opmode newopmode; 484116742Ssam enum ieee80211_phymode newphymode; 485116742Ssam int i, j, newrate, error = 0; 486116742Ssam 487138568Ssam ic = ieee80211_find_instance(ifp); 488138568Ssam if (!ic) { 489138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 490138568Ssam return EINVAL; 491138568Ssam } 492116742Ssam ime = ic->ic_media.ifm_cur; 493116742Ssam /* 494116742Ssam * First, identify the phy mode. 495116742Ssam */ 496116742Ssam switch (IFM_MODE(ime->ifm_media)) { 497116742Ssam case IFM_IEEE80211_11A: 498116742Ssam newphymode = IEEE80211_MODE_11A; 499116742Ssam break; 500116742Ssam case IFM_IEEE80211_11B: 501116742Ssam newphymode = IEEE80211_MODE_11B; 502116742Ssam break; 503116742Ssam case IFM_IEEE80211_11G: 504116742Ssam newphymode = IEEE80211_MODE_11G; 505116742Ssam break; 506124543Sonoe case IFM_IEEE80211_FH: 507124543Sonoe newphymode = IEEE80211_MODE_FH; 508124543Sonoe break; 509116742Ssam case IFM_AUTO: 510116742Ssam newphymode = IEEE80211_MODE_AUTO; 511116742Ssam break; 512116742Ssam default: 513116742Ssam return EINVAL; 514116742Ssam } 515116742Ssam /* 516138568Ssam * Turbo mode is an ``option''. 517138568Ssam * XXX does not apply to AUTO 518116742Ssam */ 519116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 520138568Ssam if (newphymode == IEEE80211_MODE_11A) 521138568Ssam newphymode = IEEE80211_MODE_TURBO_A; 522138568Ssam else if (newphymode == IEEE80211_MODE_11G) 523138568Ssam newphymode = IEEE80211_MODE_TURBO_G; 524138568Ssam else 525116742Ssam return EINVAL; 526116742Ssam } 527116742Ssam /* 528116742Ssam * Validate requested mode is available. 529116742Ssam */ 530116742Ssam if ((ic->ic_modecaps & (1<<newphymode)) == 0) 531116742Ssam return EINVAL; 532116742Ssam 533116742Ssam /* 534116742Ssam * Next, the fixed/variable rate. 535116742Ssam */ 536116742Ssam i = -1; 537116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 538116742Ssam /* 539116742Ssam * Convert media subtype to rate. 540116742Ssam */ 541116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 542116742Ssam if (newrate == 0) 543116742Ssam return EINVAL; 544116742Ssam /* 545116742Ssam * Check the rate table for the specified/current phy. 546116742Ssam */ 547116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 548116742Ssam /* 549116742Ssam * In autoselect mode search for the rate. 550116742Ssam */ 551116742Ssam for (j = IEEE80211_MODE_11A; 552116742Ssam j < IEEE80211_MODE_MAX; j++) { 553116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 554116742Ssam continue; 555116742Ssam i = findrate(ic, j, newrate); 556116742Ssam if (i != -1) { 557116742Ssam /* lock mode too */ 558116742Ssam newphymode = j; 559116742Ssam break; 560116742Ssam } 561116742Ssam } 562116742Ssam } else { 563116742Ssam i = findrate(ic, newphymode, newrate); 564116742Ssam } 565116742Ssam if (i == -1) /* mode/rate mismatch */ 566116742Ssam return EINVAL; 567116742Ssam } 568116742Ssam /* NB: defer rate setting to later */ 569116742Ssam 570116742Ssam /* 571116742Ssam * Deduce new operating mode but don't install it just yet. 572116742Ssam */ 573116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 574116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 575116742Ssam newopmode = IEEE80211_M_AHDEMO; 576116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 577116742Ssam newopmode = IEEE80211_M_HOSTAP; 578116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 579116742Ssam newopmode = IEEE80211_M_IBSS; 580117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 581117817Ssam newopmode = IEEE80211_M_MONITOR; 582116742Ssam else 583116742Ssam newopmode = IEEE80211_M_STA; 584116742Ssam 585116742Ssam /* 586116742Ssam * Autoselect doesn't make sense when operating as an AP. 587116742Ssam * If no phy mode has been selected, pick one and lock it 588116742Ssam * down so rate tables can be used in forming beacon frames 589116742Ssam * and the like. 590116742Ssam */ 591116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 592116742Ssam newphymode == IEEE80211_MODE_AUTO) { 593116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 594116742Ssam if (ic->ic_modecaps & (1<<j)) { 595116742Ssam newphymode = j; 596116742Ssam break; 597116742Ssam } 598116742Ssam } 599116742Ssam 600116742Ssam /* 601116742Ssam * Handle phy mode change. 602116742Ssam */ 603116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 604116742Ssam error = ieee80211_setmode(ic, newphymode); 605116742Ssam if (error != 0) 606116742Ssam return error; 607116742Ssam error = ENETRESET; 608116742Ssam } 609116742Ssam 610116742Ssam /* 611116742Ssam * Committed to changes, install the rate setting. 612116742Ssam */ 613116742Ssam if (ic->ic_fixed_rate != i) { 614116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 615116742Ssam error = ENETRESET; 616116742Ssam } 617116742Ssam 618116742Ssam /* 619116742Ssam * Handle operating mode change. 620116742Ssam */ 621116742Ssam if (ic->ic_opmode != newopmode) { 622116742Ssam ic->ic_opmode = newopmode; 623116742Ssam switch (newopmode) { 624116742Ssam case IEEE80211_M_AHDEMO: 625116742Ssam case IEEE80211_M_HOSTAP: 626116742Ssam case IEEE80211_M_STA: 627117817Ssam case IEEE80211_M_MONITOR: 628116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 629116742Ssam break; 630116742Ssam case IEEE80211_M_IBSS: 631116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 632116742Ssam break; 633116742Ssam } 634138568Ssam /* 635138568Ssam * Yech, slot time may change depending on the 636138568Ssam * operating mode so reset it to be sure everything 637138568Ssam * is setup appropriately. 638138568Ssam */ 639138568Ssam ieee80211_reset_erp(ic); 640138568Ssam ieee80211_wme_initparams(ic); /* after opmode change */ 641116742Ssam error = ENETRESET; 642116742Ssam } 643116742Ssam#ifdef notdef 644116742Ssam if (error == 0) 645116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 646116742Ssam#endif 647116742Ssam return error; 648116742Ssam} 649116742Ssam 650116742Ssamvoid 651116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 652116742Ssam{ 653138568Ssam struct ieee80211com *ic; 654138568Ssam struct ieee80211_rateset *rs; 655116742Ssam 656138568Ssam ic = ieee80211_find_instance(ifp); 657138568Ssam if (!ic) { 658138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 659138568Ssam return; 660138568Ssam } 661116742Ssam imr->ifm_status = IFM_AVALID; 662116742Ssam imr->ifm_active = IFM_IEEE80211; 663138568Ssam if (ic->ic_state == IEEE80211_S_RUN) 664116742Ssam imr->ifm_status |= IFM_ACTIVE; 665138568Ssam /* 666138568Ssam * Calculate a current rate if possible. 667138568Ssam */ 668148290Ssam if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 669138568Ssam /* 670138568Ssam * A fixed rate is set, report that. 671138568Ssam */ 672138568Ssam rs = &ic->ic_sup_rates[ic->ic_curmode]; 673138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 674138568Ssam rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); 675138568Ssam } else if (ic->ic_opmode == IEEE80211_M_STA) { 676138568Ssam /* 677138568Ssam * In station mode report the current transmit rate. 678138568Ssam */ 679138568Ssam rs = &ic->ic_bss->ni_rates; 680138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 681138568Ssam rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); 682128966Sandre } else 683138568Ssam imr->ifm_active |= IFM_AUTO; 684116742Ssam switch (ic->ic_opmode) { 685116742Ssam case IEEE80211_M_STA: 686116742Ssam break; 687116742Ssam case IEEE80211_M_IBSS: 688116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 689116742Ssam break; 690116742Ssam case IEEE80211_M_AHDEMO: 691116742Ssam /* should not come here */ 692116742Ssam break; 693116742Ssam case IEEE80211_M_HOSTAP: 694116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 695116742Ssam break; 696117817Ssam case IEEE80211_M_MONITOR: 697117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 698117817Ssam break; 699116742Ssam } 700116742Ssam switch (ic->ic_curmode) { 701116742Ssam case IEEE80211_MODE_11A: 702124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A; 703116742Ssam break; 704116742Ssam case IEEE80211_MODE_11B: 705124543Sonoe imr->ifm_active |= IFM_IEEE80211_11B; 706116742Ssam break; 707116742Ssam case IEEE80211_MODE_11G: 708124543Sonoe imr->ifm_active |= IFM_IEEE80211_11G; 709116742Ssam break; 710124543Sonoe case IEEE80211_MODE_FH: 711124543Sonoe imr->ifm_active |= IFM_IEEE80211_FH; 712124543Sonoe break; 713138568Ssam case IEEE80211_MODE_TURBO_A: 714124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A 715116742Ssam | IFM_IEEE80211_TURBO; 716116742Ssam break; 717138568Ssam case IEEE80211_MODE_TURBO_G: 718138568Ssam imr->ifm_active |= IFM_IEEE80211_11G 719138568Ssam | IFM_IEEE80211_TURBO; 720138568Ssam break; 721116742Ssam } 722116742Ssam} 723116742Ssam 724116742Ssamvoid 725138568Ssamieee80211_watchdog(struct ieee80211com *ic) 726116742Ssam{ 727138568Ssam struct ieee80211_node_table *nt; 728138568Ssam int need_inact_timer = 0; 729116742Ssam 730138568Ssam if (ic->ic_state != IEEE80211_S_INIT) { 731138568Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 732138568Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 733138568Ssam nt = &ic->ic_scan; 734138568Ssam if (nt->nt_inact_timer) { 735138568Ssam if (--nt->nt_inact_timer == 0) 736138568Ssam nt->nt_timeout(nt); 737138568Ssam need_inact_timer += nt->nt_inact_timer; 738138568Ssam } 739140753Ssam nt = &ic->ic_sta; 740140753Ssam if (nt->nt_inact_timer) { 741138568Ssam if (--nt->nt_inact_timer == 0) 742138568Ssam nt->nt_timeout(nt); 743138568Ssam need_inact_timer += nt->nt_inact_timer; 744138568Ssam } 745116742Ssam } 746138568Ssam if (ic->ic_mgt_timer != 0 || need_inact_timer) 747138568Ssam ic->ic_ifp->if_timer = 1; 748116742Ssam} 749116742Ssam 750116742Ssam/* 751116742Ssam * Set the current phy mode and recalculate the active channel 752116742Ssam * set based on the available channels for this mode. Also 753116742Ssam * select a new default/current channel if the current one is 754116742Ssam * inappropriate for this mode. 755116742Ssam */ 756116742Ssamint 757116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 758116742Ssam{ 759116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 760116742Ssam static const u_int chanflags[] = { 761116742Ssam 0, /* IEEE80211_MODE_AUTO */ 762116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 763116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 764116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 765124543Sonoe IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 766138568Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ 767138568Ssam IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 768116742Ssam }; 769116742Ssam struct ieee80211_channel *c; 770116742Ssam u_int modeflags; 771116742Ssam int i; 772116742Ssam 773116742Ssam /* validate new mode */ 774116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) { 775138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 776138568Ssam "%s: mode %u not supported (caps 0x%x)\n", 777138568Ssam __func__, mode, ic->ic_modecaps); 778116742Ssam return EINVAL; 779116742Ssam } 780116742Ssam 781116742Ssam /* 782116742Ssam * Verify at least one channel is present in the available 783116742Ssam * channel list before committing to the new mode. 784116742Ssam */ 785127760Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); 786116742Ssam modeflags = chanflags[mode]; 787116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 788116742Ssam c = &ic->ic_channels[i]; 789116742Ssam if (mode == IEEE80211_MODE_AUTO) { 790116742Ssam /* ignore turbo channels for autoselect */ 791116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 792116742Ssam break; 793116742Ssam } else { 794116742Ssam if ((c->ic_flags & modeflags) == modeflags) 795116742Ssam break; 796116742Ssam } 797116742Ssam } 798116742Ssam if (i > IEEE80211_CHAN_MAX) { 799138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 800138568Ssam "%s: no channels found for mode %u\n", __func__, mode); 801116742Ssam return EINVAL; 802116742Ssam } 803116742Ssam 804116742Ssam /* 805116742Ssam * Calculate the active channel set. 806116742Ssam */ 807116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 808116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 809116742Ssam c = &ic->ic_channels[i]; 810116742Ssam if (mode == IEEE80211_MODE_AUTO) { 811116742Ssam /* take anything but pure turbo channels */ 812116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 813116742Ssam setbit(ic->ic_chan_active, i); 814116742Ssam } else { 815116742Ssam if ((c->ic_flags & modeflags) == modeflags) 816116742Ssam setbit(ic->ic_chan_active, i); 817116742Ssam } 818116742Ssam } 819116742Ssam /* 820116742Ssam * If no current/default channel is setup or the current 821116742Ssam * channel is wrong for the mode then pick the first 822116742Ssam * available channel from the active list. This is likely 823116742Ssam * not the right one. 824116742Ssam */ 825116742Ssam if (ic->ic_ibss_chan == NULL || 826116742Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 827116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 828116742Ssam if (isset(ic->ic_chan_active, i)) { 829116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 830116742Ssam break; 831116742Ssam } 832138568Ssam KASSERT(ic->ic_ibss_chan != NULL && 833138568Ssam isset(ic->ic_chan_active, 834138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), 835138568Ssam ("Bad IBSS channel %u", 836138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); 837116742Ssam } 838138568Ssam /* 839138568Ssam * If the desired channel is set but no longer valid then reset it. 840138568Ssam */ 841138568Ssam if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 842138568Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) 843138568Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 844116742Ssam 845116742Ssam /* 846138568Ssam * Do mode-specific rate setup. 847116742Ssam */ 848116742Ssam if (mode == IEEE80211_MODE_11G) { 849138568Ssam /* 850138568Ssam * Use a mixed 11b/11g rate set. 851138568Ssam */ 852116742Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 853116742Ssam IEEE80211_MODE_11G); 854138568Ssam } else if (mode == IEEE80211_MODE_11B) { 855138568Ssam /* 856138568Ssam * Force pure 11b rate set. 857138568Ssam */ 858138568Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 859138568Ssam IEEE80211_MODE_11B); 860116742Ssam } 861138568Ssam /* 862138568Ssam * Setup an initial rate set according to the 863138568Ssam * current/default channel selected above. This 864138568Ssam * will be changed when scanning but must exist 865138568Ssam * now so driver have a consistent state of ic_ibss_chan. 866138568Ssam */ 867138568Ssam if (ic->ic_bss) /* NB: can be called before lateattach */ 868138568Ssam ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; 869116742Ssam 870116742Ssam ic->ic_curmode = mode; 871138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 872138568Ssam ieee80211_wme_initparams(ic); /* reset WME stat */ 873138568Ssam 874116742Ssam return 0; 875116742Ssam#undef N 876116742Ssam} 877116742Ssam 878116742Ssam/* 879116742Ssam * Return the phy mode for with the specified channel so the 880138568Ssam * caller can select a rate set. This is problematic for channels 881138568Ssam * where multiple operating modes are possible (e.g. 11g+11b). 882138568Ssam * In those cases we defer to the current operating mode when set. 883116742Ssam */ 884116742Ssamenum ieee80211_phymode 885116742Ssamieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) 886116742Ssam{ 887138568Ssam if (IEEE80211_IS_CHAN_5GHZ(chan)) { 888138568Ssam /* 889138568Ssam * This assumes all 11a turbo channels are also 890138568Ssam * usable withut turbo, which is currently true. 891138568Ssam */ 892138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_A) 893138568Ssam return IEEE80211_MODE_TURBO_A; 894116742Ssam return IEEE80211_MODE_11A; 895138568Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) 896124543Sonoe return IEEE80211_MODE_FH; 897138568Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { 898138568Ssam /* 899138568Ssam * This assumes all 11g channels are also usable 900138568Ssam * for 11b, which is currently true. 901138568Ssam */ 902138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) 903138568Ssam return IEEE80211_MODE_TURBO_G; 904138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11B) 905138568Ssam return IEEE80211_MODE_11B; 906116742Ssam return IEEE80211_MODE_11G; 907138568Ssam } else 908116742Ssam return IEEE80211_MODE_11B; 909116742Ssam} 910116742Ssam 911116742Ssam/* 912116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 913116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 914116742Ssam */ 915116742Ssamint 916116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 917116742Ssam{ 918116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 919116742Ssam static const struct { 920116742Ssam u_int m; /* rate + mode */ 921116742Ssam u_int r; /* if_media rate */ 922116742Ssam } rates[] = { 923124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 924124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 925124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 926124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 927124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 928124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 929124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 930124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 931124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 932124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 933124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 934124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 935124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 936124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 937124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 938124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 939124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 940124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 941124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 942124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 943124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 944124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 945124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 946124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 947124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 948124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 949124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 950116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 951116742Ssam }; 952116742Ssam u_int mask, i; 953116742Ssam 954116742Ssam mask = rate & IEEE80211_RATE_VAL; 955116742Ssam switch (mode) { 956116742Ssam case IEEE80211_MODE_11A: 957138568Ssam case IEEE80211_MODE_TURBO_A: 958124543Sonoe mask |= IFM_IEEE80211_11A; 959116742Ssam break; 960116742Ssam case IEEE80211_MODE_11B: 961124543Sonoe mask |= IFM_IEEE80211_11B; 962116742Ssam break; 963124543Sonoe case IEEE80211_MODE_FH: 964124543Sonoe mask |= IFM_IEEE80211_FH; 965124543Sonoe break; 966116742Ssam case IEEE80211_MODE_AUTO: 967116742Ssam /* NB: ic may be NULL for some drivers */ 968116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 969124543Sonoe mask |= IFM_IEEE80211_FH; 970124543Sonoe break; 971116742Ssam } 972116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 973116742Ssam /* fall thru... */ 974116742Ssam case IEEE80211_MODE_11G: 975138568Ssam case IEEE80211_MODE_TURBO_G: 976124543Sonoe mask |= IFM_IEEE80211_11G; 977116742Ssam break; 978116742Ssam } 979116742Ssam for (i = 0; i < N(rates); i++) 980116742Ssam if (rates[i].m == mask) 981116742Ssam return rates[i].r; 982116742Ssam return IFM_AUTO; 983116742Ssam#undef N 984116742Ssam} 985116742Ssam 986116742Ssamint 987116742Ssamieee80211_media2rate(int mword) 988116742Ssam{ 989116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 990116742Ssam static const int ieeerates[] = { 991116742Ssam -1, /* IFM_AUTO */ 992116742Ssam 0, /* IFM_MANUAL */ 993116742Ssam 0, /* IFM_NONE */ 994116742Ssam 2, /* IFM_IEEE80211_FH1 */ 995116742Ssam 4, /* IFM_IEEE80211_FH2 */ 996116742Ssam 2, /* IFM_IEEE80211_DS1 */ 997116742Ssam 4, /* IFM_IEEE80211_DS2 */ 998116742Ssam 11, /* IFM_IEEE80211_DS5 */ 999116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1000116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1001116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1002116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1003116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1004116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1005116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1006116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1007116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1008116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1009116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1010116742Ssam }; 1011116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1012116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1013116742Ssam#undef N 1014116742Ssam} 1015