ieee80211.c revision 165569
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 165569 2006-12-27 18:46:18Z sam $"); 35116742Ssam 36116742Ssam/* 37116742Ssam * IEEE 802.11 generic handler 38116742Ssam */ 39116742Ssam 40116742Ssam#include <sys/param.h> 41116742Ssam#include <sys/systm.h> 42116742Ssam#include <sys/kernel.h> 43138568Ssam 44116742Ssam#include <sys/socket.h> 45116742Ssam 46116742Ssam#include <net/if.h> 47116742Ssam#include <net/if_media.h> 48116742Ssam#include <net/ethernet.h> 49116742Ssam 50116742Ssam#include <net80211/ieee80211_var.h> 51116742Ssam 52116742Ssam#include <net/bpf.h> 53116742Ssam 54139502Ssamconst char *ieee80211_phymode_name[] = { 55116742Ssam "auto", /* IEEE80211_MODE_AUTO */ 56116742Ssam "11a", /* IEEE80211_MODE_11A */ 57116742Ssam "11b", /* IEEE80211_MODE_11B */ 58116742Ssam "11g", /* IEEE80211_MODE_11G */ 59124543Sonoe "FH", /* IEEE80211_MODE_FH */ 60138568Ssam "turboA", /* IEEE80211_MODE_TURBO_A */ 61138568Ssam "turboG", /* IEEE80211_MODE_TURBO_G */ 62116742Ssam}; 63116742Ssam 64164645Ssam/* 65164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units). 66164645Ssam */ 67164645Ssam#define B(r) ((r) | IEEE80211_RATE_BASIC) 68164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a = 69164645Ssam { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; 70165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half = 71165569Ssam { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; 72165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter = 73165569Ssam { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; 74164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b = 75164645Ssam { 4, { B(2), B(4), B(11), B(22) } }; 76164645Ssam/* NB: OFDM rates are handled specially based on mode */ 77164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g = 78164645Ssam { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; 79164645Ssam#undef B 80164645Ssam 81138568Ssam/* list of all instances */ 82138568SsamSLIST_HEAD(ieee80211_list, ieee80211com); 83138568Ssamstatic struct ieee80211_list ieee80211_list = 84138568Ssam SLIST_HEAD_INITIALIZER(ieee80211_list); 85138568Ssamstatic u_int8_t ieee80211_vapmap[32]; /* enough for 256 */ 86138568Ssamstatic struct mtx ieee80211_vap_mtx; 87138568SsamMTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF); 88138568Ssam 89138568Ssamstatic void 90138568Ssamieee80211_add_vap(struct ieee80211com *ic) 91138568Ssam{ 92138568Ssam#define N(a) (sizeof(a)/sizeof(a[0])) 93138568Ssam int i; 94138568Ssam u_int8_t b; 95138568Ssam 96138568Ssam mtx_lock(&ieee80211_vap_mtx); 97138568Ssam ic->ic_vap = 0; 98138568Ssam for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++) 99138568Ssam ic->ic_vap += NBBY; 100138568Ssam if (i == N(ieee80211_vapmap)) 101138568Ssam panic("vap table full"); 102138568Ssam for (b = ieee80211_vapmap[i]; b & 1; b >>= 1) 103138568Ssam ic->ic_vap++; 104138568Ssam setbit(ieee80211_vapmap, ic->ic_vap); 105138568Ssam SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); 106138568Ssam mtx_unlock(&ieee80211_vap_mtx); 107138568Ssam#undef N 108138568Ssam} 109138568Ssam 110138568Ssamstatic void 111138568Ssamieee80211_remove_vap(struct ieee80211com *ic) 112138568Ssam{ 113138568Ssam mtx_lock(&ieee80211_vap_mtx); 114138568Ssam SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); 115138568Ssam KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY, 116138568Ssam ("invalid vap id %d", ic->ic_vap)); 117138568Ssam KASSERT(isset(ieee80211_vapmap, ic->ic_vap), 118138568Ssam ("vap id %d not allocated", ic->ic_vap)); 119138568Ssam clrbit(ieee80211_vapmap, ic->ic_vap); 120138568Ssam mtx_unlock(&ieee80211_vap_mtx); 121138568Ssam} 122138568Ssam 123140915Ssam/* 124140915Ssam * Default reset method for use with the ioctl support. This 125140915Ssam * method is invoked after any state change in the 802.11 126140915Ssam * layer that should be propagated to the hardware but not 127140915Ssam * require re-initialization of the 802.11 state machine (e.g 128140915Ssam * rescanning for an ap). We always return ENETRESET which 129140915Ssam * should cause the driver to re-initialize the device. Drivers 130140915Ssam * can override this method to implement more optimized support. 131140915Ssam */ 132140915Ssamstatic int 133140915Ssamieee80211_default_reset(struct ifnet *ifp) 134140915Ssam{ 135140915Ssam return ENETRESET; 136140915Ssam} 137140915Ssam 138165569Ssam/* 139165569Ssam * Fill in 802.11 available channel set, mark 140165569Ssam * all available channels as active, and pick 141165569Ssam * a default channel if not already specified. 142165569Ssam */ 143165569Ssamstatic void 144165569Ssamieee80211_chan_init(struct ieee80211com *ic) 145116742Ssam{ 146164645Ssam#define RATESDEFINED(m) \ 147164645Ssam ((ic->ic_modecaps & (1<<m)) && ic->ic_sup_rates[m].rs_nrates != 0) 148165569Ssam#define DEFAULTRATES(m, def) do { \ 149165569Ssam if (!RATESDEFINED(m)) ic->ic_sup_rates[m] = def; \ 150165569Ssam} while (0) 151138568Ssam struct ifnet *ifp = ic->ic_ifp; 152116742Ssam struct ieee80211_channel *c; 153116742Ssam int i; 154116742Ssam 155116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 156165569Ssam ic->ic_modecaps = 1<<IEEE80211_MODE_AUTO; 157116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 158116742Ssam c = &ic->ic_channels[i]; 159116742Ssam if (c->ic_flags) { 160116742Ssam /* 161116742Ssam * Verify driver passed us valid data. 162116742Ssam */ 163116742Ssam if (i != ieee80211_chan2ieee(ic, c)) { 164116742Ssam if_printf(ifp, "bad channel ignored; " 165116742Ssam "freq %u flags %x number %u\n", 166116742Ssam c->ic_freq, c->ic_flags, i); 167116742Ssam c->ic_flags = 0; /* NB: remove */ 168116742Ssam continue; 169116742Ssam } 170116742Ssam setbit(ic->ic_chan_avail, i); 171116742Ssam /* 172116742Ssam * Identify mode capabilities. 173116742Ssam */ 174116742Ssam if (IEEE80211_IS_CHAN_A(c)) 175116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11A; 176116742Ssam if (IEEE80211_IS_CHAN_B(c)) 177116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11B; 178116742Ssam if (IEEE80211_IS_CHAN_PUREG(c)) 179116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; 180124543Sonoe if (IEEE80211_IS_CHAN_FHSS(c)) 181124543Sonoe ic->ic_modecaps |= 1<<IEEE80211_MODE_FH; 182116742Ssam if (IEEE80211_IS_CHAN_T(c)) 183138568Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A; 184138568Ssam if (IEEE80211_IS_CHAN_108G(c)) 185138568Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G; 186148936Ssam if (ic->ic_curchan == NULL) { 187148936Ssam /* arbitrarily pick the first channel */ 188148936Ssam ic->ic_curchan = &ic->ic_channels[i]; 189148936Ssam } 190116742Ssam } 191116742Ssam } 192164645Ssam 193164645Ssam /* fillin well-known rate sets if driver has not specified */ 194165569Ssam DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); 195165569Ssam DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); 196165569Ssam DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); 197165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); 198165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); 199165569Ssam 200165569Ssam /* 201165569Ssam * Set auto mode to reset active channel state and any desired channel. 202165569Ssam */ 203165569Ssam (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); 204165569Ssam#undef DEFAULTRATES 205165569Ssam#undef RATESDEFINED 206165569Ssam} 207165569Ssam 208165569Ssamvoid 209165569Ssamieee80211_ifattach(struct ieee80211com *ic) 210165569Ssam{ 211165569Ssam struct ifnet *ifp = ic->ic_ifp; 212165569Ssam 213165569Ssam ether_ifattach(ifp, ic->ic_myaddr); 214165569Ssam ifp->if_output = ieee80211_output; 215165569Ssam 216165569Ssam bpfattach2(ifp, DLT_IEEE802_11, 217165569Ssam sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); 218165569Ssam 219165569Ssam ieee80211_crypto_attach(ic); 220165569Ssam 221165569Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 222165569Ssam /* 223165569Ssam * Fill in 802.11 available channel set, mark all 224165569Ssam * available channels as active, and pick a default 225165569Ssam * channel if not already specified. 226165569Ssam */ 227165569Ssam ieee80211_chan_init(ic); 228139526Ssam#if 0 229138568Ssam /* 230138568Ssam * Enable WME by default if we're capable. 231138568Ssam */ 232138568Ssam if (ic->ic_caps & IEEE80211_C_WME) 233138568Ssam ic->ic_flags |= IEEE80211_F_WME; 234139526Ssam#endif 235153421Ssam if (ic->ic_caps & IEEE80211_C_BURST) 236153421Ssam ic->ic_flags |= IEEE80211_F_BURST; 237116742Ssam 238155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 239155688Ssam ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 240138568Ssam ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; 241138568Ssam IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); 242116742Ssam 243155688Ssam ic->ic_lintval = ic->ic_bintval; 244138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 245138568Ssam 246138568Ssam ieee80211_node_attach(ic); 247138568Ssam ieee80211_proto_attach(ic); 248138568Ssam 249138568Ssam ieee80211_add_vap(ic); 250138568Ssam 251138568Ssam ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ 252140915Ssam 253140915Ssam /* 254140915Ssam * Install a default reset method for the ioctl support. 255140915Ssam * The driver is expected to fill this in before calling us. 256140915Ssam */ 257140915Ssam if (ic->ic_reset == NULL) 258140915Ssam ic->ic_reset = ieee80211_default_reset; 259160690Ssam 260160690Ssam KASSERT(ifp->if_spare2 == NULL, ("oops, hosed")); 261160690Ssam ifp->if_spare2 = ic; /* XXX temp backpointer */ 262116742Ssam} 263116742Ssam 264116742Ssamvoid 265138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 266116742Ssam{ 267138568Ssam struct ifnet *ifp = ic->ic_ifp; 268116742Ssam 269138568Ssam ieee80211_remove_vap(ic); 270138568Ssam 271138568Ssam ieee80211_sysctl_detach(ic); 272138568Ssam ieee80211_proto_detach(ic); 273138568Ssam ieee80211_crypto_detach(ic); 274138568Ssam ieee80211_node_detach(ic); 275116742Ssam ifmedia_removeall(&ic->ic_media); 276138568Ssam 277138568Ssam IEEE80211_BEACON_LOCK_DESTROY(ic); 278138568Ssam 279116742Ssam bpfdetach(ifp); 280116742Ssam ether_ifdetach(ifp); 281116742Ssam} 282116742Ssam 283116742Ssam/* 284116742Ssam * Convert MHz frequency to IEEE channel number. 285116742Ssam */ 286152450Ssamint 287116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 288116742Ssam{ 289116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 290116742Ssam if (freq == 2484) 291116742Ssam return 14; 292116742Ssam if (freq < 2484) 293152450Ssam return ((int) freq - 2407) / 5; 294116742Ssam else 295116742Ssam return 15 + ((freq - 2512) / 20); 296116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 297165569Ssam if (freq <= 5000) { 298165569Ssam if (flags &(IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) 299165569Ssam return 37 + ((freq * 10) + 300165569Ssam ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 301152450Ssam return (freq - 4000) / 5; 302165569Ssam } else 303152450Ssam return (freq - 5000) / 5; 304116742Ssam } else { /* either, guess */ 305116742Ssam if (freq == 2484) 306116742Ssam return 14; 307116742Ssam if (freq < 2484) 308152450Ssam return ((int) freq - 2407) / 5; 309152450Ssam if (freq < 5000) { 310165569Ssam if (flags &(IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) 311165569Ssam return 37 + ((freq * 10) + 312165569Ssam ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 313165569Ssam else if (freq > 4900) 314152450Ssam return (freq - 4000) / 5; 315152450Ssam else 316152450Ssam return 15 + ((freq - 2512) / 20); 317152450Ssam } 318116742Ssam return (freq - 5000) / 5; 319116742Ssam } 320116742Ssam} 321116742Ssam 322116742Ssam/* 323116742Ssam * Convert channel to IEEE channel number. 324116742Ssam */ 325152450Ssamint 326116742Ssamieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) 327116742Ssam{ 328116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 329116742Ssam return c - ic->ic_channels; 330116742Ssam else if (c == IEEE80211_CHAN_ANYC) 331116742Ssam return IEEE80211_CHAN_ANY; 332117039Ssam else if (c != NULL) { 333138568Ssam if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", 334116742Ssam c->ic_freq, c->ic_flags); 335116742Ssam return 0; /* XXX */ 336117039Ssam } else { 337138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 338117039Ssam return 0; /* XXX */ 339116742Ssam } 340116742Ssam} 341116742Ssam 342116742Ssam/* 343116742Ssam * Convert IEEE channel number to MHz frequency. 344116742Ssam */ 345116742Ssamu_int 346116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 347116742Ssam{ 348116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 349116742Ssam if (chan == 14) 350116742Ssam return 2484; 351116742Ssam if (chan < 14) 352116742Ssam return 2407 + chan*5; 353116742Ssam else 354116742Ssam return 2512 + ((chan-15)*20); 355116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 356165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 357165569Ssam chan -= 37; 358165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 359165569Ssam } 360116742Ssam return 5000 + (chan*5); 361116742Ssam } else { /* either, guess */ 362116742Ssam if (chan == 14) 363116742Ssam return 2484; 364116742Ssam if (chan < 14) /* 0-13 */ 365116742Ssam return 2407 + chan*5; 366116742Ssam if (chan < 27) /* 15-26 */ 367116742Ssam return 2512 + ((chan-15)*20); 368165569Ssam /* XXX can't distinguish PSB channels */ 369116742Ssam return 5000 + (chan*5); 370116742Ssam } 371116742Ssam} 372116742Ssam 373116742Ssam/* 374116742Ssam * Setup the media data structures according to the channel and 375116742Ssam * rate tables. This must be called by the driver after 376116742Ssam * ieee80211_attach and before most anything else. 377116742Ssam */ 378116742Ssamvoid 379138568Ssamieee80211_media_init(struct ieee80211com *ic, 380116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 381116742Ssam{ 382116742Ssam#define ADD(_ic, _s, _o) \ 383116742Ssam ifmedia_add(&(_ic)->ic_media, \ 384116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 385138568Ssam struct ifnet *ifp = ic->ic_ifp; 386116742Ssam struct ifmediareq imr; 387116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 388116742Ssam struct ieee80211_rateset *rs; 389116742Ssam struct ieee80211_rateset allrates; 390116742Ssam 391165569Ssam /* NB: this works because the structure is initialized to zero */ 392165569Ssam if (LIST_EMPTY(&ic->ic_media.ifm_list)) { 393165569Ssam /* 394165569Ssam * Do late attach work that must wait for any subclass 395165569Ssam * (i.e. driver) work such as overriding methods. 396165569Ssam */ 397165569Ssam ieee80211_node_lateattach(ic); 398165569Ssam } else { 399165569Ssam /* 400165569Ssam * We are re-initializing the channel list; clear 401165569Ssam * the existing media state as the media routines 402165569Ssam * don't suppress duplicates. 403165569Ssam */ 404165569Ssam ifmedia_removeall(&ic->ic_media); 405165569Ssam ieee80211_chan_init(ic); 406165569Ssam } 407118887Ssam 408118887Ssam /* 409116742Ssam * Fill in media characteristics. 410116742Ssam */ 411116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 412116742Ssam maxrate = 0; 413116742Ssam memset(&allrates, 0, sizeof(allrates)); 414116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 415116742Ssam static const u_int mopts[] = { 416116742Ssam IFM_AUTO, 417124543Sonoe IFM_IEEE80211_11A, 418124543Sonoe IFM_IEEE80211_11B, 419124543Sonoe IFM_IEEE80211_11G, 420124543Sonoe IFM_IEEE80211_FH, 421124543Sonoe IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 422138568Ssam IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, 423116742Ssam }; 424116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 425116742Ssam continue; 426116742Ssam mopt = mopts[mode]; 427116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 428116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 429116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 430116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 431116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 432116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 433116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 434117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 435117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 436116742Ssam if (mode == IEEE80211_MODE_AUTO) 437116742Ssam continue; 438116742Ssam rs = &ic->ic_sup_rates[mode]; 439116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 440116742Ssam rate = rs->rs_rates[i]; 441116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 442116742Ssam if (mword == 0) 443116742Ssam continue; 444116742Ssam ADD(ic, mword, mopt); 445116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 446116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 447116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 448116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 449116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 450116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 451117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 452117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 453116742Ssam /* 454116742Ssam * Add rate to the collection of all rates. 455116742Ssam */ 456116742Ssam r = rate & IEEE80211_RATE_VAL; 457116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 458116742Ssam if (allrates.rs_rates[j] == r) 459116742Ssam break; 460116742Ssam if (j == allrates.rs_nrates) { 461116742Ssam /* unique, add to the set */ 462116742Ssam allrates.rs_rates[j] = r; 463116742Ssam allrates.rs_nrates++; 464116742Ssam } 465116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 466116742Ssam if (rate > maxrate) 467116742Ssam maxrate = rate; 468116742Ssam } 469116742Ssam } 470116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 471116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 472116742Ssam IEEE80211_MODE_AUTO); 473116742Ssam if (mword == 0) 474116742Ssam continue; 475116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 476116742Ssam ADD(ic, mword, 0); 477116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 478116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 479116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 480116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 481116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 482116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 483117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 484117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 485116742Ssam } 486116742Ssam ieee80211_media_status(ifp, &imr); 487116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 488116742Ssam 489116742Ssam if (maxrate) 490116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 491116742Ssam#undef ADD 492116742Ssam} 493116742Ssam 494165569Ssamconst struct ieee80211_rateset * 495165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 496165569Ssam{ 497165569Ssam enum ieee80211_phymode mode = ieee80211_chan2mode(ic, c); 498165569Ssam 499165569Ssam if (mode == IEEE80211_MODE_11A) { 500165569Ssam if (IEEE80211_IS_CHAN_HALF(c)) 501165569Ssam return &ieee80211_rateset_half; 502165569Ssam if (IEEE80211_IS_CHAN_QUARTER(c)) 503165569Ssam return &ieee80211_rateset_quarter; 504165569Ssam } 505165569Ssam return &ic->ic_sup_rates[mode]; 506165569Ssam} 507165569Ssam 508138568Ssamvoid 509138568Ssamieee80211_announce(struct ieee80211com *ic) 510138568Ssam{ 511138568Ssam struct ifnet *ifp = ic->ic_ifp; 512138568Ssam int i, mode, rate, mword; 513138568Ssam struct ieee80211_rateset *rs; 514138568Ssam 515138568Ssam for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { 516138568Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 517138568Ssam continue; 518138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 519138568Ssam rs = &ic->ic_sup_rates[mode]; 520138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 521138568Ssam rate = rs->rs_rates[i]; 522138568Ssam mword = ieee80211_rate2media(ic, rate, mode); 523138568Ssam if (mword == 0) 524138568Ssam continue; 525138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 526138568Ssam (rate & IEEE80211_RATE_VAL) / 2, 527138568Ssam ((rate & 0x1) != 0 ? ".5" : "")); 528138568Ssam } 529138568Ssam printf("\n"); 530138568Ssam } 531138568Ssam} 532138568Ssam 533116742Ssamstatic int 534116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 535116742Ssam{ 536116742Ssam#define IEEERATE(_ic,_m,_i) \ 537116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 538116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 539116742Ssam for (i = 0; i < nrates; i++) 540116742Ssam if (IEEERATE(ic, mode, i) == rate) 541116742Ssam return i; 542116742Ssam return -1; 543116742Ssam#undef IEEERATE 544116742Ssam} 545116742Ssam 546116742Ssam/* 547138568Ssam * Find an instance by it's mac address. 548138568Ssam */ 549138568Ssamstruct ieee80211com * 550138568Ssamieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) 551138568Ssam{ 552138568Ssam struct ieee80211com *ic; 553138568Ssam 554138568Ssam /* XXX lock */ 555138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 556138568Ssam if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) 557138568Ssam return ic; 558138568Ssam return NULL; 559138568Ssam} 560138568Ssam 561138568Ssamstatic struct ieee80211com * 562138568Ssamieee80211_find_instance(struct ifnet *ifp) 563138568Ssam{ 564138568Ssam struct ieee80211com *ic; 565138568Ssam 566138568Ssam /* XXX lock */ 567138568Ssam /* XXX not right for multiple instances but works for now */ 568138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 569138568Ssam if (ic->ic_ifp == ifp) 570138568Ssam return ic; 571138568Ssam return NULL; 572138568Ssam} 573138568Ssam 574138568Ssam/* 575116742Ssam * Handle a media change request. 576116742Ssam */ 577116742Ssamint 578116742Ssamieee80211_media_change(struct ifnet *ifp) 579116742Ssam{ 580138568Ssam struct ieee80211com *ic; 581116742Ssam struct ifmedia_entry *ime; 582116742Ssam enum ieee80211_opmode newopmode; 583116742Ssam enum ieee80211_phymode newphymode; 584116742Ssam int i, j, newrate, error = 0; 585116742Ssam 586138568Ssam ic = ieee80211_find_instance(ifp); 587138568Ssam if (!ic) { 588138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 589138568Ssam return EINVAL; 590138568Ssam } 591116742Ssam ime = ic->ic_media.ifm_cur; 592116742Ssam /* 593116742Ssam * First, identify the phy mode. 594116742Ssam */ 595116742Ssam switch (IFM_MODE(ime->ifm_media)) { 596116742Ssam case IFM_IEEE80211_11A: 597116742Ssam newphymode = IEEE80211_MODE_11A; 598116742Ssam break; 599116742Ssam case IFM_IEEE80211_11B: 600116742Ssam newphymode = IEEE80211_MODE_11B; 601116742Ssam break; 602116742Ssam case IFM_IEEE80211_11G: 603116742Ssam newphymode = IEEE80211_MODE_11G; 604116742Ssam break; 605124543Sonoe case IFM_IEEE80211_FH: 606124543Sonoe newphymode = IEEE80211_MODE_FH; 607124543Sonoe break; 608116742Ssam case IFM_AUTO: 609116742Ssam newphymode = IEEE80211_MODE_AUTO; 610116742Ssam break; 611116742Ssam default: 612116742Ssam return EINVAL; 613116742Ssam } 614116742Ssam /* 615138568Ssam * Turbo mode is an ``option''. 616138568Ssam * XXX does not apply to AUTO 617116742Ssam */ 618116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 619138568Ssam if (newphymode == IEEE80211_MODE_11A) 620138568Ssam newphymode = IEEE80211_MODE_TURBO_A; 621138568Ssam else if (newphymode == IEEE80211_MODE_11G) 622138568Ssam newphymode = IEEE80211_MODE_TURBO_G; 623138568Ssam else 624116742Ssam return EINVAL; 625116742Ssam } 626116742Ssam /* 627116742Ssam * Validate requested mode is available. 628116742Ssam */ 629116742Ssam if ((ic->ic_modecaps & (1<<newphymode)) == 0) 630116742Ssam return EINVAL; 631116742Ssam 632116742Ssam /* 633116742Ssam * Next, the fixed/variable rate. 634116742Ssam */ 635116742Ssam i = -1; 636116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 637116742Ssam /* 638116742Ssam * Convert media subtype to rate. 639116742Ssam */ 640116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 641116742Ssam if (newrate == 0) 642116742Ssam return EINVAL; 643116742Ssam /* 644116742Ssam * Check the rate table for the specified/current phy. 645116742Ssam */ 646116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 647116742Ssam /* 648116742Ssam * In autoselect mode search for the rate. 649116742Ssam */ 650116742Ssam for (j = IEEE80211_MODE_11A; 651116742Ssam j < IEEE80211_MODE_MAX; j++) { 652116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 653116742Ssam continue; 654116742Ssam i = findrate(ic, j, newrate); 655116742Ssam if (i != -1) { 656116742Ssam /* lock mode too */ 657116742Ssam newphymode = j; 658116742Ssam break; 659116742Ssam } 660116742Ssam } 661116742Ssam } else { 662116742Ssam i = findrate(ic, newphymode, newrate); 663116742Ssam } 664116742Ssam if (i == -1) /* mode/rate mismatch */ 665116742Ssam return EINVAL; 666116742Ssam } 667116742Ssam /* NB: defer rate setting to later */ 668116742Ssam 669116742Ssam /* 670116742Ssam * Deduce new operating mode but don't install it just yet. 671116742Ssam */ 672116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 673116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 674116742Ssam newopmode = IEEE80211_M_AHDEMO; 675116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 676116742Ssam newopmode = IEEE80211_M_HOSTAP; 677116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 678116742Ssam newopmode = IEEE80211_M_IBSS; 679117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 680117817Ssam newopmode = IEEE80211_M_MONITOR; 681116742Ssam else 682116742Ssam newopmode = IEEE80211_M_STA; 683116742Ssam 684116742Ssam /* 685116742Ssam * Autoselect doesn't make sense when operating as an AP. 686116742Ssam * If no phy mode has been selected, pick one and lock it 687116742Ssam * down so rate tables can be used in forming beacon frames 688116742Ssam * and the like. 689116742Ssam */ 690116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 691116742Ssam newphymode == IEEE80211_MODE_AUTO) { 692116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 693116742Ssam if (ic->ic_modecaps & (1<<j)) { 694116742Ssam newphymode = j; 695116742Ssam break; 696116742Ssam } 697116742Ssam } 698116742Ssam 699116742Ssam /* 700116742Ssam * Handle phy mode change. 701116742Ssam */ 702116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 703116742Ssam error = ieee80211_setmode(ic, newphymode); 704116742Ssam if (error != 0) 705116742Ssam return error; 706116742Ssam error = ENETRESET; 707116742Ssam } 708116742Ssam 709116742Ssam /* 710116742Ssam * Committed to changes, install the rate setting. 711116742Ssam */ 712116742Ssam if (ic->ic_fixed_rate != i) { 713116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 714116742Ssam error = ENETRESET; 715116742Ssam } 716116742Ssam 717116742Ssam /* 718116742Ssam * Handle operating mode change. 719116742Ssam */ 720116742Ssam if (ic->ic_opmode != newopmode) { 721116742Ssam ic->ic_opmode = newopmode; 722116742Ssam switch (newopmode) { 723116742Ssam case IEEE80211_M_AHDEMO: 724116742Ssam case IEEE80211_M_HOSTAP: 725116742Ssam case IEEE80211_M_STA: 726117817Ssam case IEEE80211_M_MONITOR: 727116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 728116742Ssam break; 729116742Ssam case IEEE80211_M_IBSS: 730116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 731116742Ssam break; 732116742Ssam } 733138568Ssam /* 734138568Ssam * Yech, slot time may change depending on the 735138568Ssam * operating mode so reset it to be sure everything 736138568Ssam * is setup appropriately. 737138568Ssam */ 738138568Ssam ieee80211_reset_erp(ic); 739138568Ssam ieee80211_wme_initparams(ic); /* after opmode change */ 740116742Ssam error = ENETRESET; 741116742Ssam } 742116742Ssam#ifdef notdef 743116742Ssam if (error == 0) 744116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 745116742Ssam#endif 746116742Ssam return error; 747116742Ssam} 748116742Ssam 749116742Ssamvoid 750116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 751116742Ssam{ 752138568Ssam struct ieee80211com *ic; 753165569Ssam const struct ieee80211_rateset *rs; 754116742Ssam 755138568Ssam ic = ieee80211_find_instance(ifp); 756138568Ssam if (!ic) { 757138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 758138568Ssam return; 759138568Ssam } 760116742Ssam imr->ifm_status = IFM_AVALID; 761116742Ssam imr->ifm_active = IFM_IEEE80211; 762138568Ssam if (ic->ic_state == IEEE80211_S_RUN) 763116742Ssam imr->ifm_status |= IFM_ACTIVE; 764138568Ssam /* 765138568Ssam * Calculate a current rate if possible. 766138568Ssam */ 767148290Ssam if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 768138568Ssam /* 769138568Ssam * A fixed rate is set, report that. 770138568Ssam */ 771138568Ssam rs = &ic->ic_sup_rates[ic->ic_curmode]; 772138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 773138568Ssam rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); 774138568Ssam } else if (ic->ic_opmode == IEEE80211_M_STA) { 775138568Ssam /* 776138568Ssam * In station mode report the current transmit rate. 777138568Ssam */ 778138568Ssam rs = &ic->ic_bss->ni_rates; 779138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 780138568Ssam rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); 781128966Sandre } else 782138568Ssam imr->ifm_active |= IFM_AUTO; 783116742Ssam switch (ic->ic_opmode) { 784116742Ssam case IEEE80211_M_STA: 785116742Ssam break; 786116742Ssam case IEEE80211_M_IBSS: 787116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 788116742Ssam break; 789116742Ssam case IEEE80211_M_AHDEMO: 790116742Ssam /* should not come here */ 791116742Ssam break; 792116742Ssam case IEEE80211_M_HOSTAP: 793116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 794116742Ssam break; 795117817Ssam case IEEE80211_M_MONITOR: 796117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 797117817Ssam break; 798116742Ssam } 799116742Ssam switch (ic->ic_curmode) { 800116742Ssam case IEEE80211_MODE_11A: 801124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A; 802116742Ssam break; 803116742Ssam case IEEE80211_MODE_11B: 804124543Sonoe imr->ifm_active |= IFM_IEEE80211_11B; 805116742Ssam break; 806116742Ssam case IEEE80211_MODE_11G: 807124543Sonoe imr->ifm_active |= IFM_IEEE80211_11G; 808116742Ssam break; 809124543Sonoe case IEEE80211_MODE_FH: 810124543Sonoe imr->ifm_active |= IFM_IEEE80211_FH; 811124543Sonoe break; 812138568Ssam case IEEE80211_MODE_TURBO_A: 813124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A 814116742Ssam | IFM_IEEE80211_TURBO; 815116742Ssam break; 816138568Ssam case IEEE80211_MODE_TURBO_G: 817138568Ssam imr->ifm_active |= IFM_IEEE80211_11G 818138568Ssam | IFM_IEEE80211_TURBO; 819138568Ssam break; 820116742Ssam } 821116742Ssam} 822116742Ssam 823116742Ssamvoid 824138568Ssamieee80211_watchdog(struct ieee80211com *ic) 825116742Ssam{ 826138568Ssam struct ieee80211_node_table *nt; 827138568Ssam int need_inact_timer = 0; 828116742Ssam 829138568Ssam if (ic->ic_state != IEEE80211_S_INIT) { 830138568Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 831138568Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 832138568Ssam nt = &ic->ic_scan; 833138568Ssam if (nt->nt_inact_timer) { 834138568Ssam if (--nt->nt_inact_timer == 0) 835138568Ssam nt->nt_timeout(nt); 836138568Ssam need_inact_timer += nt->nt_inact_timer; 837138568Ssam } 838140753Ssam nt = &ic->ic_sta; 839140753Ssam if (nt->nt_inact_timer) { 840138568Ssam if (--nt->nt_inact_timer == 0) 841138568Ssam nt->nt_timeout(nt); 842138568Ssam need_inact_timer += nt->nt_inact_timer; 843138568Ssam } 844116742Ssam } 845138568Ssam if (ic->ic_mgt_timer != 0 || need_inact_timer) 846138568Ssam ic->ic_ifp->if_timer = 1; 847116742Ssam} 848116742Ssam 849116742Ssam/* 850116742Ssam * Set the current phy mode and recalculate the active channel 851116742Ssam * set based on the available channels for this mode. Also 852116742Ssam * select a new default/current channel if the current one is 853116742Ssam * inappropriate for this mode. 854116742Ssam */ 855116742Ssamint 856116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 857116742Ssam{ 858116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 859116742Ssam static const u_int chanflags[] = { 860116742Ssam 0, /* IEEE80211_MODE_AUTO */ 861116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 862116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 863116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 864124543Sonoe IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 865138568Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ 866138568Ssam IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 867116742Ssam }; 868116742Ssam struct ieee80211_channel *c; 869116742Ssam u_int modeflags; 870116742Ssam int i; 871116742Ssam 872116742Ssam /* validate new mode */ 873116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) { 874138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 875138568Ssam "%s: mode %u not supported (caps 0x%x)\n", 876138568Ssam __func__, mode, ic->ic_modecaps); 877116742Ssam return EINVAL; 878116742Ssam } 879116742Ssam 880116742Ssam /* 881116742Ssam * Verify at least one channel is present in the available 882116742Ssam * channel list before committing to the new mode. 883116742Ssam */ 884127760Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); 885116742Ssam modeflags = chanflags[mode]; 886116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 887116742Ssam c = &ic->ic_channels[i]; 888153350Ssam if (c->ic_flags == 0) 889153350Ssam continue; 890116742Ssam if (mode == IEEE80211_MODE_AUTO) { 891153863Ssam /* ignore static turbo channels for autoselect */ 892153863Ssam if (!IEEE80211_IS_CHAN_T(c)) 893116742Ssam break; 894116742Ssam } else { 895116742Ssam if ((c->ic_flags & modeflags) == modeflags) 896116742Ssam break; 897116742Ssam } 898116742Ssam } 899116742Ssam if (i > IEEE80211_CHAN_MAX) { 900138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 901138568Ssam "%s: no channels found for mode %u\n", __func__, mode); 902116742Ssam return EINVAL; 903116742Ssam } 904116742Ssam 905116742Ssam /* 906116742Ssam * Calculate the active channel set. 907116742Ssam */ 908116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 909116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 910116742Ssam c = &ic->ic_channels[i]; 911153350Ssam if (c->ic_flags == 0) 912153350Ssam continue; 913116742Ssam if (mode == IEEE80211_MODE_AUTO) { 914153863Ssam /* take anything but static turbo channels */ 915153863Ssam if (!IEEE80211_IS_CHAN_T(c)) 916116742Ssam setbit(ic->ic_chan_active, i); 917116742Ssam } else { 918116742Ssam if ((c->ic_flags & modeflags) == modeflags) 919116742Ssam setbit(ic->ic_chan_active, i); 920116742Ssam } 921116742Ssam } 922116742Ssam /* 923116742Ssam * If no current/default channel is setup or the current 924116742Ssam * channel is wrong for the mode then pick the first 925116742Ssam * available channel from the active list. This is likely 926116742Ssam * not the right one. 927116742Ssam */ 928116742Ssam if (ic->ic_ibss_chan == NULL || 929116742Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 930116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 931116742Ssam if (isset(ic->ic_chan_active, i)) { 932116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 933116742Ssam break; 934116742Ssam } 935138568Ssam KASSERT(ic->ic_ibss_chan != NULL && 936138568Ssam isset(ic->ic_chan_active, 937138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), 938138568Ssam ("Bad IBSS channel %u", 939138568Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); 940116742Ssam } 941138568Ssam /* 942138568Ssam * If the desired channel is set but no longer valid then reset it. 943138568Ssam */ 944138568Ssam if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 945138568Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) 946138568Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 947116742Ssam 948116742Ssam /* 949138568Ssam * Do mode-specific rate setup. 950116742Ssam */ 951116742Ssam if (mode == IEEE80211_MODE_11G) { 952138568Ssam /* 953138568Ssam * Use a mixed 11b/11g rate set. 954138568Ssam */ 955116742Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 956116742Ssam IEEE80211_MODE_11G); 957138568Ssam } else if (mode == IEEE80211_MODE_11B) { 958138568Ssam /* 959138568Ssam * Force pure 11b rate set. 960138568Ssam */ 961138568Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 962138568Ssam IEEE80211_MODE_11B); 963116742Ssam } 964138568Ssam /* 965138568Ssam * Setup an initial rate set according to the 966138568Ssam * current/default channel selected above. This 967138568Ssam * will be changed when scanning but must exist 968138568Ssam * now so driver have a consistent state of ic_ibss_chan. 969138568Ssam */ 970138568Ssam if (ic->ic_bss) /* NB: can be called before lateattach */ 971138568Ssam ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; 972116742Ssam 973116742Ssam ic->ic_curmode = mode; 974138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 975138568Ssam ieee80211_wme_initparams(ic); /* reset WME stat */ 976138568Ssam 977116742Ssam return 0; 978116742Ssam#undef N 979116742Ssam} 980116742Ssam 981116742Ssam/* 982116742Ssam * Return the phy mode for with the specified channel so the 983138568Ssam * caller can select a rate set. This is problematic for channels 984138568Ssam * where multiple operating modes are possible (e.g. 11g+11b). 985138568Ssam * In those cases we defer to the current operating mode when set. 986116742Ssam */ 987116742Ssamenum ieee80211_phymode 988165569Ssamieee80211_chan2mode(struct ieee80211com *ic, const struct ieee80211_channel *chan) 989116742Ssam{ 990153350Ssam if (IEEE80211_IS_CHAN_T(chan)) { 991153350Ssam return IEEE80211_MODE_TURBO_A; 992153350Ssam } else if (IEEE80211_IS_CHAN_5GHZ(chan)) { 993116742Ssam return IEEE80211_MODE_11A; 994138568Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) 995124543Sonoe return IEEE80211_MODE_FH; 996138568Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { 997138568Ssam /* 998138568Ssam * This assumes all 11g channels are also usable 999138568Ssam * for 11b, which is currently true. 1000138568Ssam */ 1001138568Ssam if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) 1002138568Ssam return IEEE80211_MODE_TURBO_G; 1003138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11B) 1004138568Ssam return IEEE80211_MODE_11B; 1005116742Ssam return IEEE80211_MODE_11G; 1006138568Ssam } else 1007116742Ssam return IEEE80211_MODE_11B; 1008116742Ssam} 1009116742Ssam 1010116742Ssam/* 1011116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 1012116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 1013116742Ssam */ 1014116742Ssamint 1015116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1016116742Ssam{ 1017116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1018116742Ssam static const struct { 1019116742Ssam u_int m; /* rate + mode */ 1020116742Ssam u_int r; /* if_media rate */ 1021116742Ssam } rates[] = { 1022124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1023124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1024124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1025124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1026124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1027124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1028124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1029124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1030124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1031124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1032124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1033124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1034124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1035124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1036124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1037124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1038124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1039124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1040124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1041124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1042124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1043124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1044124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1045124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1046124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1047124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1048124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1049165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1050165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1051165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1052116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 1053116742Ssam }; 1054116742Ssam u_int mask, i; 1055116742Ssam 1056116742Ssam mask = rate & IEEE80211_RATE_VAL; 1057116742Ssam switch (mode) { 1058116742Ssam case IEEE80211_MODE_11A: 1059138568Ssam case IEEE80211_MODE_TURBO_A: 1060124543Sonoe mask |= IFM_IEEE80211_11A; 1061116742Ssam break; 1062116742Ssam case IEEE80211_MODE_11B: 1063124543Sonoe mask |= IFM_IEEE80211_11B; 1064116742Ssam break; 1065124543Sonoe case IEEE80211_MODE_FH: 1066124543Sonoe mask |= IFM_IEEE80211_FH; 1067124543Sonoe break; 1068116742Ssam case IEEE80211_MODE_AUTO: 1069116742Ssam /* NB: ic may be NULL for some drivers */ 1070116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 1071124543Sonoe mask |= IFM_IEEE80211_FH; 1072124543Sonoe break; 1073116742Ssam } 1074116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1075116742Ssam /* fall thru... */ 1076116742Ssam case IEEE80211_MODE_11G: 1077138568Ssam case IEEE80211_MODE_TURBO_G: 1078124543Sonoe mask |= IFM_IEEE80211_11G; 1079116742Ssam break; 1080116742Ssam } 1081116742Ssam for (i = 0; i < N(rates); i++) 1082116742Ssam if (rates[i].m == mask) 1083116742Ssam return rates[i].r; 1084116742Ssam return IFM_AUTO; 1085116742Ssam#undef N 1086116742Ssam} 1087116742Ssam 1088116742Ssamint 1089116742Ssamieee80211_media2rate(int mword) 1090116742Ssam{ 1091116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1092116742Ssam static const int ieeerates[] = { 1093116742Ssam -1, /* IFM_AUTO */ 1094116742Ssam 0, /* IFM_MANUAL */ 1095116742Ssam 0, /* IFM_NONE */ 1096116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1097116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1098116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1099116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1100116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1101116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1102116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1103116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1104116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1105116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1106116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1107116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1108116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1109116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1110116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1111116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1112165569Ssam 0, /* IFM_IEEE80211_DS354k */ 1113165569Ssam 0, /* IFM_IEEE80211_DS512k */ 1114165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 1115165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 1116165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 1117116742Ssam }; 1118116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1119116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1120116742Ssam#undef N 1121116742Ssam} 1122