ieee80211.c revision 148936
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 148936 2005-08-10 16:22:30Z 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; 170148936Ssam if (ic->ic_curchan == NULL) { 171148936Ssam /* arbitrarily pick the first channel */ 172148936Ssam ic->ic_curchan = &ic->ic_channels[i]; 173148936Ssam } 174116742Ssam } 175116742Ssam } 176116742Ssam /* validate ic->ic_curmode */ 177116742Ssam if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0) 178116742Ssam ic->ic_curmode = IEEE80211_MODE_AUTO; 179127760Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 180139526Ssam#if 0 181138568Ssam /* 182138568Ssam * Enable WME by default if we're capable. 183138568Ssam */ 184138568Ssam if (ic->ic_caps & IEEE80211_C_WME) 185138568Ssam ic->ic_flags |= IEEE80211_F_WME; 186139526Ssam#endif 187116742Ssam (void) ieee80211_setmode(ic, ic->ic_curmode); 188116742Ssam 189148843Ssam if (ic->ic_bintval == 0) 190148843Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 191148843Ssam ic->ic_bmisstimeout = 7*ic->ic_bintval; /* default 7 beacons */ 192138568Ssam ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; 193138568Ssam IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); 194116742Ssam 195148843Ssam if (ic->ic_lintval == 0) 196148843Ssam ic->ic_lintval = ic->ic_bintval; 197138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 198138568Ssam 199138568Ssam ieee80211_node_attach(ic); 200138568Ssam ieee80211_proto_attach(ic); 201138568Ssam 202138568Ssam ieee80211_add_vap(ic); 203138568Ssam 204138568Ssam ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ 205140915Ssam 206140915Ssam /* 207140915Ssam * Install a default reset method for the ioctl support. 208140915Ssam * The driver is expected to fill this in before calling us. 209140915Ssam */ 210140915Ssam if (ic->ic_reset == NULL) 211140915Ssam ic->ic_reset = ieee80211_default_reset; 212116742Ssam} 213116742Ssam 214116742Ssamvoid 215138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 216116742Ssam{ 217138568Ssam struct ifnet *ifp = ic->ic_ifp; 218116742Ssam 219138568Ssam ieee80211_remove_vap(ic); 220138568Ssam 221138568Ssam ieee80211_sysctl_detach(ic); 222138568Ssam ieee80211_proto_detach(ic); 223138568Ssam ieee80211_crypto_detach(ic); 224138568Ssam ieee80211_node_detach(ic); 225116742Ssam ifmedia_removeall(&ic->ic_media); 226138568Ssam 227138568Ssam IEEE80211_BEACON_LOCK_DESTROY(ic); 228138568Ssam 229116742Ssam bpfdetach(ifp); 230116742Ssam ether_ifdetach(ifp); 231116742Ssam} 232116742Ssam 233116742Ssam/* 234116742Ssam * Convert MHz frequency to IEEE channel number. 235116742Ssam */ 236116742Ssamu_int 237116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 238116742Ssam{ 239116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 240116742Ssam if (freq == 2484) 241116742Ssam return 14; 242116742Ssam if (freq < 2484) 243116742Ssam return (freq - 2407) / 5; 244116742Ssam else 245116742Ssam return 15 + ((freq - 2512) / 20); 246116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 247116742Ssam return (freq - 5000) / 5; 248116742Ssam } else { /* either, guess */ 249116742Ssam if (freq == 2484) 250116742Ssam return 14; 251116742Ssam if (freq < 2484) 252116742Ssam return (freq - 2407) / 5; 253116742Ssam if (freq < 5000) 254116742Ssam return 15 + ((freq - 2512) / 20); 255116742Ssam return (freq - 5000) / 5; 256116742Ssam } 257116742Ssam} 258116742Ssam 259116742Ssam/* 260116742Ssam * Convert channel to IEEE channel number. 261116742Ssam */ 262116742Ssamu_int 263116742Ssamieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) 264116742Ssam{ 265116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 266116742Ssam return c - ic->ic_channels; 267116742Ssam else if (c == IEEE80211_CHAN_ANYC) 268116742Ssam return IEEE80211_CHAN_ANY; 269117039Ssam else if (c != NULL) { 270138568Ssam if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", 271116742Ssam c->ic_freq, c->ic_flags); 272116742Ssam return 0; /* XXX */ 273117039Ssam } else { 274138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 275117039Ssam return 0; /* XXX */ 276116742Ssam } 277116742Ssam} 278116742Ssam 279116742Ssam/* 280116742Ssam * Convert IEEE channel number to MHz frequency. 281116742Ssam */ 282116742Ssamu_int 283116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 284116742Ssam{ 285116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 286116742Ssam if (chan == 14) 287116742Ssam return 2484; 288116742Ssam if (chan < 14) 289116742Ssam return 2407 + chan*5; 290116742Ssam else 291116742Ssam return 2512 + ((chan-15)*20); 292116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 293116742Ssam return 5000 + (chan*5); 294116742Ssam } else { /* either, guess */ 295116742Ssam if (chan == 14) 296116742Ssam return 2484; 297116742Ssam if (chan < 14) /* 0-13 */ 298116742Ssam return 2407 + chan*5; 299116742Ssam if (chan < 27) /* 15-26 */ 300116742Ssam return 2512 + ((chan-15)*20); 301116742Ssam return 5000 + (chan*5); 302116742Ssam } 303116742Ssam} 304116742Ssam 305116742Ssam/* 306116742Ssam * Setup the media data structures according to the channel and 307116742Ssam * rate tables. This must be called by the driver after 308116742Ssam * ieee80211_attach and before most anything else. 309116742Ssam */ 310116742Ssamvoid 311138568Ssamieee80211_media_init(struct ieee80211com *ic, 312116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 313116742Ssam{ 314116742Ssam#define ADD(_ic, _s, _o) \ 315116742Ssam ifmedia_add(&(_ic)->ic_media, \ 316116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 317138568Ssam struct ifnet *ifp = ic->ic_ifp; 318116742Ssam struct ifmediareq imr; 319116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 320116742Ssam struct ieee80211_rateset *rs; 321116742Ssam struct ieee80211_rateset allrates; 322116742Ssam 323116742Ssam /* 324118887Ssam * Do late attach work that must wait for any subclass 325118887Ssam * (i.e. driver) work such as overriding methods. 326118887Ssam */ 327138568Ssam ieee80211_node_lateattach(ic); 328118887Ssam 329118887Ssam /* 330116742Ssam * Fill in media characteristics. 331116742Ssam */ 332116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 333116742Ssam maxrate = 0; 334116742Ssam memset(&allrates, 0, sizeof(allrates)); 335116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 336116742Ssam static const u_int mopts[] = { 337116742Ssam IFM_AUTO, 338124543Sonoe IFM_IEEE80211_11A, 339124543Sonoe IFM_IEEE80211_11B, 340124543Sonoe IFM_IEEE80211_11G, 341124543Sonoe IFM_IEEE80211_FH, 342124543Sonoe IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 343138568Ssam IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, 344116742Ssam }; 345116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 346116742Ssam continue; 347116742Ssam mopt = mopts[mode]; 348116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 349116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 350116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 351116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 352116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 353116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 354116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 355117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 356117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 357116742Ssam if (mode == IEEE80211_MODE_AUTO) 358116742Ssam continue; 359116742Ssam rs = &ic->ic_sup_rates[mode]; 360116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 361116742Ssam rate = rs->rs_rates[i]; 362116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 363116742Ssam if (mword == 0) 364116742Ssam continue; 365116742Ssam ADD(ic, mword, mopt); 366116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 367116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 368116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 369116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 370116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 371116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 372117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 373117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 374116742Ssam /* 375116742Ssam * Add rate to the collection of all rates. 376116742Ssam */ 377116742Ssam r = rate & IEEE80211_RATE_VAL; 378116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 379116742Ssam if (allrates.rs_rates[j] == r) 380116742Ssam break; 381116742Ssam if (j == allrates.rs_nrates) { 382116742Ssam /* unique, add to the set */ 383116742Ssam allrates.rs_rates[j] = r; 384116742Ssam allrates.rs_nrates++; 385116742Ssam } 386116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 387116742Ssam if (rate > maxrate) 388116742Ssam maxrate = rate; 389116742Ssam } 390116742Ssam } 391116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 392116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 393116742Ssam IEEE80211_MODE_AUTO); 394116742Ssam if (mword == 0) 395116742Ssam continue; 396116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 397116742Ssam ADD(ic, mword, 0); 398116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 399116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 400116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 401116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 402116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 403116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 404117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 405117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 406116742Ssam } 407116742Ssam ieee80211_media_status(ifp, &imr); 408116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 409116742Ssam 410116742Ssam if (maxrate) 411116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 412116742Ssam#undef ADD 413116742Ssam} 414116742Ssam 415138568Ssamvoid 416138568Ssamieee80211_announce(struct ieee80211com *ic) 417138568Ssam{ 418138568Ssam struct ifnet *ifp = ic->ic_ifp; 419138568Ssam int i, mode, rate, mword; 420138568Ssam struct ieee80211_rateset *rs; 421138568Ssam 422138568Ssam for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { 423138568Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 424138568Ssam continue; 425138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 426138568Ssam rs = &ic->ic_sup_rates[mode]; 427138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 428138568Ssam rate = rs->rs_rates[i]; 429138568Ssam mword = ieee80211_rate2media(ic, rate, mode); 430138568Ssam if (mword == 0) 431138568Ssam continue; 432138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 433138568Ssam (rate & IEEE80211_RATE_VAL) / 2, 434138568Ssam ((rate & 0x1) != 0 ? ".5" : "")); 435138568Ssam } 436138568Ssam printf("\n"); 437138568Ssam } 438138568Ssam} 439138568Ssam 440116742Ssamstatic int 441116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 442116742Ssam{ 443116742Ssam#define IEEERATE(_ic,_m,_i) \ 444116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 445116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 446116742Ssam for (i = 0; i < nrates; i++) 447116742Ssam if (IEEERATE(ic, mode, i) == rate) 448116742Ssam return i; 449116742Ssam return -1; 450116742Ssam#undef IEEERATE 451116742Ssam} 452116742Ssam 453116742Ssam/* 454138568Ssam * Find an instance by it's mac address. 455138568Ssam */ 456138568Ssamstruct ieee80211com * 457138568Ssamieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) 458138568Ssam{ 459138568Ssam struct ieee80211com *ic; 460138568Ssam 461138568Ssam /* XXX lock */ 462138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 463138568Ssam if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) 464138568Ssam return ic; 465138568Ssam return NULL; 466138568Ssam} 467138568Ssam 468138568Ssamstatic struct ieee80211com * 469138568Ssamieee80211_find_instance(struct ifnet *ifp) 470138568Ssam{ 471138568Ssam struct ieee80211com *ic; 472138568Ssam 473138568Ssam /* XXX lock */ 474138568Ssam /* XXX not right for multiple instances but works for now */ 475138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 476138568Ssam if (ic->ic_ifp == ifp) 477138568Ssam return ic; 478138568Ssam return NULL; 479138568Ssam} 480138568Ssam 481138568Ssam/* 482116742Ssam * Handle a media change request. 483116742Ssam */ 484116742Ssamint 485116742Ssamieee80211_media_change(struct ifnet *ifp) 486116742Ssam{ 487138568Ssam struct ieee80211com *ic; 488116742Ssam struct ifmedia_entry *ime; 489116742Ssam enum ieee80211_opmode newopmode; 490116742Ssam enum ieee80211_phymode newphymode; 491116742Ssam int i, j, newrate, error = 0; 492116742Ssam 493138568Ssam ic = ieee80211_find_instance(ifp); 494138568Ssam if (!ic) { 495138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 496138568Ssam return EINVAL; 497138568Ssam } 498116742Ssam ime = ic->ic_media.ifm_cur; 499116742Ssam /* 500116742Ssam * First, identify the phy mode. 501116742Ssam */ 502116742Ssam switch (IFM_MODE(ime->ifm_media)) { 503116742Ssam case IFM_IEEE80211_11A: 504116742Ssam newphymode = IEEE80211_MODE_11A; 505116742Ssam break; 506116742Ssam case IFM_IEEE80211_11B: 507116742Ssam newphymode = IEEE80211_MODE_11B; 508116742Ssam break; 509116742Ssam case IFM_IEEE80211_11G: 510116742Ssam newphymode = IEEE80211_MODE_11G; 511116742Ssam break; 512124543Sonoe case IFM_IEEE80211_FH: 513124543Sonoe newphymode = IEEE80211_MODE_FH; 514124543Sonoe break; 515116742Ssam case IFM_AUTO: 516116742Ssam newphymode = IEEE80211_MODE_AUTO; 517116742Ssam break; 518116742Ssam default: 519116742Ssam return EINVAL; 520116742Ssam } 521116742Ssam /* 522138568Ssam * Turbo mode is an ``option''. 523138568Ssam * XXX does not apply to AUTO 524116742Ssam */ 525116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 526138568Ssam if (newphymode == IEEE80211_MODE_11A) 527138568Ssam newphymode = IEEE80211_MODE_TURBO_A; 528138568Ssam else if (newphymode == IEEE80211_MODE_11G) 529138568Ssam newphymode = IEEE80211_MODE_TURBO_G; 530138568Ssam else 531116742Ssam return EINVAL; 532116742Ssam } 533116742Ssam /* 534116742Ssam * Validate requested mode is available. 535116742Ssam */ 536116742Ssam if ((ic->ic_modecaps & (1<<newphymode)) == 0) 537116742Ssam return EINVAL; 538116742Ssam 539116742Ssam /* 540116742Ssam * Next, the fixed/variable rate. 541116742Ssam */ 542116742Ssam i = -1; 543116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 544116742Ssam /* 545116742Ssam * Convert media subtype to rate. 546116742Ssam */ 547116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 548116742Ssam if (newrate == 0) 549116742Ssam return EINVAL; 550116742Ssam /* 551116742Ssam * Check the rate table for the specified/current phy. 552116742Ssam */ 553116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 554116742Ssam /* 555116742Ssam * In autoselect mode search for the rate. 556116742Ssam */ 557116742Ssam for (j = IEEE80211_MODE_11A; 558116742Ssam j < IEEE80211_MODE_MAX; j++) { 559116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 560116742Ssam continue; 561116742Ssam i = findrate(ic, j, newrate); 562116742Ssam if (i != -1) { 563116742Ssam /* lock mode too */ 564116742Ssam newphymode = j; 565116742Ssam break; 566116742Ssam } 567116742Ssam } 568116742Ssam } else { 569116742Ssam i = findrate(ic, newphymode, newrate); 570116742Ssam } 571116742Ssam if (i == -1) /* mode/rate mismatch */ 572116742Ssam return EINVAL; 573116742Ssam } 574116742Ssam /* NB: defer rate setting to later */ 575116742Ssam 576116742Ssam /* 577116742Ssam * Deduce new operating mode but don't install it just yet. 578116742Ssam */ 579116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 580116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 581116742Ssam newopmode = IEEE80211_M_AHDEMO; 582116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 583116742Ssam newopmode = IEEE80211_M_HOSTAP; 584116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 585116742Ssam newopmode = IEEE80211_M_IBSS; 586117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 587117817Ssam newopmode = IEEE80211_M_MONITOR; 588116742Ssam else 589116742Ssam newopmode = IEEE80211_M_STA; 590116742Ssam 591116742Ssam /* 592116742Ssam * Autoselect doesn't make sense when operating as an AP. 593116742Ssam * If no phy mode has been selected, pick one and lock it 594116742Ssam * down so rate tables can be used in forming beacon frames 595116742Ssam * and the like. 596116742Ssam */ 597116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 598116742Ssam newphymode == IEEE80211_MODE_AUTO) { 599116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 600116742Ssam if (ic->ic_modecaps & (1<<j)) { 601116742Ssam newphymode = j; 602116742Ssam break; 603116742Ssam } 604116742Ssam } 605116742Ssam 606116742Ssam /* 607116742Ssam * Handle phy mode change. 608116742Ssam */ 609116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 610116742Ssam error = ieee80211_setmode(ic, newphymode); 611116742Ssam if (error != 0) 612116742Ssam return error; 613116742Ssam error = ENETRESET; 614116742Ssam } 615116742Ssam 616116742Ssam /* 617116742Ssam * Committed to changes, install the rate setting. 618116742Ssam */ 619116742Ssam if (ic->ic_fixed_rate != i) { 620116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 621116742Ssam error = ENETRESET; 622116742Ssam } 623116742Ssam 624116742Ssam /* 625116742Ssam * Handle operating mode change. 626116742Ssam */ 627116742Ssam if (ic->ic_opmode != newopmode) { 628116742Ssam ic->ic_opmode = newopmode; 629116742Ssam switch (newopmode) { 630116742Ssam case IEEE80211_M_AHDEMO: 631116742Ssam case IEEE80211_M_HOSTAP: 632116742Ssam case IEEE80211_M_STA: 633117817Ssam case IEEE80211_M_MONITOR: 634116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 635116742Ssam break; 636116742Ssam case IEEE80211_M_IBSS: 637116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 638116742Ssam break; 639116742Ssam } 640138568Ssam /* 641138568Ssam * Yech, slot time may change depending on the 642138568Ssam * operating mode so reset it to be sure everything 643138568Ssam * is setup appropriately. 644138568Ssam */ 645138568Ssam ieee80211_reset_erp(ic); 646138568Ssam ieee80211_wme_initparams(ic); /* after opmode change */ 647116742Ssam error = ENETRESET; 648116742Ssam } 649116742Ssam#ifdef notdef 650116742Ssam if (error == 0) 651116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 652116742Ssam#endif 653116742Ssam return error; 654116742Ssam} 655116742Ssam 656116742Ssamvoid 657116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 658116742Ssam{ 659138568Ssam struct ieee80211com *ic; 660138568Ssam struct ieee80211_rateset *rs; 661116742Ssam 662138568Ssam ic = ieee80211_find_instance(ifp); 663138568Ssam if (!ic) { 664138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 665138568Ssam return; 666138568Ssam } 667116742Ssam imr->ifm_status = IFM_AVALID; 668116742Ssam imr->ifm_active = IFM_IEEE80211; 669138568Ssam if (ic->ic_state == IEEE80211_S_RUN) 670116742Ssam imr->ifm_status |= IFM_ACTIVE; 671138568Ssam /* 672138568Ssam * Calculate a current rate if possible. 673138568Ssam */ 674148290Ssam if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 675138568Ssam /* 676138568Ssam * A fixed rate is set, report that. 677138568Ssam */ 678138568Ssam rs = &ic->ic_sup_rates[ic->ic_curmode]; 679138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 680138568Ssam rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); 681138568Ssam } else if (ic->ic_opmode == IEEE80211_M_STA) { 682138568Ssam /* 683138568Ssam * In station mode report the current transmit rate. 684138568Ssam */ 685138568Ssam rs = &ic->ic_bss->ni_rates; 686138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 687138568Ssam rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); 688128966Sandre } else 689138568Ssam imr->ifm_active |= IFM_AUTO; 690116742Ssam switch (ic->ic_opmode) { 691116742Ssam case IEEE80211_M_STA: 692116742Ssam break; 693116742Ssam case IEEE80211_M_IBSS: 694116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 695116742Ssam break; 696116742Ssam case IEEE80211_M_AHDEMO: 697116742Ssam /* should not come here */ 698116742Ssam break; 699116742Ssam case IEEE80211_M_HOSTAP: 700116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 701116742Ssam break; 702117817Ssam case IEEE80211_M_MONITOR: 703117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 704117817Ssam break; 705116742Ssam } 706116742Ssam switch (ic->ic_curmode) { 707116742Ssam case IEEE80211_MODE_11A: 708124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A; 709116742Ssam break; 710116742Ssam case IEEE80211_MODE_11B: 711124543Sonoe imr->ifm_active |= IFM_IEEE80211_11B; 712116742Ssam break; 713116742Ssam case IEEE80211_MODE_11G: 714124543Sonoe imr->ifm_active |= IFM_IEEE80211_11G; 715116742Ssam break; 716124543Sonoe case IEEE80211_MODE_FH: 717124543Sonoe imr->ifm_active |= IFM_IEEE80211_FH; 718124543Sonoe break; 719138568Ssam case IEEE80211_MODE_TURBO_A: 720124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A 721116742Ssam | IFM_IEEE80211_TURBO; 722116742Ssam break; 723138568Ssam case IEEE80211_MODE_TURBO_G: 724138568Ssam imr->ifm_active |= IFM_IEEE80211_11G 725138568Ssam | IFM_IEEE80211_TURBO; 726138568Ssam break; 727116742Ssam } 728116742Ssam} 729116742Ssam 730116742Ssamvoid 731138568Ssamieee80211_watchdog(struct ieee80211com *ic) 732116742Ssam{ 733138568Ssam struct ieee80211_node_table *nt; 734138568Ssam int need_inact_timer = 0; 735116742Ssam 736138568Ssam if (ic->ic_state != IEEE80211_S_INIT) { 737138568Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 738138568Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 739138568Ssam nt = &ic->ic_scan; 740138568Ssam 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 } 745140753Ssam nt = &ic->ic_sta; 746140753Ssam if (nt->nt_inact_timer) { 747138568Ssam if (--nt->nt_inact_timer == 0) 748138568Ssam nt->nt_timeout(nt); 749138568Ssam need_inact_timer += nt->nt_inact_timer; 750138568Ssam } 751116742Ssam } 752138568Ssam if (ic->ic_mgt_timer != 0 || need_inact_timer) 753138568Ssam ic->ic_ifp->if_timer = 1; 754116742Ssam} 755116742Ssam 756116742Ssam/* 757116742Ssam * Set the current phy mode and recalculate the active channel 758116742Ssam * set based on the available channels for this mode. Also 759116742Ssam * select a new default/current channel if the current one is 760116742Ssam * inappropriate for this mode. 761116742Ssam */ 762116742Ssamint 763116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 764116742Ssam{ 765116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 766116742Ssam static const u_int chanflags[] = { 767116742Ssam 0, /* IEEE80211_MODE_AUTO */ 768116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 769116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 770116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 771124543Sonoe IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 772138568Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ 773138568Ssam IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 774116742Ssam }; 775116742Ssam struct ieee80211_channel *c; 776116742Ssam u_int modeflags; 777116742Ssam int i; 778116742Ssam 779116742Ssam /* validate new mode */ 780116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) { 781138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 782138568Ssam "%s: mode %u not supported (caps 0x%x)\n", 783138568Ssam __func__, mode, ic->ic_modecaps); 784116742Ssam return EINVAL; 785116742Ssam } 786116742Ssam 787116742Ssam /* 788116742Ssam * Verify at least one channel is present in the available 789116742Ssam * channel list before committing to the new mode. 790116742Ssam */ 791127760Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); 792116742Ssam modeflags = chanflags[mode]; 793116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 794116742Ssam c = &ic->ic_channels[i]; 795116742Ssam if (mode == IEEE80211_MODE_AUTO) { 796116742Ssam /* ignore turbo channels for autoselect */ 797116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 798116742Ssam break; 799116742Ssam } else { 800116742Ssam if ((c->ic_flags & modeflags) == modeflags) 801116742Ssam break; 802116742Ssam } 803116742Ssam } 804116742Ssam if (i > IEEE80211_CHAN_MAX) { 805138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 806138568Ssam "%s: no channels found for mode %u\n", __func__, mode); 807116742Ssam return EINVAL; 808116742Ssam } 809116742Ssam 810116742Ssam /* 811116742Ssam * Calculate the active channel set. 812116742Ssam */ 813116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 814116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 815116742Ssam c = &ic->ic_channels[i]; 816116742Ssam if (mode == IEEE80211_MODE_AUTO) { 817116742Ssam /* take anything but pure turbo channels */ 818116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 819116742Ssam setbit(ic->ic_chan_active, i); 820116742Ssam } else { 821116742Ssam if ((c->ic_flags & modeflags) == modeflags) 822116742Ssam setbit(ic->ic_chan_active, i); 823116742Ssam } 824116742Ssam } 825116742Ssam /* 826116742Ssam * If no current/default channel is setup or the current 827116742Ssam * channel is wrong for the mode then pick the first 828116742Ssam * available channel from the active list. This is likely 829116742Ssam * not the right one. 830116742Ssam */ 831116742Ssam if (ic->ic_ibss_chan == NULL || 832116742Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 833116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 834116742Ssam if (isset(ic->ic_chan_active, i)) { 835116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 836116742Ssam break; 837116742Ssam } 838138568Ssam KASSERT(ic->ic_ibss_chan != NULL && 839138568Ssam isset(ic->ic_chan_active, 840138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), 841138568Ssam ("Bad IBSS channel %u", 842138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); 843116742Ssam } 844138568Ssam /* 845138568Ssam * If the desired channel is set but no longer valid then reset it. 846138568Ssam */ 847138568Ssam if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 848138568Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) 849138568Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 850116742Ssam 851116742Ssam /* 852138568Ssam * Do mode-specific rate setup. 853116742Ssam */ 854116742Ssam if (mode == IEEE80211_MODE_11G) { 855138568Ssam /* 856138568Ssam * Use a mixed 11b/11g rate set. 857138568Ssam */ 858116742Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 859116742Ssam IEEE80211_MODE_11G); 860138568Ssam } else if (mode == IEEE80211_MODE_11B) { 861138568Ssam /* 862138568Ssam * Force pure 11b rate set. 863138568Ssam */ 864138568Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 865138568Ssam IEEE80211_MODE_11B); 866116742Ssam } 867138568Ssam /* 868138568Ssam * Setup an initial rate set according to the 869138568Ssam * current/default channel selected above. This 870138568Ssam * will be changed when scanning but must exist 871138568Ssam * now so driver have a consistent state of ic_ibss_chan. 872138568Ssam */ 873138568Ssam if (ic->ic_bss) /* NB: can be called before lateattach */ 874138568Ssam ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; 875116742Ssam 876116742Ssam ic->ic_curmode = mode; 877138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 878138568Ssam ieee80211_wme_initparams(ic); /* reset WME stat */ 879138568Ssam 880116742Ssam return 0; 881116742Ssam#undef N 882116742Ssam} 883116742Ssam 884116742Ssam/* 885116742Ssam * Return the phy mode for with the specified channel so the 886138568Ssam * caller can select a rate set. This is problematic for channels 887138568Ssam * where multiple operating modes are possible (e.g. 11g+11b). 888138568Ssam * In those cases we defer to the current operating mode when set. 889116742Ssam */ 890116742Ssamenum ieee80211_phymode 891116742Ssamieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) 892116742Ssam{ 893138568Ssam if (IEEE80211_IS_CHAN_5GHZ(chan)) { 894138568Ssam /* 895138568Ssam * This assumes all 11a turbo channels are also 896138568Ssam * usable withut turbo, which is currently true. 897138568Ssam */ 898138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_A) 899138568Ssam return IEEE80211_MODE_TURBO_A; 900116742Ssam return IEEE80211_MODE_11A; 901138568Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) 902124543Sonoe return IEEE80211_MODE_FH; 903138568Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { 904138568Ssam /* 905138568Ssam * This assumes all 11g channels are also usable 906138568Ssam * for 11b, which is currently true. 907138568Ssam */ 908138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) 909138568Ssam return IEEE80211_MODE_TURBO_G; 910138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11B) 911138568Ssam return IEEE80211_MODE_11B; 912116742Ssam return IEEE80211_MODE_11G; 913138568Ssam } else 914116742Ssam return IEEE80211_MODE_11B; 915116742Ssam} 916116742Ssam 917116742Ssam/* 918116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 919116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 920116742Ssam */ 921116742Ssamint 922116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 923116742Ssam{ 924116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 925116742Ssam static const struct { 926116742Ssam u_int m; /* rate + mode */ 927116742Ssam u_int r; /* if_media rate */ 928116742Ssam } rates[] = { 929124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 930124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 931124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 932124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 933124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 934124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 935124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 936124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 937124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 938124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 939124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 940124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 941124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 942124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 943124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 944124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 945124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 946124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 947124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 948124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 949124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 950124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 951124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 952124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 953124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 954124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 955124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 956116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 957116742Ssam }; 958116742Ssam u_int mask, i; 959116742Ssam 960116742Ssam mask = rate & IEEE80211_RATE_VAL; 961116742Ssam switch (mode) { 962116742Ssam case IEEE80211_MODE_11A: 963138568Ssam case IEEE80211_MODE_TURBO_A: 964124543Sonoe mask |= IFM_IEEE80211_11A; 965116742Ssam break; 966116742Ssam case IEEE80211_MODE_11B: 967124543Sonoe mask |= IFM_IEEE80211_11B; 968116742Ssam break; 969124543Sonoe case IEEE80211_MODE_FH: 970124543Sonoe mask |= IFM_IEEE80211_FH; 971124543Sonoe break; 972116742Ssam case IEEE80211_MODE_AUTO: 973116742Ssam /* NB: ic may be NULL for some drivers */ 974116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 975124543Sonoe mask |= IFM_IEEE80211_FH; 976124543Sonoe break; 977116742Ssam } 978116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 979116742Ssam /* fall thru... */ 980116742Ssam case IEEE80211_MODE_11G: 981138568Ssam case IEEE80211_MODE_TURBO_G: 982124543Sonoe mask |= IFM_IEEE80211_11G; 983116742Ssam break; 984116742Ssam } 985116742Ssam for (i = 0; i < N(rates); i++) 986116742Ssam if (rates[i].m == mask) 987116742Ssam return rates[i].r; 988116742Ssam return IFM_AUTO; 989116742Ssam#undef N 990116742Ssam} 991116742Ssam 992116742Ssamint 993116742Ssamieee80211_media2rate(int mword) 994116742Ssam{ 995116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 996116742Ssam static const int ieeerates[] = { 997116742Ssam -1, /* IFM_AUTO */ 998116742Ssam 0, /* IFM_MANUAL */ 999116742Ssam 0, /* IFM_NONE */ 1000116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1001116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1002116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1003116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1004116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1005116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1006116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1007116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1008116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1009116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1010116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1011116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1012116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1013116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1014116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1015116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1016116742Ssam }; 1017116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1018116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1019116742Ssam#undef N 1020116742Ssam} 1021