ieee80211.c revision 139530
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 139530 2004-12-31 22:42:38Z 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 106116742Ssamvoid 107138568Ssamieee80211_ifattach(struct ieee80211com *ic) 108116742Ssam{ 109138568Ssam struct ifnet *ifp = ic->ic_ifp; 110116742Ssam struct ieee80211_channel *c; 111116742Ssam int i; 112116742Ssam 113116742Ssam ether_ifattach(ifp, ic->ic_myaddr); 114116742Ssam bpfattach2(ifp, DLT_IEEE802_11, 115116742Ssam sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); 116116742Ssam 117138568Ssam ieee80211_crypto_attach(ic); 118138568Ssam 119116742Ssam /* 120116742Ssam * Fill in 802.11 available channel set, mark 121116742Ssam * all available channels as active, and pick 122116742Ssam * a default channel if not already specified. 123116742Ssam */ 124116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 125116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO; 126116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 127116742Ssam c = &ic->ic_channels[i]; 128116742Ssam if (c->ic_flags) { 129116742Ssam /* 130116742Ssam * Verify driver passed us valid data. 131116742Ssam */ 132116742Ssam if (i != ieee80211_chan2ieee(ic, c)) { 133116742Ssam if_printf(ifp, "bad channel ignored; " 134116742Ssam "freq %u flags %x number %u\n", 135116742Ssam c->ic_freq, c->ic_flags, i); 136116742Ssam c->ic_flags = 0; /* NB: remove */ 137116742Ssam continue; 138116742Ssam } 139116742Ssam setbit(ic->ic_chan_avail, i); 140116742Ssam /* 141116742Ssam * Identify mode capabilities. 142116742Ssam */ 143116742Ssam if (IEEE80211_IS_CHAN_A(c)) 144116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11A; 145116742Ssam if (IEEE80211_IS_CHAN_B(c)) 146116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11B; 147116742Ssam if (IEEE80211_IS_CHAN_PUREG(c)) 148116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; 149124543Sonoe if (IEEE80211_IS_CHAN_FHSS(c)) 150124543Sonoe ic->ic_modecaps |= 1<<IEEE80211_MODE_FH; 151116742Ssam if (IEEE80211_IS_CHAN_T(c)) 152138568Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A; 153138568Ssam if (IEEE80211_IS_CHAN_108G(c)) 154138568Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G; 155116742Ssam } 156116742Ssam } 157116742Ssam /* validate ic->ic_curmode */ 158116742Ssam if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0) 159116742Ssam ic->ic_curmode = IEEE80211_MODE_AUTO; 160127760Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 161139526Ssam#if 0 162138568Ssam /* 163138568Ssam * Enable WME by default if we're capable. 164138568Ssam */ 165138568Ssam if (ic->ic_caps & IEEE80211_C_WME) 166138568Ssam ic->ic_flags |= IEEE80211_F_WME; 167139526Ssam#endif 168116742Ssam (void) ieee80211_setmode(ic, ic->ic_curmode); 169116742Ssam 170116742Ssam if (ic->ic_lintval == 0) 171138568Ssam ic->ic_lintval = IEEE80211_BINTVAL_DEFAULT; 172116742Ssam ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ 173138568Ssam ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; 174138568Ssam IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); 175116742Ssam 176138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 177138568Ssam 178138568Ssam ieee80211_node_attach(ic); 179138568Ssam ieee80211_proto_attach(ic); 180138568Ssam 181138568Ssam ieee80211_add_vap(ic); 182138568Ssam 183138568Ssam ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ 184116742Ssam} 185116742Ssam 186116742Ssamvoid 187138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 188116742Ssam{ 189138568Ssam struct ifnet *ifp = ic->ic_ifp; 190116742Ssam 191138568Ssam ieee80211_remove_vap(ic); 192138568Ssam 193138568Ssam ieee80211_sysctl_detach(ic); 194138568Ssam ieee80211_proto_detach(ic); 195138568Ssam ieee80211_crypto_detach(ic); 196138568Ssam ieee80211_node_detach(ic); 197116742Ssam ifmedia_removeall(&ic->ic_media); 198138568Ssam 199138568Ssam IEEE80211_BEACON_LOCK_DESTROY(ic); 200138568Ssam 201116742Ssam bpfdetach(ifp); 202116742Ssam ether_ifdetach(ifp); 203116742Ssam} 204116742Ssam 205116742Ssam/* 206116742Ssam * Convert MHz frequency to IEEE channel number. 207116742Ssam */ 208116742Ssamu_int 209116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 210116742Ssam{ 211116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 212116742Ssam if (freq == 2484) 213116742Ssam return 14; 214116742Ssam if (freq < 2484) 215116742Ssam return (freq - 2407) / 5; 216116742Ssam else 217116742Ssam return 15 + ((freq - 2512) / 20); 218116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 219116742Ssam return (freq - 5000) / 5; 220116742Ssam } else { /* either, guess */ 221116742Ssam if (freq == 2484) 222116742Ssam return 14; 223116742Ssam if (freq < 2484) 224116742Ssam return (freq - 2407) / 5; 225116742Ssam if (freq < 5000) 226116742Ssam return 15 + ((freq - 2512) / 20); 227116742Ssam return (freq - 5000) / 5; 228116742Ssam } 229116742Ssam} 230116742Ssam 231116742Ssam/* 232116742Ssam * Convert channel to IEEE channel number. 233116742Ssam */ 234116742Ssamu_int 235116742Ssamieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) 236116742Ssam{ 237116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 238116742Ssam return c - ic->ic_channels; 239116742Ssam else if (c == IEEE80211_CHAN_ANYC) 240116742Ssam return IEEE80211_CHAN_ANY; 241117039Ssam else if (c != NULL) { 242138568Ssam if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", 243116742Ssam c->ic_freq, c->ic_flags); 244116742Ssam return 0; /* XXX */ 245117039Ssam } else { 246138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 247117039Ssam return 0; /* XXX */ 248116742Ssam } 249116742Ssam} 250116742Ssam 251116742Ssam/* 252116742Ssam * Convert IEEE channel number to MHz frequency. 253116742Ssam */ 254116742Ssamu_int 255116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 256116742Ssam{ 257116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 258116742Ssam if (chan == 14) 259116742Ssam return 2484; 260116742Ssam if (chan < 14) 261116742Ssam return 2407 + chan*5; 262116742Ssam else 263116742Ssam return 2512 + ((chan-15)*20); 264116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 265116742Ssam return 5000 + (chan*5); 266116742Ssam } else { /* either, guess */ 267116742Ssam if (chan == 14) 268116742Ssam return 2484; 269116742Ssam if (chan < 14) /* 0-13 */ 270116742Ssam return 2407 + chan*5; 271116742Ssam if (chan < 27) /* 15-26 */ 272116742Ssam return 2512 + ((chan-15)*20); 273116742Ssam return 5000 + (chan*5); 274116742Ssam } 275116742Ssam} 276116742Ssam 277116742Ssam/* 278116742Ssam * Setup the media data structures according to the channel and 279116742Ssam * rate tables. This must be called by the driver after 280116742Ssam * ieee80211_attach and before most anything else. 281116742Ssam */ 282116742Ssamvoid 283138568Ssamieee80211_media_init(struct ieee80211com *ic, 284116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 285116742Ssam{ 286116742Ssam#define ADD(_ic, _s, _o) \ 287116742Ssam ifmedia_add(&(_ic)->ic_media, \ 288116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 289138568Ssam struct ifnet *ifp = ic->ic_ifp; 290116742Ssam struct ifmediareq imr; 291116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 292116742Ssam struct ieee80211_rateset *rs; 293116742Ssam struct ieee80211_rateset allrates; 294116742Ssam 295116742Ssam /* 296118887Ssam * Do late attach work that must wait for any subclass 297118887Ssam * (i.e. driver) work such as overriding methods. 298118887Ssam */ 299138568Ssam ieee80211_node_lateattach(ic); 300118887Ssam 301118887Ssam /* 302116742Ssam * Fill in media characteristics. 303116742Ssam */ 304116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 305116742Ssam maxrate = 0; 306116742Ssam memset(&allrates, 0, sizeof(allrates)); 307116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 308116742Ssam static const u_int mopts[] = { 309116742Ssam IFM_AUTO, 310124543Sonoe IFM_IEEE80211_11A, 311124543Sonoe IFM_IEEE80211_11B, 312124543Sonoe IFM_IEEE80211_11G, 313124543Sonoe IFM_IEEE80211_FH, 314124543Sonoe IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 315138568Ssam IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, 316116742Ssam }; 317116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 318116742Ssam continue; 319116742Ssam mopt = mopts[mode]; 320116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 321116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 322116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 323116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 324116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 325116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 326116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 327117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 328117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 329116742Ssam if (mode == IEEE80211_MODE_AUTO) 330116742Ssam continue; 331116742Ssam rs = &ic->ic_sup_rates[mode]; 332116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 333116742Ssam rate = rs->rs_rates[i]; 334116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 335116742Ssam if (mword == 0) 336116742Ssam continue; 337116742Ssam ADD(ic, mword, mopt); 338116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 339116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 340116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 341116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 342116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 343116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 344117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 345117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 346116742Ssam /* 347116742Ssam * Add rate to the collection of all rates. 348116742Ssam */ 349116742Ssam r = rate & IEEE80211_RATE_VAL; 350116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 351116742Ssam if (allrates.rs_rates[j] == r) 352116742Ssam break; 353116742Ssam if (j == allrates.rs_nrates) { 354116742Ssam /* unique, add to the set */ 355116742Ssam allrates.rs_rates[j] = r; 356116742Ssam allrates.rs_nrates++; 357116742Ssam } 358116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 359116742Ssam if (rate > maxrate) 360116742Ssam maxrate = rate; 361116742Ssam } 362116742Ssam } 363116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 364116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 365116742Ssam IEEE80211_MODE_AUTO); 366116742Ssam if (mword == 0) 367116742Ssam continue; 368116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 369116742Ssam ADD(ic, mword, 0); 370116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 371116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 372116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 373116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 374116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 375116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 376117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 377117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 378116742Ssam } 379116742Ssam ieee80211_media_status(ifp, &imr); 380116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 381116742Ssam 382116742Ssam if (maxrate) 383116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 384116742Ssam#undef ADD 385116742Ssam} 386116742Ssam 387138568Ssamvoid 388138568Ssamieee80211_announce(struct ieee80211com *ic) 389138568Ssam{ 390138568Ssam struct ifnet *ifp = ic->ic_ifp; 391138568Ssam int i, mode, rate, mword; 392138568Ssam struct ieee80211_rateset *rs; 393138568Ssam 394138568Ssam for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { 395138568Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 396138568Ssam continue; 397138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 398138568Ssam rs = &ic->ic_sup_rates[mode]; 399138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 400138568Ssam rate = rs->rs_rates[i]; 401138568Ssam mword = ieee80211_rate2media(ic, rate, mode); 402138568Ssam if (mword == 0) 403138568Ssam continue; 404138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 405138568Ssam (rate & IEEE80211_RATE_VAL) / 2, 406138568Ssam ((rate & 0x1) != 0 ? ".5" : "")); 407138568Ssam } 408138568Ssam printf("\n"); 409138568Ssam } 410138568Ssam} 411138568Ssam 412116742Ssamstatic int 413116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 414116742Ssam{ 415116742Ssam#define IEEERATE(_ic,_m,_i) \ 416116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 417116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 418116742Ssam for (i = 0; i < nrates; i++) 419116742Ssam if (IEEERATE(ic, mode, i) == rate) 420116742Ssam return i; 421116742Ssam return -1; 422116742Ssam#undef IEEERATE 423116742Ssam} 424116742Ssam 425116742Ssam/* 426138568Ssam * Find an instance by it's mac address. 427138568Ssam */ 428138568Ssamstruct ieee80211com * 429138568Ssamieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) 430138568Ssam{ 431138568Ssam struct ieee80211com *ic; 432138568Ssam 433138568Ssam /* XXX lock */ 434138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 435138568Ssam if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) 436138568Ssam return ic; 437138568Ssam return NULL; 438138568Ssam} 439138568Ssam 440138568Ssamstatic struct ieee80211com * 441138568Ssamieee80211_find_instance(struct ifnet *ifp) 442138568Ssam{ 443138568Ssam struct ieee80211com *ic; 444138568Ssam 445138568Ssam /* XXX lock */ 446138568Ssam /* XXX not right for multiple instances but works for now */ 447138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 448138568Ssam if (ic->ic_ifp == ifp) 449138568Ssam return ic; 450138568Ssam return NULL; 451138568Ssam} 452138568Ssam 453138568Ssam/* 454116742Ssam * Handle a media change request. 455116742Ssam */ 456116742Ssamint 457116742Ssamieee80211_media_change(struct ifnet *ifp) 458116742Ssam{ 459138568Ssam struct ieee80211com *ic; 460116742Ssam struct ifmedia_entry *ime; 461116742Ssam enum ieee80211_opmode newopmode; 462116742Ssam enum ieee80211_phymode newphymode; 463116742Ssam int i, j, newrate, error = 0; 464116742Ssam 465138568Ssam ic = ieee80211_find_instance(ifp); 466138568Ssam if (!ic) { 467138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 468138568Ssam return EINVAL; 469138568Ssam } 470116742Ssam ime = ic->ic_media.ifm_cur; 471116742Ssam /* 472116742Ssam * First, identify the phy mode. 473116742Ssam */ 474116742Ssam switch (IFM_MODE(ime->ifm_media)) { 475116742Ssam case IFM_IEEE80211_11A: 476116742Ssam newphymode = IEEE80211_MODE_11A; 477116742Ssam break; 478116742Ssam case IFM_IEEE80211_11B: 479116742Ssam newphymode = IEEE80211_MODE_11B; 480116742Ssam break; 481116742Ssam case IFM_IEEE80211_11G: 482116742Ssam newphymode = IEEE80211_MODE_11G; 483116742Ssam break; 484124543Sonoe case IFM_IEEE80211_FH: 485124543Sonoe newphymode = IEEE80211_MODE_FH; 486124543Sonoe break; 487116742Ssam case IFM_AUTO: 488116742Ssam newphymode = IEEE80211_MODE_AUTO; 489116742Ssam break; 490116742Ssam default: 491116742Ssam return EINVAL; 492116742Ssam } 493116742Ssam /* 494138568Ssam * Turbo mode is an ``option''. 495138568Ssam * XXX does not apply to AUTO 496116742Ssam */ 497116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 498138568Ssam if (newphymode == IEEE80211_MODE_11A) 499138568Ssam newphymode = IEEE80211_MODE_TURBO_A; 500138568Ssam else if (newphymode == IEEE80211_MODE_11G) 501138568Ssam newphymode = IEEE80211_MODE_TURBO_G; 502138568Ssam else 503116742Ssam return EINVAL; 504116742Ssam } 505116742Ssam /* 506116742Ssam * Validate requested mode is available. 507116742Ssam */ 508116742Ssam if ((ic->ic_modecaps & (1<<newphymode)) == 0) 509116742Ssam return EINVAL; 510116742Ssam 511116742Ssam /* 512116742Ssam * Next, the fixed/variable rate. 513116742Ssam */ 514116742Ssam i = -1; 515116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 516116742Ssam /* 517116742Ssam * Convert media subtype to rate. 518116742Ssam */ 519116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 520116742Ssam if (newrate == 0) 521116742Ssam return EINVAL; 522116742Ssam /* 523116742Ssam * Check the rate table for the specified/current phy. 524116742Ssam */ 525116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 526116742Ssam /* 527116742Ssam * In autoselect mode search for the rate. 528116742Ssam */ 529116742Ssam for (j = IEEE80211_MODE_11A; 530116742Ssam j < IEEE80211_MODE_MAX; j++) { 531116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 532116742Ssam continue; 533116742Ssam i = findrate(ic, j, newrate); 534116742Ssam if (i != -1) { 535116742Ssam /* lock mode too */ 536116742Ssam newphymode = j; 537116742Ssam break; 538116742Ssam } 539116742Ssam } 540116742Ssam } else { 541116742Ssam i = findrate(ic, newphymode, newrate); 542116742Ssam } 543116742Ssam if (i == -1) /* mode/rate mismatch */ 544116742Ssam return EINVAL; 545116742Ssam } 546116742Ssam /* NB: defer rate setting to later */ 547116742Ssam 548116742Ssam /* 549116742Ssam * Deduce new operating mode but don't install it just yet. 550116742Ssam */ 551116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 552116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 553116742Ssam newopmode = IEEE80211_M_AHDEMO; 554116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 555116742Ssam newopmode = IEEE80211_M_HOSTAP; 556116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 557116742Ssam newopmode = IEEE80211_M_IBSS; 558117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 559117817Ssam newopmode = IEEE80211_M_MONITOR; 560116742Ssam else 561116742Ssam newopmode = IEEE80211_M_STA; 562116742Ssam 563116742Ssam /* 564116742Ssam * Autoselect doesn't make sense when operating as an AP. 565116742Ssam * If no phy mode has been selected, pick one and lock it 566116742Ssam * down so rate tables can be used in forming beacon frames 567116742Ssam * and the like. 568116742Ssam */ 569116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 570116742Ssam newphymode == IEEE80211_MODE_AUTO) { 571116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 572116742Ssam if (ic->ic_modecaps & (1<<j)) { 573116742Ssam newphymode = j; 574116742Ssam break; 575116742Ssam } 576116742Ssam } 577116742Ssam 578116742Ssam /* 579116742Ssam * Handle phy mode change. 580116742Ssam */ 581116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 582116742Ssam error = ieee80211_setmode(ic, newphymode); 583116742Ssam if (error != 0) 584116742Ssam return error; 585116742Ssam error = ENETRESET; 586116742Ssam } 587116742Ssam 588116742Ssam /* 589116742Ssam * Committed to changes, install the rate setting. 590116742Ssam */ 591116742Ssam if (ic->ic_fixed_rate != i) { 592116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 593116742Ssam error = ENETRESET; 594116742Ssam } 595116742Ssam 596116742Ssam /* 597116742Ssam * Handle operating mode change. 598116742Ssam */ 599116742Ssam if (ic->ic_opmode != newopmode) { 600116742Ssam ic->ic_opmode = newopmode; 601116742Ssam switch (newopmode) { 602116742Ssam case IEEE80211_M_AHDEMO: 603116742Ssam case IEEE80211_M_HOSTAP: 604116742Ssam case IEEE80211_M_STA: 605117817Ssam case IEEE80211_M_MONITOR: 606116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 607116742Ssam break; 608116742Ssam case IEEE80211_M_IBSS: 609116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 610116742Ssam break; 611116742Ssam } 612138568Ssam /* 613138568Ssam * Yech, slot time may change depending on the 614138568Ssam * operating mode so reset it to be sure everything 615138568Ssam * is setup appropriately. 616138568Ssam */ 617138568Ssam ieee80211_reset_erp(ic); 618138568Ssam ieee80211_wme_initparams(ic); /* after opmode change */ 619116742Ssam error = ENETRESET; 620116742Ssam } 621116742Ssam#ifdef notdef 622116742Ssam if (error == 0) 623116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 624116742Ssam#endif 625116742Ssam return error; 626116742Ssam} 627116742Ssam 628116742Ssamvoid 629116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 630116742Ssam{ 631138568Ssam struct ieee80211com *ic; 632138568Ssam struct ieee80211_rateset *rs; 633116742Ssam 634138568Ssam ic = ieee80211_find_instance(ifp); 635138568Ssam if (!ic) { 636138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 637138568Ssam return; 638138568Ssam } 639116742Ssam imr->ifm_status = IFM_AVALID; 640116742Ssam imr->ifm_active = IFM_IEEE80211; 641138568Ssam if (ic->ic_state == IEEE80211_S_RUN) 642116742Ssam imr->ifm_status |= IFM_ACTIVE; 643138568Ssam /* 644138568Ssam * Calculate a current rate if possible. 645138568Ssam */ 646138568Ssam if (ic->ic_fixed_rate != -1) { 647138568Ssam /* 648138568Ssam * A fixed rate is set, report that. 649138568Ssam */ 650138568Ssam rs = &ic->ic_sup_rates[ic->ic_curmode]; 651138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 652138568Ssam rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); 653138568Ssam } else if (ic->ic_opmode == IEEE80211_M_STA) { 654138568Ssam /* 655138568Ssam * In station mode report the current transmit rate. 656138568Ssam */ 657138568Ssam rs = &ic->ic_bss->ni_rates; 658138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 659138568Ssam rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); 660128966Sandre } else 661138568Ssam imr->ifm_active |= IFM_AUTO; 662116742Ssam switch (ic->ic_opmode) { 663116742Ssam case IEEE80211_M_STA: 664116742Ssam break; 665116742Ssam case IEEE80211_M_IBSS: 666116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 667116742Ssam break; 668116742Ssam case IEEE80211_M_AHDEMO: 669116742Ssam /* should not come here */ 670116742Ssam break; 671116742Ssam case IEEE80211_M_HOSTAP: 672116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 673116742Ssam break; 674117817Ssam case IEEE80211_M_MONITOR: 675117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 676117817Ssam break; 677116742Ssam } 678116742Ssam switch (ic->ic_curmode) { 679116742Ssam case IEEE80211_MODE_11A: 680124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A; 681116742Ssam break; 682116742Ssam case IEEE80211_MODE_11B: 683124543Sonoe imr->ifm_active |= IFM_IEEE80211_11B; 684116742Ssam break; 685116742Ssam case IEEE80211_MODE_11G: 686124543Sonoe imr->ifm_active |= IFM_IEEE80211_11G; 687116742Ssam break; 688124543Sonoe case IEEE80211_MODE_FH: 689124543Sonoe imr->ifm_active |= IFM_IEEE80211_FH; 690124543Sonoe break; 691138568Ssam case IEEE80211_MODE_TURBO_A: 692124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A 693116742Ssam | IFM_IEEE80211_TURBO; 694116742Ssam break; 695138568Ssam case IEEE80211_MODE_TURBO_G: 696138568Ssam imr->ifm_active |= IFM_IEEE80211_11G 697138568Ssam | IFM_IEEE80211_TURBO; 698138568Ssam break; 699116742Ssam } 700116742Ssam} 701116742Ssam 702116742Ssamvoid 703138568Ssamieee80211_watchdog(struct ieee80211com *ic) 704116742Ssam{ 705138568Ssam struct ieee80211_node_table *nt; 706138568Ssam int need_inact_timer = 0; 707116742Ssam 708138568Ssam if (ic->ic_state != IEEE80211_S_INIT) { 709138568Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 710138568Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 711138568Ssam nt = &ic->ic_scan; 712138568Ssam if (nt->nt_inact_timer) { 713138568Ssam if (--nt->nt_inact_timer == 0) 714138568Ssam nt->nt_timeout(nt); 715138568Ssam need_inact_timer += nt->nt_inact_timer; 716138568Ssam } 717138568Ssam nt = ic->ic_sta; 718138568Ssam if (nt != NULL && nt->nt_inact_timer) { 719138568Ssam if (--nt->nt_inact_timer == 0) 720138568Ssam nt->nt_timeout(nt); 721138568Ssam need_inact_timer += nt->nt_inact_timer; 722138568Ssam } 723116742Ssam } 724138568Ssam if (ic->ic_mgt_timer != 0 || need_inact_timer) 725138568Ssam ic->ic_ifp->if_timer = 1; 726116742Ssam} 727116742Ssam 728116742Ssam/* 729116742Ssam * Set the current phy mode and recalculate the active channel 730116742Ssam * set based on the available channels for this mode. Also 731116742Ssam * select a new default/current channel if the current one is 732116742Ssam * inappropriate for this mode. 733116742Ssam */ 734116742Ssamint 735116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 736116742Ssam{ 737116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 738116742Ssam static const u_int chanflags[] = { 739116742Ssam 0, /* IEEE80211_MODE_AUTO */ 740116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 741116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 742116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 743124543Sonoe IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 744138568Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ 745138568Ssam IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 746116742Ssam }; 747116742Ssam struct ieee80211_channel *c; 748116742Ssam u_int modeflags; 749116742Ssam int i; 750116742Ssam 751116742Ssam /* validate new mode */ 752116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) { 753138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 754138568Ssam "%s: mode %u not supported (caps 0x%x)\n", 755138568Ssam __func__, mode, ic->ic_modecaps); 756116742Ssam return EINVAL; 757116742Ssam } 758116742Ssam 759116742Ssam /* 760116742Ssam * Verify at least one channel is present in the available 761116742Ssam * channel list before committing to the new mode. 762116742Ssam */ 763127760Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); 764116742Ssam modeflags = chanflags[mode]; 765116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 766116742Ssam c = &ic->ic_channels[i]; 767116742Ssam if (mode == IEEE80211_MODE_AUTO) { 768116742Ssam /* ignore turbo channels for autoselect */ 769116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 770116742Ssam break; 771116742Ssam } else { 772116742Ssam if ((c->ic_flags & modeflags) == modeflags) 773116742Ssam break; 774116742Ssam } 775116742Ssam } 776116742Ssam if (i > IEEE80211_CHAN_MAX) { 777138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 778138568Ssam "%s: no channels found for mode %u\n", __func__, mode); 779116742Ssam return EINVAL; 780116742Ssam } 781116742Ssam 782116742Ssam /* 783116742Ssam * Calculate the active channel set. 784116742Ssam */ 785116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 786116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 787116742Ssam c = &ic->ic_channels[i]; 788116742Ssam if (mode == IEEE80211_MODE_AUTO) { 789116742Ssam /* take anything but pure turbo channels */ 790116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 791116742Ssam setbit(ic->ic_chan_active, i); 792116742Ssam } else { 793116742Ssam if ((c->ic_flags & modeflags) == modeflags) 794116742Ssam setbit(ic->ic_chan_active, i); 795116742Ssam } 796116742Ssam } 797116742Ssam /* 798116742Ssam * If no current/default channel is setup or the current 799116742Ssam * channel is wrong for the mode then pick the first 800116742Ssam * available channel from the active list. This is likely 801116742Ssam * not the right one. 802116742Ssam */ 803116742Ssam if (ic->ic_ibss_chan == NULL || 804116742Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 805116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 806116742Ssam if (isset(ic->ic_chan_active, i)) { 807116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 808116742Ssam break; 809116742Ssam } 810138568Ssam KASSERT(ic->ic_ibss_chan != NULL && 811138568Ssam isset(ic->ic_chan_active, 812138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), 813138568Ssam ("Bad IBSS channel %u", 814138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); 815116742Ssam } 816138568Ssam /* 817138568Ssam * If the desired channel is set but no longer valid then reset it. 818138568Ssam */ 819138568Ssam if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 820138568Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) 821138568Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 822116742Ssam 823116742Ssam /* 824138568Ssam * Do mode-specific rate setup. 825116742Ssam */ 826116742Ssam if (mode == IEEE80211_MODE_11G) { 827138568Ssam /* 828138568Ssam * Use a mixed 11b/11g rate set. 829138568Ssam */ 830116742Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 831116742Ssam IEEE80211_MODE_11G); 832138568Ssam } else if (mode == IEEE80211_MODE_11B) { 833138568Ssam /* 834138568Ssam * Force pure 11b rate set. 835138568Ssam */ 836138568Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 837138568Ssam IEEE80211_MODE_11B); 838116742Ssam } 839138568Ssam /* 840138568Ssam * Setup an initial rate set according to the 841138568Ssam * current/default channel selected above. This 842138568Ssam * will be changed when scanning but must exist 843138568Ssam * now so driver have a consistent state of ic_ibss_chan. 844138568Ssam */ 845138568Ssam if (ic->ic_bss) /* NB: can be called before lateattach */ 846138568Ssam ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; 847116742Ssam 848116742Ssam ic->ic_curmode = mode; 849138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 850138568Ssam ieee80211_wme_initparams(ic); /* reset WME stat */ 851138568Ssam 852116742Ssam return 0; 853116742Ssam#undef N 854116742Ssam} 855116742Ssam 856116742Ssam/* 857116742Ssam * Return the phy mode for with the specified channel so the 858138568Ssam * caller can select a rate set. This is problematic for channels 859138568Ssam * where multiple operating modes are possible (e.g. 11g+11b). 860138568Ssam * In those cases we defer to the current operating mode when set. 861116742Ssam */ 862116742Ssamenum ieee80211_phymode 863116742Ssamieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) 864116742Ssam{ 865138568Ssam if (IEEE80211_IS_CHAN_5GHZ(chan)) { 866138568Ssam /* 867138568Ssam * This assumes all 11a turbo channels are also 868138568Ssam * usable withut turbo, which is currently true. 869138568Ssam */ 870138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_A) 871138568Ssam return IEEE80211_MODE_TURBO_A; 872116742Ssam return IEEE80211_MODE_11A; 873138568Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) 874124543Sonoe return IEEE80211_MODE_FH; 875138568Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { 876138568Ssam /* 877138568Ssam * This assumes all 11g channels are also usable 878138568Ssam * for 11b, which is currently true. 879138568Ssam */ 880138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) 881138568Ssam return IEEE80211_MODE_TURBO_G; 882138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11B) 883138568Ssam return IEEE80211_MODE_11B; 884116742Ssam return IEEE80211_MODE_11G; 885138568Ssam } else 886116742Ssam return IEEE80211_MODE_11B; 887116742Ssam} 888116742Ssam 889116742Ssam/* 890116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 891116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 892116742Ssam */ 893116742Ssamint 894116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 895116742Ssam{ 896116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 897116742Ssam static const struct { 898116742Ssam u_int m; /* rate + mode */ 899116742Ssam u_int r; /* if_media rate */ 900116742Ssam } rates[] = { 901124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 902124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 903124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 904124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 905124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 906124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 907124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 908124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 909124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 910124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 911124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 912124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 913124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 914124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 915124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 916124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 917124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 918124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 919124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 920124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 921124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 922124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 923124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 924124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 925124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 926124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 927124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 928116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 929116742Ssam }; 930116742Ssam u_int mask, i; 931116742Ssam 932116742Ssam mask = rate & IEEE80211_RATE_VAL; 933116742Ssam switch (mode) { 934116742Ssam case IEEE80211_MODE_11A: 935138568Ssam case IEEE80211_MODE_TURBO_A: 936124543Sonoe mask |= IFM_IEEE80211_11A; 937116742Ssam break; 938116742Ssam case IEEE80211_MODE_11B: 939124543Sonoe mask |= IFM_IEEE80211_11B; 940116742Ssam break; 941124543Sonoe case IEEE80211_MODE_FH: 942124543Sonoe mask |= IFM_IEEE80211_FH; 943124543Sonoe break; 944116742Ssam case IEEE80211_MODE_AUTO: 945116742Ssam /* NB: ic may be NULL for some drivers */ 946116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 947124543Sonoe mask |= IFM_IEEE80211_FH; 948124543Sonoe break; 949116742Ssam } 950116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 951116742Ssam /* fall thru... */ 952116742Ssam case IEEE80211_MODE_11G: 953138568Ssam case IEEE80211_MODE_TURBO_G: 954124543Sonoe mask |= IFM_IEEE80211_11G; 955116742Ssam break; 956116742Ssam } 957116742Ssam for (i = 0; i < N(rates); i++) 958116742Ssam if (rates[i].m == mask) 959116742Ssam return rates[i].r; 960116742Ssam return IFM_AUTO; 961116742Ssam#undef N 962116742Ssam} 963116742Ssam 964116742Ssamint 965116742Ssamieee80211_media2rate(int mword) 966116742Ssam{ 967116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 968116742Ssam static const int ieeerates[] = { 969116742Ssam -1, /* IFM_AUTO */ 970116742Ssam 0, /* IFM_MANUAL */ 971116742Ssam 0, /* IFM_NONE */ 972116742Ssam 2, /* IFM_IEEE80211_FH1 */ 973116742Ssam 4, /* IFM_IEEE80211_FH2 */ 974116742Ssam 2, /* IFM_IEEE80211_DS1 */ 975116742Ssam 4, /* IFM_IEEE80211_DS2 */ 976116742Ssam 11, /* IFM_IEEE80211_DS5 */ 977116742Ssam 22, /* IFM_IEEE80211_DS11 */ 978116742Ssam 44, /* IFM_IEEE80211_DS22 */ 979116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 980116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 981116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 982116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 983116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 984116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 985116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 986116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 987116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 988116742Ssam }; 989116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 990116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 991116742Ssam#undef N 992116742Ssam} 993