ieee80211.c revision 153421
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 153421 2005-12-14 19:32:53Z 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 187153421Ssam if (ic->ic_caps & IEEE80211_C_BURST) 188153421Ssam ic->ic_flags |= IEEE80211_F_BURST; 189116742Ssam (void) ieee80211_setmode(ic, ic->ic_curmode); 190116742Ssam 191148843Ssam if (ic->ic_bintval == 0) 192148843Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 193148843Ssam ic->ic_bmisstimeout = 7*ic->ic_bintval; /* default 7 beacons */ 194138568Ssam ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; 195138568Ssam IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); 196116742Ssam 197148843Ssam if (ic->ic_lintval == 0) 198148843Ssam ic->ic_lintval = ic->ic_bintval; 199138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 200138568Ssam 201138568Ssam ieee80211_node_attach(ic); 202138568Ssam ieee80211_proto_attach(ic); 203138568Ssam 204138568Ssam ieee80211_add_vap(ic); 205138568Ssam 206138568Ssam ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ 207140915Ssam 208140915Ssam /* 209140915Ssam * Install a default reset method for the ioctl support. 210140915Ssam * The driver is expected to fill this in before calling us. 211140915Ssam */ 212140915Ssam if (ic->ic_reset == NULL) 213140915Ssam ic->ic_reset = ieee80211_default_reset; 214116742Ssam} 215116742Ssam 216116742Ssamvoid 217138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 218116742Ssam{ 219138568Ssam struct ifnet *ifp = ic->ic_ifp; 220116742Ssam 221138568Ssam ieee80211_remove_vap(ic); 222138568Ssam 223138568Ssam ieee80211_sysctl_detach(ic); 224138568Ssam ieee80211_proto_detach(ic); 225138568Ssam ieee80211_crypto_detach(ic); 226138568Ssam ieee80211_node_detach(ic); 227116742Ssam ifmedia_removeall(&ic->ic_media); 228138568Ssam 229138568Ssam IEEE80211_BEACON_LOCK_DESTROY(ic); 230138568Ssam 231116742Ssam bpfdetach(ifp); 232116742Ssam ether_ifdetach(ifp); 233116742Ssam} 234116742Ssam 235116742Ssam/* 236116742Ssam * Convert MHz frequency to IEEE channel number. 237116742Ssam */ 238152450Ssamint 239116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 240116742Ssam{ 241152450Ssam#define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990) 242116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 243116742Ssam if (freq == 2484) 244116742Ssam return 14; 245116742Ssam if (freq < 2484) 246152450Ssam return ((int) freq - 2407) / 5; 247116742Ssam else 248116742Ssam return 15 + ((freq - 2512) / 20); 249116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 250152450Ssam if (IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) 251152450Ssam return ((freq * 10) + 252152450Ssam (((freq % 5) == 2) ? 5 : 0) - 49400) / 5; 253152450Ssam if (freq <= 5000) 254152450Ssam return (freq - 4000) / 5; 255152450Ssam else 256152450Ssam return (freq - 5000) / 5; 257116742Ssam } else { /* either, guess */ 258116742Ssam if (freq == 2484) 259116742Ssam return 14; 260116742Ssam if (freq < 2484) 261152450Ssam return ((int) freq - 2407) / 5; 262152450Ssam if (freq < 5000) { 263152450Ssam if (IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) 264152450Ssam return ((freq * 10) + 265152450Ssam (((freq % 5) == 2) ? 5 : 0) - 49400)/5; 266152450Ssam else if (freq > 4900) 267152450Ssam return (freq - 4000) / 5; 268152450Ssam else 269152450Ssam return 15 + ((freq - 2512) / 20); 270152450Ssam } 271116742Ssam return (freq - 5000) / 5; 272116742Ssam } 273152450Ssam#undef IS_CHAN_IN_PUBLIC_SAFETY_BAND 274116742Ssam} 275116742Ssam 276116742Ssam/* 277116742Ssam * Convert channel to IEEE channel number. 278116742Ssam */ 279152450Ssamint 280116742Ssamieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) 281116742Ssam{ 282116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 283116742Ssam return c - ic->ic_channels; 284116742Ssam else if (c == IEEE80211_CHAN_ANYC) 285116742Ssam return IEEE80211_CHAN_ANY; 286117039Ssam else if (c != NULL) { 287138568Ssam if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", 288116742Ssam c->ic_freq, c->ic_flags); 289116742Ssam return 0; /* XXX */ 290117039Ssam } else { 291138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 292117039Ssam return 0; /* XXX */ 293116742Ssam } 294116742Ssam} 295116742Ssam 296116742Ssam/* 297116742Ssam * Convert IEEE channel number to MHz frequency. 298116742Ssam */ 299116742Ssamu_int 300116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 301116742Ssam{ 302116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 303116742Ssam if (chan == 14) 304116742Ssam return 2484; 305116742Ssam if (chan < 14) 306116742Ssam return 2407 + chan*5; 307116742Ssam else 308116742Ssam return 2512 + ((chan-15)*20); 309116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 310116742Ssam return 5000 + (chan*5); 311116742Ssam } else { /* either, guess */ 312116742Ssam if (chan == 14) 313116742Ssam return 2484; 314116742Ssam if (chan < 14) /* 0-13 */ 315116742Ssam return 2407 + chan*5; 316116742Ssam if (chan < 27) /* 15-26 */ 317116742Ssam return 2512 + ((chan-15)*20); 318116742Ssam return 5000 + (chan*5); 319116742Ssam } 320116742Ssam} 321116742Ssam 322116742Ssam/* 323116742Ssam * Setup the media data structures according to the channel and 324116742Ssam * rate tables. This must be called by the driver after 325116742Ssam * ieee80211_attach and before most anything else. 326116742Ssam */ 327116742Ssamvoid 328138568Ssamieee80211_media_init(struct ieee80211com *ic, 329116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 330116742Ssam{ 331116742Ssam#define ADD(_ic, _s, _o) \ 332116742Ssam ifmedia_add(&(_ic)->ic_media, \ 333116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 334138568Ssam struct ifnet *ifp = ic->ic_ifp; 335116742Ssam struct ifmediareq imr; 336116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 337116742Ssam struct ieee80211_rateset *rs; 338116742Ssam struct ieee80211_rateset allrates; 339116742Ssam 340116742Ssam /* 341118887Ssam * Do late attach work that must wait for any subclass 342118887Ssam * (i.e. driver) work such as overriding methods. 343118887Ssam */ 344138568Ssam ieee80211_node_lateattach(ic); 345118887Ssam 346118887Ssam /* 347116742Ssam * Fill in media characteristics. 348116742Ssam */ 349116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 350116742Ssam maxrate = 0; 351116742Ssam memset(&allrates, 0, sizeof(allrates)); 352116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 353116742Ssam static const u_int mopts[] = { 354116742Ssam IFM_AUTO, 355124543Sonoe IFM_IEEE80211_11A, 356124543Sonoe IFM_IEEE80211_11B, 357124543Sonoe IFM_IEEE80211_11G, 358124543Sonoe IFM_IEEE80211_FH, 359124543Sonoe IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 360138568Ssam IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, 361116742Ssam }; 362116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 363116742Ssam continue; 364116742Ssam mopt = mopts[mode]; 365116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 366116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 367116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 368116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 369116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 370116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 371116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 372117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 373117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 374116742Ssam if (mode == IEEE80211_MODE_AUTO) 375116742Ssam continue; 376116742Ssam rs = &ic->ic_sup_rates[mode]; 377116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 378116742Ssam rate = rs->rs_rates[i]; 379116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 380116742Ssam if (mword == 0) 381116742Ssam continue; 382116742Ssam ADD(ic, mword, mopt); 383116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 384116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 385116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 386116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 387116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 388116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 389117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 390117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 391116742Ssam /* 392116742Ssam * Add rate to the collection of all rates. 393116742Ssam */ 394116742Ssam r = rate & IEEE80211_RATE_VAL; 395116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 396116742Ssam if (allrates.rs_rates[j] == r) 397116742Ssam break; 398116742Ssam if (j == allrates.rs_nrates) { 399116742Ssam /* unique, add to the set */ 400116742Ssam allrates.rs_rates[j] = r; 401116742Ssam allrates.rs_nrates++; 402116742Ssam } 403116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 404116742Ssam if (rate > maxrate) 405116742Ssam maxrate = rate; 406116742Ssam } 407116742Ssam } 408116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 409116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 410116742Ssam IEEE80211_MODE_AUTO); 411116742Ssam if (mword == 0) 412116742Ssam continue; 413116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 414116742Ssam ADD(ic, mword, 0); 415116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 416116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 417116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 418116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 419116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 420116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 421117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 422117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 423116742Ssam } 424116742Ssam ieee80211_media_status(ifp, &imr); 425116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 426116742Ssam 427116742Ssam if (maxrate) 428116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 429116742Ssam#undef ADD 430116742Ssam} 431116742Ssam 432138568Ssamvoid 433138568Ssamieee80211_announce(struct ieee80211com *ic) 434138568Ssam{ 435138568Ssam struct ifnet *ifp = ic->ic_ifp; 436138568Ssam int i, mode, rate, mword; 437138568Ssam struct ieee80211_rateset *rs; 438138568Ssam 439138568Ssam for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { 440138568Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 441138568Ssam continue; 442138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 443138568Ssam rs = &ic->ic_sup_rates[mode]; 444138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 445138568Ssam rate = rs->rs_rates[i]; 446138568Ssam mword = ieee80211_rate2media(ic, rate, mode); 447138568Ssam if (mword == 0) 448138568Ssam continue; 449138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 450138568Ssam (rate & IEEE80211_RATE_VAL) / 2, 451138568Ssam ((rate & 0x1) != 0 ? ".5" : "")); 452138568Ssam } 453138568Ssam printf("\n"); 454138568Ssam } 455138568Ssam} 456138568Ssam 457116742Ssamstatic int 458116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 459116742Ssam{ 460116742Ssam#define IEEERATE(_ic,_m,_i) \ 461116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 462116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 463116742Ssam for (i = 0; i < nrates; i++) 464116742Ssam if (IEEERATE(ic, mode, i) == rate) 465116742Ssam return i; 466116742Ssam return -1; 467116742Ssam#undef IEEERATE 468116742Ssam} 469116742Ssam 470116742Ssam/* 471138568Ssam * Find an instance by it's mac address. 472138568Ssam */ 473138568Ssamstruct ieee80211com * 474138568Ssamieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) 475138568Ssam{ 476138568Ssam struct ieee80211com *ic; 477138568Ssam 478138568Ssam /* XXX lock */ 479138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 480138568Ssam if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) 481138568Ssam return ic; 482138568Ssam return NULL; 483138568Ssam} 484138568Ssam 485138568Ssamstatic struct ieee80211com * 486138568Ssamieee80211_find_instance(struct ifnet *ifp) 487138568Ssam{ 488138568Ssam struct ieee80211com *ic; 489138568Ssam 490138568Ssam /* XXX lock */ 491138568Ssam /* XXX not right for multiple instances but works for now */ 492138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 493138568Ssam if (ic->ic_ifp == ifp) 494138568Ssam return ic; 495138568Ssam return NULL; 496138568Ssam} 497138568Ssam 498138568Ssam/* 499116742Ssam * Handle a media change request. 500116742Ssam */ 501116742Ssamint 502116742Ssamieee80211_media_change(struct ifnet *ifp) 503116742Ssam{ 504138568Ssam struct ieee80211com *ic; 505116742Ssam struct ifmedia_entry *ime; 506116742Ssam enum ieee80211_opmode newopmode; 507116742Ssam enum ieee80211_phymode newphymode; 508116742Ssam int i, j, newrate, error = 0; 509116742Ssam 510138568Ssam ic = ieee80211_find_instance(ifp); 511138568Ssam if (!ic) { 512138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 513138568Ssam return EINVAL; 514138568Ssam } 515116742Ssam ime = ic->ic_media.ifm_cur; 516116742Ssam /* 517116742Ssam * First, identify the phy mode. 518116742Ssam */ 519116742Ssam switch (IFM_MODE(ime->ifm_media)) { 520116742Ssam case IFM_IEEE80211_11A: 521116742Ssam newphymode = IEEE80211_MODE_11A; 522116742Ssam break; 523116742Ssam case IFM_IEEE80211_11B: 524116742Ssam newphymode = IEEE80211_MODE_11B; 525116742Ssam break; 526116742Ssam case IFM_IEEE80211_11G: 527116742Ssam newphymode = IEEE80211_MODE_11G; 528116742Ssam break; 529124543Sonoe case IFM_IEEE80211_FH: 530124543Sonoe newphymode = IEEE80211_MODE_FH; 531124543Sonoe break; 532116742Ssam case IFM_AUTO: 533116742Ssam newphymode = IEEE80211_MODE_AUTO; 534116742Ssam break; 535116742Ssam default: 536116742Ssam return EINVAL; 537116742Ssam } 538116742Ssam /* 539138568Ssam * Turbo mode is an ``option''. 540138568Ssam * XXX does not apply to AUTO 541116742Ssam */ 542116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 543138568Ssam if (newphymode == IEEE80211_MODE_11A) 544138568Ssam newphymode = IEEE80211_MODE_TURBO_A; 545138568Ssam else if (newphymode == IEEE80211_MODE_11G) 546138568Ssam newphymode = IEEE80211_MODE_TURBO_G; 547138568Ssam else 548116742Ssam return EINVAL; 549116742Ssam } 550116742Ssam /* 551116742Ssam * Validate requested mode is available. 552116742Ssam */ 553116742Ssam if ((ic->ic_modecaps & (1<<newphymode)) == 0) 554116742Ssam return EINVAL; 555116742Ssam 556116742Ssam /* 557116742Ssam * Next, the fixed/variable rate. 558116742Ssam */ 559116742Ssam i = -1; 560116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 561116742Ssam /* 562116742Ssam * Convert media subtype to rate. 563116742Ssam */ 564116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 565116742Ssam if (newrate == 0) 566116742Ssam return EINVAL; 567116742Ssam /* 568116742Ssam * Check the rate table for the specified/current phy. 569116742Ssam */ 570116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 571116742Ssam /* 572116742Ssam * In autoselect mode search for the rate. 573116742Ssam */ 574116742Ssam for (j = IEEE80211_MODE_11A; 575116742Ssam j < IEEE80211_MODE_MAX; j++) { 576116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 577116742Ssam continue; 578116742Ssam i = findrate(ic, j, newrate); 579116742Ssam if (i != -1) { 580116742Ssam /* lock mode too */ 581116742Ssam newphymode = j; 582116742Ssam break; 583116742Ssam } 584116742Ssam } 585116742Ssam } else { 586116742Ssam i = findrate(ic, newphymode, newrate); 587116742Ssam } 588116742Ssam if (i == -1) /* mode/rate mismatch */ 589116742Ssam return EINVAL; 590116742Ssam } 591116742Ssam /* NB: defer rate setting to later */ 592116742Ssam 593116742Ssam /* 594116742Ssam * Deduce new operating mode but don't install it just yet. 595116742Ssam */ 596116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 597116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 598116742Ssam newopmode = IEEE80211_M_AHDEMO; 599116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 600116742Ssam newopmode = IEEE80211_M_HOSTAP; 601116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 602116742Ssam newopmode = IEEE80211_M_IBSS; 603117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 604117817Ssam newopmode = IEEE80211_M_MONITOR; 605116742Ssam else 606116742Ssam newopmode = IEEE80211_M_STA; 607116742Ssam 608116742Ssam /* 609116742Ssam * Autoselect doesn't make sense when operating as an AP. 610116742Ssam * If no phy mode has been selected, pick one and lock it 611116742Ssam * down so rate tables can be used in forming beacon frames 612116742Ssam * and the like. 613116742Ssam */ 614116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 615116742Ssam newphymode == IEEE80211_MODE_AUTO) { 616116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 617116742Ssam if (ic->ic_modecaps & (1<<j)) { 618116742Ssam newphymode = j; 619116742Ssam break; 620116742Ssam } 621116742Ssam } 622116742Ssam 623116742Ssam /* 624116742Ssam * Handle phy mode change. 625116742Ssam */ 626116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 627116742Ssam error = ieee80211_setmode(ic, newphymode); 628116742Ssam if (error != 0) 629116742Ssam return error; 630116742Ssam error = ENETRESET; 631116742Ssam } 632116742Ssam 633116742Ssam /* 634116742Ssam * Committed to changes, install the rate setting. 635116742Ssam */ 636116742Ssam if (ic->ic_fixed_rate != i) { 637116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 638116742Ssam error = ENETRESET; 639116742Ssam } 640116742Ssam 641116742Ssam /* 642116742Ssam * Handle operating mode change. 643116742Ssam */ 644116742Ssam if (ic->ic_opmode != newopmode) { 645116742Ssam ic->ic_opmode = newopmode; 646116742Ssam switch (newopmode) { 647116742Ssam case IEEE80211_M_AHDEMO: 648116742Ssam case IEEE80211_M_HOSTAP: 649116742Ssam case IEEE80211_M_STA: 650117817Ssam case IEEE80211_M_MONITOR: 651116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 652116742Ssam break; 653116742Ssam case IEEE80211_M_IBSS: 654116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 655116742Ssam break; 656116742Ssam } 657138568Ssam /* 658138568Ssam * Yech, slot time may change depending on the 659138568Ssam * operating mode so reset it to be sure everything 660138568Ssam * is setup appropriately. 661138568Ssam */ 662138568Ssam ieee80211_reset_erp(ic); 663138568Ssam ieee80211_wme_initparams(ic); /* after opmode change */ 664116742Ssam error = ENETRESET; 665116742Ssam } 666116742Ssam#ifdef notdef 667116742Ssam if (error == 0) 668116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 669116742Ssam#endif 670116742Ssam return error; 671116742Ssam} 672116742Ssam 673116742Ssamvoid 674116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 675116742Ssam{ 676138568Ssam struct ieee80211com *ic; 677138568Ssam struct ieee80211_rateset *rs; 678116742Ssam 679138568Ssam ic = ieee80211_find_instance(ifp); 680138568Ssam if (!ic) { 681138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 682138568Ssam return; 683138568Ssam } 684116742Ssam imr->ifm_status = IFM_AVALID; 685116742Ssam imr->ifm_active = IFM_IEEE80211; 686138568Ssam if (ic->ic_state == IEEE80211_S_RUN) 687116742Ssam imr->ifm_status |= IFM_ACTIVE; 688138568Ssam /* 689138568Ssam * Calculate a current rate if possible. 690138568Ssam */ 691148290Ssam if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 692138568Ssam /* 693138568Ssam * A fixed rate is set, report that. 694138568Ssam */ 695138568Ssam rs = &ic->ic_sup_rates[ic->ic_curmode]; 696138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 697138568Ssam rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); 698138568Ssam } else if (ic->ic_opmode == IEEE80211_M_STA) { 699138568Ssam /* 700138568Ssam * In station mode report the current transmit rate. 701138568Ssam */ 702138568Ssam rs = &ic->ic_bss->ni_rates; 703138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 704138568Ssam rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); 705128966Sandre } else 706138568Ssam imr->ifm_active |= IFM_AUTO; 707116742Ssam switch (ic->ic_opmode) { 708116742Ssam case IEEE80211_M_STA: 709116742Ssam break; 710116742Ssam case IEEE80211_M_IBSS: 711116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 712116742Ssam break; 713116742Ssam case IEEE80211_M_AHDEMO: 714116742Ssam /* should not come here */ 715116742Ssam break; 716116742Ssam case IEEE80211_M_HOSTAP: 717116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 718116742Ssam break; 719117817Ssam case IEEE80211_M_MONITOR: 720117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 721117817Ssam break; 722116742Ssam } 723116742Ssam switch (ic->ic_curmode) { 724116742Ssam case IEEE80211_MODE_11A: 725124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A; 726116742Ssam break; 727116742Ssam case IEEE80211_MODE_11B: 728124543Sonoe imr->ifm_active |= IFM_IEEE80211_11B; 729116742Ssam break; 730116742Ssam case IEEE80211_MODE_11G: 731124543Sonoe imr->ifm_active |= IFM_IEEE80211_11G; 732116742Ssam break; 733124543Sonoe case IEEE80211_MODE_FH: 734124543Sonoe imr->ifm_active |= IFM_IEEE80211_FH; 735124543Sonoe break; 736138568Ssam case IEEE80211_MODE_TURBO_A: 737124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A 738116742Ssam | IFM_IEEE80211_TURBO; 739116742Ssam break; 740138568Ssam case IEEE80211_MODE_TURBO_G: 741138568Ssam imr->ifm_active |= IFM_IEEE80211_11G 742138568Ssam | IFM_IEEE80211_TURBO; 743138568Ssam break; 744116742Ssam } 745116742Ssam} 746116742Ssam 747116742Ssamvoid 748138568Ssamieee80211_watchdog(struct ieee80211com *ic) 749116742Ssam{ 750138568Ssam struct ieee80211_node_table *nt; 751138568Ssam int need_inact_timer = 0; 752116742Ssam 753138568Ssam if (ic->ic_state != IEEE80211_S_INIT) { 754138568Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 755138568Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 756138568Ssam nt = &ic->ic_scan; 757138568Ssam if (nt->nt_inact_timer) { 758138568Ssam if (--nt->nt_inact_timer == 0) 759138568Ssam nt->nt_timeout(nt); 760138568Ssam need_inact_timer += nt->nt_inact_timer; 761138568Ssam } 762140753Ssam nt = &ic->ic_sta; 763140753Ssam if (nt->nt_inact_timer) { 764138568Ssam if (--nt->nt_inact_timer == 0) 765138568Ssam nt->nt_timeout(nt); 766138568Ssam need_inact_timer += nt->nt_inact_timer; 767138568Ssam } 768116742Ssam } 769138568Ssam if (ic->ic_mgt_timer != 0 || need_inact_timer) 770138568Ssam ic->ic_ifp->if_timer = 1; 771116742Ssam} 772116742Ssam 773116742Ssam/* 774116742Ssam * Set the current phy mode and recalculate the active channel 775116742Ssam * set based on the available channels for this mode. Also 776116742Ssam * select a new default/current channel if the current one is 777116742Ssam * inappropriate for this mode. 778116742Ssam */ 779116742Ssamint 780116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 781116742Ssam{ 782116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 783116742Ssam static const u_int chanflags[] = { 784116742Ssam 0, /* IEEE80211_MODE_AUTO */ 785116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 786116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 787116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 788124543Sonoe IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 789138568Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ 790138568Ssam IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 791116742Ssam }; 792116742Ssam struct ieee80211_channel *c; 793116742Ssam u_int modeflags; 794116742Ssam int i; 795116742Ssam 796116742Ssam /* validate new mode */ 797116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) { 798138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 799138568Ssam "%s: mode %u not supported (caps 0x%x)\n", 800138568Ssam __func__, mode, ic->ic_modecaps); 801116742Ssam return EINVAL; 802116742Ssam } 803116742Ssam 804116742Ssam /* 805116742Ssam * Verify at least one channel is present in the available 806116742Ssam * channel list before committing to the new mode. 807116742Ssam */ 808127760Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); 809116742Ssam modeflags = chanflags[mode]; 810116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 811116742Ssam c = &ic->ic_channels[i]; 812153350Ssam if (c->ic_flags == 0) 813153350Ssam continue; 814116742Ssam if (mode == IEEE80211_MODE_AUTO) { 815116742Ssam /* ignore turbo channels for autoselect */ 816153350Ssam if ((c->ic_flags & IEEE80211_CHAN_TURBO) == 0) 817116742Ssam break; 818116742Ssam } else { 819116742Ssam if ((c->ic_flags & modeflags) == modeflags) 820116742Ssam break; 821116742Ssam } 822116742Ssam } 823116742Ssam if (i > IEEE80211_CHAN_MAX) { 824138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 825138568Ssam "%s: no channels found for mode %u\n", __func__, mode); 826116742Ssam return EINVAL; 827116742Ssam } 828116742Ssam 829116742Ssam /* 830116742Ssam * Calculate the active channel set. 831116742Ssam */ 832116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 833116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 834116742Ssam c = &ic->ic_channels[i]; 835153350Ssam if (c->ic_flags == 0) 836153350Ssam continue; 837116742Ssam if (mode == IEEE80211_MODE_AUTO) { 838116742Ssam /* take anything but pure turbo channels */ 839153350Ssam if ((c->ic_flags & IEEE80211_CHAN_TURBO) == 0) 840116742Ssam setbit(ic->ic_chan_active, i); 841116742Ssam } else { 842116742Ssam if ((c->ic_flags & modeflags) == modeflags) 843116742Ssam setbit(ic->ic_chan_active, i); 844116742Ssam } 845116742Ssam } 846116742Ssam /* 847116742Ssam * If no current/default channel is setup or the current 848116742Ssam * channel is wrong for the mode then pick the first 849116742Ssam * available channel from the active list. This is likely 850116742Ssam * not the right one. 851116742Ssam */ 852116742Ssam if (ic->ic_ibss_chan == NULL || 853116742Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 854116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 855116742Ssam if (isset(ic->ic_chan_active, i)) { 856116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 857116742Ssam break; 858116742Ssam } 859138568Ssam KASSERT(ic->ic_ibss_chan != NULL && 860138568Ssam isset(ic->ic_chan_active, 861138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), 862138568Ssam ("Bad IBSS channel %u", 863138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); 864116742Ssam } 865138568Ssam /* 866138568Ssam * If the desired channel is set but no longer valid then reset it. 867138568Ssam */ 868138568Ssam if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 869138568Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) 870138568Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 871116742Ssam 872116742Ssam /* 873138568Ssam * Do mode-specific rate setup. 874116742Ssam */ 875116742Ssam if (mode == IEEE80211_MODE_11G) { 876138568Ssam /* 877138568Ssam * Use a mixed 11b/11g rate set. 878138568Ssam */ 879116742Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 880116742Ssam IEEE80211_MODE_11G); 881138568Ssam } else if (mode == IEEE80211_MODE_11B) { 882138568Ssam /* 883138568Ssam * Force pure 11b rate set. 884138568Ssam */ 885138568Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 886138568Ssam IEEE80211_MODE_11B); 887116742Ssam } 888138568Ssam /* 889138568Ssam * Setup an initial rate set according to the 890138568Ssam * current/default channel selected above. This 891138568Ssam * will be changed when scanning but must exist 892138568Ssam * now so driver have a consistent state of ic_ibss_chan. 893138568Ssam */ 894138568Ssam if (ic->ic_bss) /* NB: can be called before lateattach */ 895138568Ssam ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; 896116742Ssam 897116742Ssam ic->ic_curmode = mode; 898138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 899138568Ssam ieee80211_wme_initparams(ic); /* reset WME stat */ 900138568Ssam 901116742Ssam return 0; 902116742Ssam#undef N 903116742Ssam} 904116742Ssam 905116742Ssam/* 906116742Ssam * Return the phy mode for with the specified channel so the 907138568Ssam * caller can select a rate set. This is problematic for channels 908138568Ssam * where multiple operating modes are possible (e.g. 11g+11b). 909138568Ssam * In those cases we defer to the current operating mode when set. 910116742Ssam */ 911116742Ssamenum ieee80211_phymode 912116742Ssamieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) 913116742Ssam{ 914153350Ssam if (IEEE80211_IS_CHAN_T(chan)) { 915153350Ssam return IEEE80211_MODE_TURBO_A; 916153350Ssam } else if (IEEE80211_IS_CHAN_5GHZ(chan)) { 917116742Ssam return IEEE80211_MODE_11A; 918138568Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) 919124543Sonoe return IEEE80211_MODE_FH; 920138568Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { 921138568Ssam /* 922138568Ssam * This assumes all 11g channels are also usable 923138568Ssam * for 11b, which is currently true. 924138568Ssam */ 925138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) 926138568Ssam return IEEE80211_MODE_TURBO_G; 927138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11B) 928138568Ssam return IEEE80211_MODE_11B; 929116742Ssam return IEEE80211_MODE_11G; 930138568Ssam } else 931116742Ssam return IEEE80211_MODE_11B; 932116742Ssam} 933116742Ssam 934116742Ssam/* 935116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 936116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 937116742Ssam */ 938116742Ssamint 939116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 940116742Ssam{ 941116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 942116742Ssam static const struct { 943116742Ssam u_int m; /* rate + mode */ 944116742Ssam u_int r; /* if_media rate */ 945116742Ssam } rates[] = { 946124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 947124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 948124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 949124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 950124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 951124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 952124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 953124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 954124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 955124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 956124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 957124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 958124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 959124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 960124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 961124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 962124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 963124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 964124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 965124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 966124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 967124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 968124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 969124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 970124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 971124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 972124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 973116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 974116742Ssam }; 975116742Ssam u_int mask, i; 976116742Ssam 977116742Ssam mask = rate & IEEE80211_RATE_VAL; 978116742Ssam switch (mode) { 979116742Ssam case IEEE80211_MODE_11A: 980138568Ssam case IEEE80211_MODE_TURBO_A: 981124543Sonoe mask |= IFM_IEEE80211_11A; 982116742Ssam break; 983116742Ssam case IEEE80211_MODE_11B: 984124543Sonoe mask |= IFM_IEEE80211_11B; 985116742Ssam break; 986124543Sonoe case IEEE80211_MODE_FH: 987124543Sonoe mask |= IFM_IEEE80211_FH; 988124543Sonoe break; 989116742Ssam case IEEE80211_MODE_AUTO: 990116742Ssam /* NB: ic may be NULL for some drivers */ 991116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 992124543Sonoe mask |= IFM_IEEE80211_FH; 993124543Sonoe break; 994116742Ssam } 995116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 996116742Ssam /* fall thru... */ 997116742Ssam case IEEE80211_MODE_11G: 998138568Ssam case IEEE80211_MODE_TURBO_G: 999124543Sonoe mask |= IFM_IEEE80211_11G; 1000116742Ssam break; 1001116742Ssam } 1002116742Ssam for (i = 0; i < N(rates); i++) 1003116742Ssam if (rates[i].m == mask) 1004116742Ssam return rates[i].r; 1005116742Ssam return IFM_AUTO; 1006116742Ssam#undef N 1007116742Ssam} 1008116742Ssam 1009116742Ssamint 1010116742Ssamieee80211_media2rate(int mword) 1011116742Ssam{ 1012116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1013116742Ssam static const int ieeerates[] = { 1014116742Ssam -1, /* IFM_AUTO */ 1015116742Ssam 0, /* IFM_MANUAL */ 1016116742Ssam 0, /* IFM_NONE */ 1017116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1018116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1019116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1020116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1021116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1022116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1023116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1024116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1025116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1026116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1027116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1028116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1029116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1030116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1031116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1032116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1033116742Ssam }; 1034116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1035116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1036116742Ssam#undef N 1037116742Ssam} 1038