ieee80211.c revision 170360
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3170360Ssam * Copyright (c) 2002-2007 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. 14116742Ssam * 15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25116742Ssam */ 26116742Ssam 27116742Ssam#include <sys/cdefs.h> 28116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211.c 170360 2007-06-06 04:56:04Z sam $"); 29116742Ssam 30116742Ssam/* 31116742Ssam * IEEE 802.11 generic handler 32116742Ssam */ 33116742Ssam 34116742Ssam#include <sys/param.h> 35116742Ssam#include <sys/systm.h> 36116742Ssam#include <sys/kernel.h> 37138568Ssam 38116742Ssam#include <sys/socket.h> 39116742Ssam 40116742Ssam#include <net/if.h> 41116742Ssam#include <net/if_media.h> 42116742Ssam#include <net/ethernet.h> 43116742Ssam 44116742Ssam#include <net80211/ieee80211_var.h> 45116742Ssam 46116742Ssam#include <net/bpf.h> 47116742Ssam 48139502Ssamconst char *ieee80211_phymode_name[] = { 49116742Ssam "auto", /* IEEE80211_MODE_AUTO */ 50116742Ssam "11a", /* IEEE80211_MODE_11A */ 51116742Ssam "11b", /* IEEE80211_MODE_11B */ 52116742Ssam "11g", /* IEEE80211_MODE_11G */ 53124543Sonoe "FH", /* IEEE80211_MODE_FH */ 54138568Ssam "turboA", /* IEEE80211_MODE_TURBO_A */ 55138568Ssam "turboG", /* IEEE80211_MODE_TURBO_G */ 56116742Ssam}; 57116742Ssam 58164645Ssam/* 59164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units). 60164645Ssam */ 61164645Ssam#define B(r) ((r) | IEEE80211_RATE_BASIC) 62164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a = 63164645Ssam { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; 64165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half = 65165569Ssam { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; 66165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter = 67165569Ssam { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; 68164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b = 69164645Ssam { 4, { B(2), B(4), B(11), B(22) } }; 70164645Ssam/* NB: OFDM rates are handled specially based on mode */ 71164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g = 72164645Ssam { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; 73164645Ssam#undef B 74164645Ssam 75138568Ssam/* list of all instances */ 76138568SsamSLIST_HEAD(ieee80211_list, ieee80211com); 77138568Ssamstatic struct ieee80211_list ieee80211_list = 78138568Ssam SLIST_HEAD_INITIALIZER(ieee80211_list); 79138568Ssamstatic u_int8_t ieee80211_vapmap[32]; /* enough for 256 */ 80138568Ssamstatic struct mtx ieee80211_vap_mtx; 81138568SsamMTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF); 82138568Ssam 83138568Ssamstatic void 84138568Ssamieee80211_add_vap(struct ieee80211com *ic) 85138568Ssam{ 86138568Ssam#define N(a) (sizeof(a)/sizeof(a[0])) 87138568Ssam int i; 88138568Ssam u_int8_t b; 89138568Ssam 90138568Ssam mtx_lock(&ieee80211_vap_mtx); 91138568Ssam ic->ic_vap = 0; 92138568Ssam for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++) 93138568Ssam ic->ic_vap += NBBY; 94138568Ssam if (i == N(ieee80211_vapmap)) 95138568Ssam panic("vap table full"); 96138568Ssam for (b = ieee80211_vapmap[i]; b & 1; b >>= 1) 97138568Ssam ic->ic_vap++; 98138568Ssam setbit(ieee80211_vapmap, ic->ic_vap); 99138568Ssam SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); 100138568Ssam mtx_unlock(&ieee80211_vap_mtx); 101138568Ssam#undef N 102138568Ssam} 103138568Ssam 104138568Ssamstatic void 105138568Ssamieee80211_remove_vap(struct ieee80211com *ic) 106138568Ssam{ 107138568Ssam mtx_lock(&ieee80211_vap_mtx); 108138568Ssam SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); 109138568Ssam KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY, 110138568Ssam ("invalid vap id %d", ic->ic_vap)); 111138568Ssam KASSERT(isset(ieee80211_vapmap, ic->ic_vap), 112138568Ssam ("vap id %d not allocated", ic->ic_vap)); 113138568Ssam clrbit(ieee80211_vapmap, ic->ic_vap); 114138568Ssam mtx_unlock(&ieee80211_vap_mtx); 115138568Ssam} 116138568Ssam 117140915Ssam/* 118140915Ssam * Default reset method for use with the ioctl support. This 119140915Ssam * method is invoked after any state change in the 802.11 120140915Ssam * layer that should be propagated to the hardware but not 121140915Ssam * require re-initialization of the 802.11 state machine (e.g 122140915Ssam * rescanning for an ap). We always return ENETRESET which 123140915Ssam * should cause the driver to re-initialize the device. Drivers 124140915Ssam * can override this method to implement more optimized support. 125140915Ssam */ 126140915Ssamstatic int 127140915Ssamieee80211_default_reset(struct ifnet *ifp) 128140915Ssam{ 129140915Ssam return ENETRESET; 130140915Ssam} 131140915Ssam 132165569Ssam/* 133165569Ssam * Fill in 802.11 available channel set, mark 134165569Ssam * all available channels as active, and pick 135165569Ssam * a default channel if not already specified. 136165569Ssam */ 137165569Ssamstatic void 138165569Ssamieee80211_chan_init(struct ieee80211com *ic) 139116742Ssam{ 140165569Ssam#define DEFAULTRATES(m, def) do { \ 141167468Ssam if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \ 142165574Ssam ic->ic_sup_rates[m] = def; \ 143165569Ssam} while (0) 144138568Ssam struct ifnet *ifp = ic->ic_ifp; 145116742Ssam struct ieee80211_channel *c; 146116742Ssam int i; 147116742Ssam 148116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 149167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); 150116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 151116742Ssam c = &ic->ic_channels[i]; 152116742Ssam if (c->ic_flags) { 153116742Ssam /* 154116742Ssam * Verify driver passed us valid data. 155116742Ssam */ 156116742Ssam if (i != ieee80211_chan2ieee(ic, c)) { 157116742Ssam if_printf(ifp, "bad channel ignored; " 158116742Ssam "freq %u flags %x number %u\n", 159116742Ssam c->ic_freq, c->ic_flags, i); 160116742Ssam c->ic_flags = 0; /* NB: remove */ 161116742Ssam continue; 162116742Ssam } 163116742Ssam setbit(ic->ic_chan_avail, i); 164116742Ssam /* 165116742Ssam * Identify mode capabilities. 166116742Ssam */ 167116742Ssam if (IEEE80211_IS_CHAN_A(c)) 168167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11A); 169116742Ssam if (IEEE80211_IS_CHAN_B(c)) 170167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11B); 171165574Ssam if (IEEE80211_IS_CHAN_ANYG(c)) 172167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11G); 173124543Sonoe if (IEEE80211_IS_CHAN_FHSS(c)) 174167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_FH); 175116742Ssam if (IEEE80211_IS_CHAN_T(c)) 176167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); 177138568Ssam if (IEEE80211_IS_CHAN_108G(c)) 178167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); 179148936Ssam if (ic->ic_curchan == NULL) { 180148936Ssam /* arbitrarily pick the first channel */ 181148936Ssam ic->ic_curchan = &ic->ic_channels[i]; 182148936Ssam } 183116742Ssam } 184116742Ssam } 185164645Ssam 186164645Ssam /* fillin well-known rate sets if driver has not specified */ 187165569Ssam DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); 188165569Ssam DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); 189165569Ssam DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); 190165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); 191165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); 192165569Ssam 193165569Ssam /* 194165569Ssam * Set auto mode to reset active channel state and any desired channel. 195165569Ssam */ 196165569Ssam (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); 197165569Ssam#undef DEFAULTRATES 198165569Ssam} 199165569Ssam 200165569Ssamvoid 201165569Ssamieee80211_ifattach(struct ieee80211com *ic) 202165569Ssam{ 203165569Ssam struct ifnet *ifp = ic->ic_ifp; 204165569Ssam 205165569Ssam ether_ifattach(ifp, ic->ic_myaddr); 206165569Ssam ifp->if_output = ieee80211_output; 207165569Ssam 208165569Ssam bpfattach2(ifp, DLT_IEEE802_11, 209165569Ssam sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); 210165569Ssam 211165569Ssam ieee80211_crypto_attach(ic); 212165569Ssam 213165569Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 214165569Ssam /* 215165569Ssam * Fill in 802.11 available channel set, mark all 216165569Ssam * available channels as active, and pick a default 217165569Ssam * channel if not already specified. 218165569Ssam */ 219165569Ssam ieee80211_chan_init(ic); 220139526Ssam#if 0 221138568Ssam /* 222138568Ssam * Enable WME by default if we're capable. 223138568Ssam */ 224138568Ssam if (ic->ic_caps & IEEE80211_C_WME) 225138568Ssam ic->ic_flags |= IEEE80211_F_WME; 226139526Ssam#endif 227153421Ssam if (ic->ic_caps & IEEE80211_C_BURST) 228153421Ssam ic->ic_flags |= IEEE80211_F_BURST; 229116742Ssam 230155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 231155688Ssam ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 232138568Ssam ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; 233138568Ssam IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); 234116742Ssam 235155688Ssam ic->ic_lintval = ic->ic_bintval; 236138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 237138568Ssam 238138568Ssam ieee80211_node_attach(ic); 239138568Ssam ieee80211_proto_attach(ic); 240138568Ssam 241138568Ssam ieee80211_add_vap(ic); 242138568Ssam 243138568Ssam ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ 244140915Ssam 245140915Ssam /* 246140915Ssam * Install a default reset method for the ioctl support. 247140915Ssam * The driver is expected to fill this in before calling us. 248140915Ssam */ 249140915Ssam if (ic->ic_reset == NULL) 250140915Ssam ic->ic_reset = ieee80211_default_reset; 251160690Ssam 252160690Ssam KASSERT(ifp->if_spare2 == NULL, ("oops, hosed")); 253160690Ssam ifp->if_spare2 = ic; /* XXX temp backpointer */ 254116742Ssam} 255116742Ssam 256116742Ssamvoid 257138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 258116742Ssam{ 259138568Ssam struct ifnet *ifp = ic->ic_ifp; 260116742Ssam 261138568Ssam ieee80211_remove_vap(ic); 262138568Ssam 263138568Ssam ieee80211_sysctl_detach(ic); 264166012Ssam /* NB: must be called before ieee80211_node_detach */ 265138568Ssam ieee80211_proto_detach(ic); 266138568Ssam ieee80211_crypto_detach(ic); 267138568Ssam ieee80211_node_detach(ic); 268116742Ssam ifmedia_removeall(&ic->ic_media); 269138568Ssam 270138568Ssam IEEE80211_BEACON_LOCK_DESTROY(ic); 271138568Ssam 272116742Ssam bpfdetach(ifp); 273116742Ssam ether_ifdetach(ifp); 274116742Ssam} 275116742Ssam 276166012Ssamstatic __inline int 277166012Ssammapgsm(u_int freq, u_int flags) 278166012Ssam{ 279166012Ssam freq *= 10; 280166012Ssam if (flags & IEEE80211_CHAN_QUARTER) 281166012Ssam freq += 5; 282166012Ssam else if (flags & IEEE80211_CHAN_HALF) 283166012Ssam freq += 10; 284166012Ssam else 285166012Ssam freq += 20; 286166012Ssam /* NB: there is no 907/20 wide but leave room */ 287166012Ssam return (freq - 906*10) / 5; 288166012Ssam} 289166012Ssam 290166012Ssamstatic __inline int 291166012Ssammappsb(u_int freq, u_int flags) 292166012Ssam{ 293166012Ssam return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 294166012Ssam} 295166012Ssam 296116742Ssam/* 297116742Ssam * Convert MHz frequency to IEEE channel number. 298116742Ssam */ 299152450Ssamint 300116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 301116742Ssam{ 302167430Ssam#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) 303166012Ssam if (flags & IEEE80211_CHAN_GSM) 304166012Ssam return mapgsm(freq, flags); 305116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 306116742Ssam if (freq == 2484) 307116742Ssam return 14; 308116742Ssam if (freq < 2484) 309152450Ssam return ((int) freq - 2407) / 5; 310116742Ssam else 311116742Ssam return 15 + ((freq - 2512) / 20); 312116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 313165569Ssam if (freq <= 5000) { 314167430Ssam if (IS_FREQ_IN_PSB(freq)) 315166012Ssam return mappsb(freq, flags); 316152450Ssam return (freq - 4000) / 5; 317165569Ssam } else 318152450Ssam return (freq - 5000) / 5; 319116742Ssam } else { /* either, guess */ 320116742Ssam if (freq == 2484) 321116742Ssam return 14; 322166012Ssam if (freq < 2484) { 323166012Ssam if (907 <= freq && freq <= 922) 324166012Ssam return mapgsm(freq, flags); 325152450Ssam return ((int) freq - 2407) / 5; 326166012Ssam } 327152450Ssam if (freq < 5000) { 328167430Ssam if (IS_FREQ_IN_PSB(freq)) 329166012Ssam return mappsb(freq, flags); 330165569Ssam else if (freq > 4900) 331152450Ssam return (freq - 4000) / 5; 332152450Ssam else 333152450Ssam return 15 + ((freq - 2512) / 20); 334152450Ssam } 335116742Ssam return (freq - 5000) / 5; 336116742Ssam } 337167430Ssam#undef IS_FREQ_IN_PSB 338116742Ssam} 339116742Ssam 340116742Ssam/* 341116742Ssam * Convert channel to IEEE channel number. 342116742Ssam */ 343152450Ssamint 344165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) 345116742Ssam{ 346116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 347116742Ssam return c - ic->ic_channels; 348116742Ssam else if (c == IEEE80211_CHAN_ANYC) 349116742Ssam return IEEE80211_CHAN_ANY; 350117039Ssam else if (c != NULL) { 351138568Ssam if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", 352116742Ssam c->ic_freq, c->ic_flags); 353116742Ssam return 0; /* XXX */ 354117039Ssam } else { 355138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 356117039Ssam return 0; /* XXX */ 357116742Ssam } 358116742Ssam} 359116742Ssam 360116742Ssam/* 361116742Ssam * Convert IEEE channel number to MHz frequency. 362116742Ssam */ 363116742Ssamu_int 364116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 365116742Ssam{ 366166012Ssam if (flags & IEEE80211_CHAN_GSM) 367166012Ssam return 907 + 5 * (chan / 10); 368116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 369116742Ssam if (chan == 14) 370116742Ssam return 2484; 371116742Ssam if (chan < 14) 372116742Ssam return 2407 + chan*5; 373116742Ssam else 374116742Ssam return 2512 + ((chan-15)*20); 375116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 376165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 377165569Ssam chan -= 37; 378165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 379165569Ssam } 380116742Ssam return 5000 + (chan*5); 381116742Ssam } else { /* either, guess */ 382166012Ssam /* XXX can't distinguish PSB+GSM channels */ 383116742Ssam if (chan == 14) 384116742Ssam return 2484; 385116742Ssam if (chan < 14) /* 0-13 */ 386116742Ssam return 2407 + chan*5; 387116742Ssam if (chan < 27) /* 15-26 */ 388116742Ssam return 2512 + ((chan-15)*20); 389116742Ssam return 5000 + (chan*5); 390116742Ssam } 391116742Ssam} 392116742Ssam 393116742Ssam/* 394116742Ssam * Setup the media data structures according to the channel and 395116742Ssam * rate tables. This must be called by the driver after 396116742Ssam * ieee80211_attach and before most anything else. 397116742Ssam */ 398116742Ssamvoid 399138568Ssamieee80211_media_init(struct ieee80211com *ic, 400116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 401116742Ssam{ 402116742Ssam#define ADD(_ic, _s, _o) \ 403116742Ssam ifmedia_add(&(_ic)->ic_media, \ 404116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 405138568Ssam struct ifnet *ifp = ic->ic_ifp; 406116742Ssam struct ifmediareq imr; 407116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 408116742Ssam struct ieee80211_rateset *rs; 409116742Ssam struct ieee80211_rateset allrates; 410116742Ssam 411165569Ssam /* NB: this works because the structure is initialized to zero */ 412165569Ssam if (LIST_EMPTY(&ic->ic_media.ifm_list)) { 413165569Ssam /* 414165569Ssam * Do late attach work that must wait for any subclass 415165569Ssam * (i.e. driver) work such as overriding methods. 416165569Ssam */ 417165569Ssam ieee80211_node_lateattach(ic); 418165569Ssam } else { 419165569Ssam /* 420165569Ssam * We are re-initializing the channel list; clear 421165569Ssam * the existing media state as the media routines 422165569Ssam * don't suppress duplicates. 423165569Ssam */ 424165569Ssam ifmedia_removeall(&ic->ic_media); 425165569Ssam ieee80211_chan_init(ic); 426165569Ssam } 427118887Ssam 428118887Ssam /* 429116742Ssam * Fill in media characteristics. 430116742Ssam */ 431116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 432116742Ssam maxrate = 0; 433116742Ssam memset(&allrates, 0, sizeof(allrates)); 434116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 435116742Ssam static const u_int mopts[] = { 436116742Ssam IFM_AUTO, 437124543Sonoe IFM_IEEE80211_11A, 438124543Sonoe IFM_IEEE80211_11B, 439124543Sonoe IFM_IEEE80211_11G, 440124543Sonoe IFM_IEEE80211_FH, 441124543Sonoe IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 442138568Ssam IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, 443116742Ssam }; 444167468Ssam if (isclr(ic->ic_modecaps, mode)) 445116742Ssam continue; 446116742Ssam mopt = mopts[mode]; 447116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 448116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 449116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 450116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 451116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 452116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 453116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 454117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 455117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 456116742Ssam if (mode == IEEE80211_MODE_AUTO) 457116742Ssam continue; 458116742Ssam rs = &ic->ic_sup_rates[mode]; 459116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 460116742Ssam rate = rs->rs_rates[i]; 461116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 462116742Ssam if (mword == 0) 463116742Ssam continue; 464116742Ssam ADD(ic, mword, mopt); 465116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 466116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 467116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 468116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 469116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 470116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 471117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 472117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 473116742Ssam /* 474116742Ssam * Add rate to the collection of all rates. 475116742Ssam */ 476116742Ssam r = rate & IEEE80211_RATE_VAL; 477116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 478116742Ssam if (allrates.rs_rates[j] == r) 479116742Ssam break; 480116742Ssam if (j == allrates.rs_nrates) { 481116742Ssam /* unique, add to the set */ 482116742Ssam allrates.rs_rates[j] = r; 483116742Ssam allrates.rs_nrates++; 484116742Ssam } 485116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 486116742Ssam if (rate > maxrate) 487116742Ssam maxrate = rate; 488116742Ssam } 489116742Ssam } 490116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 491116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 492116742Ssam IEEE80211_MODE_AUTO); 493116742Ssam if (mword == 0) 494116742Ssam continue; 495116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 496116742Ssam ADD(ic, mword, 0); 497116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 498116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 499116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 500116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 501116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 502116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 503117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 504117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 505116742Ssam } 506116742Ssam ieee80211_media_status(ifp, &imr); 507116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 508116742Ssam 509116742Ssam if (maxrate) 510116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 511116742Ssam#undef ADD 512116742Ssam} 513116742Ssam 514165569Ssamconst struct ieee80211_rateset * 515165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 516165569Ssam{ 517165569Ssam enum ieee80211_phymode mode = ieee80211_chan2mode(ic, c); 518165569Ssam 519166012Ssam if (IEEE80211_IS_CHAN_HALF(c)) 520166012Ssam return &ieee80211_rateset_half; 521166012Ssam if (IEEE80211_IS_CHAN_QUARTER(c)) 522166012Ssam return &ieee80211_rateset_quarter; 523165569Ssam return &ic->ic_sup_rates[mode]; 524165569Ssam} 525165569Ssam 526138568Ssamvoid 527138568Ssamieee80211_announce(struct ieee80211com *ic) 528138568Ssam{ 529138568Ssam struct ifnet *ifp = ic->ic_ifp; 530138568Ssam int i, mode, rate, mword; 531138568Ssam struct ieee80211_rateset *rs; 532138568Ssam 533138568Ssam for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { 534167468Ssam if (isclr(ic->ic_modecaps, mode)) 535138568Ssam continue; 536138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 537138568Ssam rs = &ic->ic_sup_rates[mode]; 538138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 539138568Ssam rate = rs->rs_rates[i]; 540138568Ssam mword = ieee80211_rate2media(ic, rate, mode); 541138568Ssam if (mword == 0) 542138568Ssam continue; 543138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 544138568Ssam (rate & IEEE80211_RATE_VAL) / 2, 545138568Ssam ((rate & 0x1) != 0 ? ".5" : "")); 546138568Ssam } 547138568Ssam printf("\n"); 548138568Ssam } 549138568Ssam} 550138568Ssam 551116742Ssamstatic int 552116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 553116742Ssam{ 554116742Ssam#define IEEERATE(_ic,_m,_i) \ 555116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 556116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 557116742Ssam for (i = 0; i < nrates; i++) 558116742Ssam if (IEEERATE(ic, mode, i) == rate) 559116742Ssam return i; 560116742Ssam return -1; 561116742Ssam#undef IEEERATE 562116742Ssam} 563116742Ssam 564116742Ssam/* 565138568Ssam * Find an instance by it's mac address. 566138568Ssam */ 567138568Ssamstruct ieee80211com * 568138568Ssamieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) 569138568Ssam{ 570138568Ssam struct ieee80211com *ic; 571138568Ssam 572138568Ssam /* XXX lock */ 573138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 574138568Ssam if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) 575138568Ssam return ic; 576138568Ssam return NULL; 577138568Ssam} 578138568Ssam 579138568Ssamstatic struct ieee80211com * 580138568Ssamieee80211_find_instance(struct ifnet *ifp) 581138568Ssam{ 582138568Ssam struct ieee80211com *ic; 583138568Ssam 584138568Ssam /* XXX lock */ 585138568Ssam /* XXX not right for multiple instances but works for now */ 586138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 587138568Ssam if (ic->ic_ifp == ifp) 588138568Ssam return ic; 589138568Ssam return NULL; 590138568Ssam} 591138568Ssam 592138568Ssam/* 593116742Ssam * Handle a media change request. 594116742Ssam */ 595116742Ssamint 596116742Ssamieee80211_media_change(struct ifnet *ifp) 597116742Ssam{ 598138568Ssam struct ieee80211com *ic; 599116742Ssam struct ifmedia_entry *ime; 600116742Ssam enum ieee80211_opmode newopmode; 601116742Ssam enum ieee80211_phymode newphymode; 602116742Ssam int i, j, newrate, error = 0; 603116742Ssam 604138568Ssam ic = ieee80211_find_instance(ifp); 605138568Ssam if (!ic) { 606138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 607138568Ssam return EINVAL; 608138568Ssam } 609116742Ssam ime = ic->ic_media.ifm_cur; 610116742Ssam /* 611116742Ssam * First, identify the phy mode. 612116742Ssam */ 613116742Ssam switch (IFM_MODE(ime->ifm_media)) { 614116742Ssam case IFM_IEEE80211_11A: 615116742Ssam newphymode = IEEE80211_MODE_11A; 616116742Ssam break; 617116742Ssam case IFM_IEEE80211_11B: 618116742Ssam newphymode = IEEE80211_MODE_11B; 619116742Ssam break; 620116742Ssam case IFM_IEEE80211_11G: 621116742Ssam newphymode = IEEE80211_MODE_11G; 622116742Ssam break; 623124543Sonoe case IFM_IEEE80211_FH: 624124543Sonoe newphymode = IEEE80211_MODE_FH; 625124543Sonoe break; 626116742Ssam case IFM_AUTO: 627116742Ssam newphymode = IEEE80211_MODE_AUTO; 628116742Ssam break; 629116742Ssam default: 630116742Ssam return EINVAL; 631116742Ssam } 632116742Ssam /* 633138568Ssam * Turbo mode is an ``option''. 634138568Ssam * XXX does not apply to AUTO 635116742Ssam */ 636116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 637138568Ssam if (newphymode == IEEE80211_MODE_11A) 638138568Ssam newphymode = IEEE80211_MODE_TURBO_A; 639138568Ssam else if (newphymode == IEEE80211_MODE_11G) 640138568Ssam newphymode = IEEE80211_MODE_TURBO_G; 641138568Ssam else 642116742Ssam return EINVAL; 643116742Ssam } 644116742Ssam /* 645116742Ssam * Validate requested mode is available. 646116742Ssam */ 647167468Ssam if (isclr(ic->ic_modecaps, newphymode)) 648116742Ssam return EINVAL; 649116742Ssam 650116742Ssam /* 651116742Ssam * Next, the fixed/variable rate. 652116742Ssam */ 653116742Ssam i = -1; 654116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 655116742Ssam /* 656116742Ssam * Convert media subtype to rate. 657116742Ssam */ 658116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 659116742Ssam if (newrate == 0) 660116742Ssam return EINVAL; 661116742Ssam /* 662116742Ssam * Check the rate table for the specified/current phy. 663116742Ssam */ 664116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 665116742Ssam /* 666116742Ssam * In autoselect mode search for the rate. 667116742Ssam */ 668116742Ssam for (j = IEEE80211_MODE_11A; 669116742Ssam j < IEEE80211_MODE_MAX; j++) { 670167468Ssam if (isclr(ic->ic_modecaps, j)) 671116742Ssam continue; 672116742Ssam i = findrate(ic, j, newrate); 673116742Ssam if (i != -1) { 674116742Ssam /* lock mode too */ 675116742Ssam newphymode = j; 676116742Ssam break; 677116742Ssam } 678116742Ssam } 679116742Ssam } else { 680116742Ssam i = findrate(ic, newphymode, newrate); 681116742Ssam } 682116742Ssam if (i == -1) /* mode/rate mismatch */ 683116742Ssam return EINVAL; 684116742Ssam } 685116742Ssam /* NB: defer rate setting to later */ 686116742Ssam 687116742Ssam /* 688116742Ssam * Deduce new operating mode but don't install it just yet. 689116742Ssam */ 690116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 691116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 692116742Ssam newopmode = IEEE80211_M_AHDEMO; 693116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 694116742Ssam newopmode = IEEE80211_M_HOSTAP; 695116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 696116742Ssam newopmode = IEEE80211_M_IBSS; 697117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 698117817Ssam newopmode = IEEE80211_M_MONITOR; 699116742Ssam else 700116742Ssam newopmode = IEEE80211_M_STA; 701116742Ssam 702116742Ssam /* 703116742Ssam * Autoselect doesn't make sense when operating as an AP. 704116742Ssam * If no phy mode has been selected, pick one and lock it 705116742Ssam * down so rate tables can be used in forming beacon frames 706116742Ssam * and the like. 707116742Ssam */ 708116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 709116742Ssam newphymode == IEEE80211_MODE_AUTO) { 710116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 711167468Ssam if (isset(ic->ic_modecaps, j)) { 712116742Ssam newphymode = j; 713116742Ssam break; 714116742Ssam } 715116742Ssam } 716116742Ssam 717116742Ssam /* 718116742Ssam * Handle phy mode change. 719116742Ssam */ 720116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 721116742Ssam error = ieee80211_setmode(ic, newphymode); 722116742Ssam if (error != 0) 723116742Ssam return error; 724116742Ssam error = ENETRESET; 725116742Ssam } 726116742Ssam 727116742Ssam /* 728116742Ssam * Committed to changes, install the rate setting. 729116742Ssam */ 730116742Ssam if (ic->ic_fixed_rate != i) { 731116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 732116742Ssam error = ENETRESET; 733116742Ssam } 734116742Ssam 735116742Ssam /* 736116742Ssam * Handle operating mode change. 737116742Ssam */ 738116742Ssam if (ic->ic_opmode != newopmode) { 739116742Ssam ic->ic_opmode = newopmode; 740116742Ssam switch (newopmode) { 741116742Ssam case IEEE80211_M_AHDEMO: 742116742Ssam case IEEE80211_M_HOSTAP: 743116742Ssam case IEEE80211_M_STA: 744117817Ssam case IEEE80211_M_MONITOR: 745116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 746116742Ssam break; 747116742Ssam case IEEE80211_M_IBSS: 748116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 749116742Ssam break; 750116742Ssam } 751138568Ssam /* 752138568Ssam * Yech, slot time may change depending on the 753138568Ssam * operating mode so reset it to be sure everything 754138568Ssam * is setup appropriately. 755138568Ssam */ 756138568Ssam ieee80211_reset_erp(ic); 757138568Ssam ieee80211_wme_initparams(ic); /* after opmode change */ 758116742Ssam error = ENETRESET; 759116742Ssam } 760116742Ssam#ifdef notdef 761116742Ssam if (error == 0) 762116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 763116742Ssam#endif 764116742Ssam return error; 765116742Ssam} 766116742Ssam 767116742Ssamvoid 768116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 769116742Ssam{ 770138568Ssam struct ieee80211com *ic; 771165569Ssam const struct ieee80211_rateset *rs; 772116742Ssam 773138568Ssam ic = ieee80211_find_instance(ifp); 774138568Ssam if (!ic) { 775138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 776138568Ssam return; 777138568Ssam } 778116742Ssam imr->ifm_status = IFM_AVALID; 779116742Ssam imr->ifm_active = IFM_IEEE80211; 780138568Ssam if (ic->ic_state == IEEE80211_S_RUN) 781116742Ssam imr->ifm_status |= IFM_ACTIVE; 782138568Ssam /* 783138568Ssam * Calculate a current rate if possible. 784138568Ssam */ 785148290Ssam if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 786138568Ssam /* 787138568Ssam * A fixed rate is set, report that. 788138568Ssam */ 789166012Ssam rs = ieee80211_get_suprates(ic, ic->ic_curchan); 790138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 791138568Ssam rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); 792138568Ssam } else if (ic->ic_opmode == IEEE80211_M_STA) { 793138568Ssam /* 794138568Ssam * In station mode report the current transmit rate. 795138568Ssam */ 796138568Ssam rs = &ic->ic_bss->ni_rates; 797138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 798138568Ssam rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); 799128966Sandre } else 800138568Ssam imr->ifm_active |= IFM_AUTO; 801116742Ssam switch (ic->ic_opmode) { 802116742Ssam case IEEE80211_M_STA: 803116742Ssam break; 804116742Ssam case IEEE80211_M_IBSS: 805116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 806116742Ssam break; 807116742Ssam case IEEE80211_M_AHDEMO: 808116742Ssam /* should not come here */ 809116742Ssam break; 810116742Ssam case IEEE80211_M_HOSTAP: 811116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 812116742Ssam break; 813117817Ssam case IEEE80211_M_MONITOR: 814117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 815117817Ssam break; 816116742Ssam } 817116742Ssam switch (ic->ic_curmode) { 818116742Ssam case IEEE80211_MODE_11A: 819124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A; 820116742Ssam break; 821116742Ssam case IEEE80211_MODE_11B: 822124543Sonoe imr->ifm_active |= IFM_IEEE80211_11B; 823116742Ssam break; 824116742Ssam case IEEE80211_MODE_11G: 825124543Sonoe imr->ifm_active |= IFM_IEEE80211_11G; 826116742Ssam break; 827124543Sonoe case IEEE80211_MODE_FH: 828124543Sonoe imr->ifm_active |= IFM_IEEE80211_FH; 829124543Sonoe break; 830138568Ssam case IEEE80211_MODE_TURBO_A: 831124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A 832116742Ssam | IFM_IEEE80211_TURBO; 833116742Ssam break; 834138568Ssam case IEEE80211_MODE_TURBO_G: 835138568Ssam imr->ifm_active |= IFM_IEEE80211_11G 836138568Ssam | IFM_IEEE80211_TURBO; 837138568Ssam break; 838116742Ssam } 839116742Ssam} 840116742Ssam 841116742Ssamvoid 842138568Ssamieee80211_watchdog(struct ieee80211com *ic) 843116742Ssam{ 844138568Ssam struct ieee80211_node_table *nt; 845138568Ssam int need_inact_timer = 0; 846116742Ssam 847138568Ssam if (ic->ic_state != IEEE80211_S_INIT) { 848138568Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 849138568Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 850138568Ssam nt = &ic->ic_scan; 851138568Ssam if (nt->nt_inact_timer) { 852138568Ssam if (--nt->nt_inact_timer == 0) 853138568Ssam nt->nt_timeout(nt); 854138568Ssam need_inact_timer += nt->nt_inact_timer; 855138568Ssam } 856140753Ssam nt = &ic->ic_sta; 857140753Ssam if (nt->nt_inact_timer) { 858138568Ssam if (--nt->nt_inact_timer == 0) 859138568Ssam nt->nt_timeout(nt); 860138568Ssam need_inact_timer += nt->nt_inact_timer; 861138568Ssam } 862116742Ssam } 863138568Ssam if (ic->ic_mgt_timer != 0 || need_inact_timer) 864138568Ssam ic->ic_ifp->if_timer = 1; 865116742Ssam} 866116742Ssam 867116742Ssam/* 868116742Ssam * Set the current phy mode and recalculate the active channel 869116742Ssam * set based on the available channels for this mode. Also 870116742Ssam * select a new default/current channel if the current one is 871116742Ssam * inappropriate for this mode. 872116742Ssam */ 873116742Ssamint 874116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 875116742Ssam{ 876116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 877116742Ssam static const u_int chanflags[] = { 878116742Ssam 0, /* IEEE80211_MODE_AUTO */ 879116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 880116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 881116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 882124543Sonoe IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 883138568Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ 884138568Ssam IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 885116742Ssam }; 886116742Ssam struct ieee80211_channel *c; 887116742Ssam u_int modeflags; 888116742Ssam int i; 889116742Ssam 890116742Ssam /* validate new mode */ 891167468Ssam if (isclr(ic->ic_modecaps, mode)) { 892138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 893138568Ssam "%s: mode %u not supported (caps 0x%x)\n", 894138568Ssam __func__, mode, ic->ic_modecaps); 895116742Ssam return EINVAL; 896116742Ssam } 897116742Ssam 898116742Ssam /* 899116742Ssam * Verify at least one channel is present in the available 900116742Ssam * channel list before committing to the new mode. 901116742Ssam */ 902127760Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); 903116742Ssam modeflags = chanflags[mode]; 904116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 905116742Ssam c = &ic->ic_channels[i]; 906153350Ssam if (c->ic_flags == 0) 907153350Ssam continue; 908116742Ssam if (mode == IEEE80211_MODE_AUTO) { 909153863Ssam /* ignore static turbo channels for autoselect */ 910153863Ssam if (!IEEE80211_IS_CHAN_T(c)) 911116742Ssam break; 912116742Ssam } else { 913116742Ssam if ((c->ic_flags & modeflags) == modeflags) 914116742Ssam break; 915116742Ssam } 916116742Ssam } 917116742Ssam if (i > IEEE80211_CHAN_MAX) { 918138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 919138568Ssam "%s: no channels found for mode %u\n", __func__, mode); 920116742Ssam return EINVAL; 921116742Ssam } 922116742Ssam 923116742Ssam /* 924116742Ssam * Calculate the active channel set. 925116742Ssam */ 926116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 927116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 928116742Ssam c = &ic->ic_channels[i]; 929153350Ssam if (c->ic_flags == 0) 930153350Ssam continue; 931116742Ssam if (mode == IEEE80211_MODE_AUTO) { 932153863Ssam /* take anything but static turbo channels */ 933153863Ssam if (!IEEE80211_IS_CHAN_T(c)) 934116742Ssam setbit(ic->ic_chan_active, i); 935116742Ssam } else { 936116742Ssam if ((c->ic_flags & modeflags) == modeflags) 937116742Ssam setbit(ic->ic_chan_active, i); 938116742Ssam } 939116742Ssam } 940116742Ssam /* 941116742Ssam * If no current/default channel is setup or the current 942116742Ssam * channel is wrong for the mode then pick the first 943116742Ssam * available channel from the active list. This is likely 944116742Ssam * not the right one. 945116742Ssam */ 946166012Ssam if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_curchan))) { 947166012Ssam ic->ic_curchan = NULL; 948116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 949116742Ssam if (isset(ic->ic_chan_active, i)) { 950166012Ssam ic->ic_curchan = &ic->ic_channels[i]; 951116742Ssam break; 952116742Ssam } 953166012Ssam KASSERT(ic->ic_curchan != NULL, ("no current channel")); 954116742Ssam } 955166012Ssam if (ic->ic_ibss_chan == NULL || 956166012Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) 957166012Ssam ic->ic_ibss_chan = ic->ic_curchan; 958138568Ssam /* 959138568Ssam * If the desired channel is set but no longer valid then reset it. 960138568Ssam */ 961138568Ssam if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 962138568Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) 963138568Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 964116742Ssam 965116742Ssam /* 966166012Ssam * Adjust basic rates in 11b/11g supported rate set. 967166012Ssam * Note that if operating on a hal/quarter rate channel 968166012Ssam * this is a noop as those rates sets are different 969166012Ssam * and used instead. 970116742Ssam */ 971166012Ssam if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 972166012Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode); 973166012Ssam 974138568Ssam /* 975138568Ssam * Setup an initial rate set according to the 976138568Ssam * current/default channel selected above. This 977138568Ssam * will be changed when scanning but must exist 978138568Ssam * now so driver have a consistent state of ic_ibss_chan. 979138568Ssam */ 980166012Ssam if (ic->ic_bss != NULL) /* NB: can be called before lateattach */ 981138568Ssam ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; 982116742Ssam 983116742Ssam ic->ic_curmode = mode; 984138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 985138568Ssam ieee80211_wme_initparams(ic); /* reset WME stat */ 986138568Ssam 987116742Ssam return 0; 988116742Ssam#undef N 989116742Ssam} 990116742Ssam 991116742Ssam/* 992116742Ssam * Return the phy mode for with the specified channel so the 993138568Ssam * caller can select a rate set. This is problematic for channels 994138568Ssam * where multiple operating modes are possible (e.g. 11g+11b). 995138568Ssam * In those cases we defer to the current operating mode when set. 996116742Ssam */ 997116742Ssamenum ieee80211_phymode 998165569Ssamieee80211_chan2mode(struct ieee80211com *ic, const struct ieee80211_channel *chan) 999116742Ssam{ 1000153350Ssam if (IEEE80211_IS_CHAN_T(chan)) { 1001153350Ssam return IEEE80211_MODE_TURBO_A; 1002153350Ssam } else if (IEEE80211_IS_CHAN_5GHZ(chan)) { 1003116742Ssam return IEEE80211_MODE_11A; 1004138568Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) 1005124543Sonoe return IEEE80211_MODE_FH; 1006138568Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { 1007138568Ssam /* 1008138568Ssam * This assumes all 11g channels are also usable 1009138568Ssam * for 11b, which is currently true. 1010138568Ssam */ 1011138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) 1012138568Ssam return IEEE80211_MODE_TURBO_G; 1013138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11B) 1014138568Ssam return IEEE80211_MODE_11B; 1015116742Ssam return IEEE80211_MODE_11G; 1016138568Ssam } else 1017116742Ssam return IEEE80211_MODE_11B; 1018116742Ssam} 1019116742Ssam 1020116742Ssam/* 1021116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 1022116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 1023116742Ssam */ 1024116742Ssamint 1025116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1026116742Ssam{ 1027116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1028116742Ssam static const struct { 1029116742Ssam u_int m; /* rate + mode */ 1030116742Ssam u_int r; /* if_media rate */ 1031116742Ssam } rates[] = { 1032124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1033124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1034124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1035124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1036124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1037124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1038124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1039124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1040124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1041124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1042124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1043124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1044124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1045124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1046124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1047124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1048124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1049124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1050124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1051124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1052124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1053124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1054124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1055124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1056124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1057124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1058124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1059165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1060165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1061165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1062116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 1063116742Ssam }; 1064116742Ssam u_int mask, i; 1065116742Ssam 1066116742Ssam mask = rate & IEEE80211_RATE_VAL; 1067116742Ssam switch (mode) { 1068116742Ssam case IEEE80211_MODE_11A: 1069138568Ssam case IEEE80211_MODE_TURBO_A: 1070124543Sonoe mask |= IFM_IEEE80211_11A; 1071116742Ssam break; 1072116742Ssam case IEEE80211_MODE_11B: 1073124543Sonoe mask |= IFM_IEEE80211_11B; 1074116742Ssam break; 1075124543Sonoe case IEEE80211_MODE_FH: 1076124543Sonoe mask |= IFM_IEEE80211_FH; 1077124543Sonoe break; 1078116742Ssam case IEEE80211_MODE_AUTO: 1079116742Ssam /* NB: ic may be NULL for some drivers */ 1080116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 1081124543Sonoe mask |= IFM_IEEE80211_FH; 1082124543Sonoe break; 1083116742Ssam } 1084116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1085116742Ssam /* fall thru... */ 1086116742Ssam case IEEE80211_MODE_11G: 1087138568Ssam case IEEE80211_MODE_TURBO_G: 1088124543Sonoe mask |= IFM_IEEE80211_11G; 1089116742Ssam break; 1090116742Ssam } 1091116742Ssam for (i = 0; i < N(rates); i++) 1092116742Ssam if (rates[i].m == mask) 1093116742Ssam return rates[i].r; 1094116742Ssam return IFM_AUTO; 1095116742Ssam#undef N 1096116742Ssam} 1097116742Ssam 1098116742Ssamint 1099116742Ssamieee80211_media2rate(int mword) 1100116742Ssam{ 1101116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1102116742Ssam static const int ieeerates[] = { 1103116742Ssam -1, /* IFM_AUTO */ 1104116742Ssam 0, /* IFM_MANUAL */ 1105116742Ssam 0, /* IFM_NONE */ 1106116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1107116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1108116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1109116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1110116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1111116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1112116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1113116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1114116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1115116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1116116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1117116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1118116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1119116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1120116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1121116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1122165569Ssam 0, /* IFM_IEEE80211_DS354k */ 1123165569Ssam 0, /* IFM_IEEE80211_DS512k */ 1124165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 1125165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 1126165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 1127116742Ssam }; 1128116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1129116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1130116742Ssam#undef N 1131116742Ssam} 1132