ieee80211.c revision 173861
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 173861 2007-11-23 05:57:20Z 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 */ 56170530Ssam "sturboA", /* IEEE80211_MODE_STURBO_A */ 57170530Ssam "11na", /* IEEE80211_MODE_11NA */ 58170530Ssam "11ng", /* IEEE80211_MODE_11NG */ 59116742Ssam}; 60116742Ssam 61164645Ssam/* 62164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units). 63164645Ssam */ 64164645Ssam#define B(r) ((r) | IEEE80211_RATE_BASIC) 65164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a = 66164645Ssam { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; 67165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half = 68165569Ssam { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; 69165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter = 70165569Ssam { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; 71164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b = 72164645Ssam { 4, { B(2), B(4), B(11), B(22) } }; 73164645Ssam/* NB: OFDM rates are handled specially based on mode */ 74164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g = 75164645Ssam { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; 76164645Ssam#undef B 77164645Ssam 78170530Ssamstatic int media_status(enum ieee80211_opmode , 79170530Ssam const struct ieee80211_channel *); 80170530Ssam 81138568Ssam/* list of all instances */ 82138568SsamSLIST_HEAD(ieee80211_list, ieee80211com); 83138568Ssamstatic struct ieee80211_list ieee80211_list = 84138568Ssam SLIST_HEAD_INITIALIZER(ieee80211_list); 85170530Ssamstatic uint8_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; 94170530Ssam uint8_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{ 146165569Ssam#define DEFAULTRATES(m, def) do { \ 147167468Ssam if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \ 148165574Ssam ic->ic_sup_rates[m] = def; \ 149165569Ssam} while (0) 150116742Ssam struct ieee80211_channel *c; 151116742Ssam int i; 152116742Ssam 153170530Ssam KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX, 154170530Ssam ("invalid number of channels specified: %u", ic->ic_nchans)); 155116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 156167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); 157170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 158116742Ssam c = &ic->ic_channels[i]; 159170530Ssam KASSERT(c->ic_flags != 0, ("channel with no flags")); 160170530Ssam KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX, 161170530Ssam ("channel with bogus ieee number %u", c->ic_ieee)); 162170530Ssam setbit(ic->ic_chan_avail, c->ic_ieee); 163170530Ssam /* 164170530Ssam * Identify mode capabilities. 165170530Ssam */ 166170530Ssam if (IEEE80211_IS_CHAN_A(c)) 167170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11A); 168170530Ssam if (IEEE80211_IS_CHAN_B(c)) 169170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11B); 170170530Ssam if (IEEE80211_IS_CHAN_ANYG(c)) 171170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11G); 172170530Ssam if (IEEE80211_IS_CHAN_FHSS(c)) 173170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_FH); 174170530Ssam if (IEEE80211_IS_CHAN_108A(c)) 175170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); 176170530Ssam if (IEEE80211_IS_CHAN_108G(c)) 177170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); 178170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 179170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); 180170530Ssam if (IEEE80211_IS_CHAN_HTA(c)) 181170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); 182170530Ssam if (IEEE80211_IS_CHAN_HTG(c)) 183170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); 184116742Ssam } 185170530Ssam /* initialize candidate channels to all available */ 186170530Ssam memcpy(ic->ic_chan_active, ic->ic_chan_avail, 187170530Ssam sizeof(ic->ic_chan_avail)); 188164645Ssam 189170530Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 190170530Ssam ic->ic_bsschan = IEEE80211_CHAN_ANYC; 191172233Ssam ic->ic_prevchan = NULL; 192170530Ssam /* arbitrarily pick the first channel */ 193170530Ssam ic->ic_curchan = &ic->ic_channels[0]; 194170530Ssam 195164645Ssam /* fillin well-known rate sets if driver has not specified */ 196165569Ssam DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); 197165569Ssam DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); 198165569Ssam DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); 199165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); 200165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); 201165569Ssam 202165569Ssam /* 203165569Ssam * Set auto mode to reset active channel state and any desired channel. 204165569Ssam */ 205165569Ssam (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); 206165569Ssam#undef DEFAULTRATES 207165569Ssam} 208165569Ssam 209165569Ssamvoid 210165569Ssamieee80211_ifattach(struct ieee80211com *ic) 211165569Ssam{ 212165569Ssam struct ifnet *ifp = ic->ic_ifp; 213165569Ssam 214165569Ssam ether_ifattach(ifp, ic->ic_myaddr); 215165569Ssam ifp->if_output = ieee80211_output; 216165569Ssam 217165569Ssam bpfattach2(ifp, DLT_IEEE802_11, 218165569Ssam sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); 219165569Ssam 220170530Ssam /* override the 802.3 setting */ 221170530Ssam ifp->if_hdrlen = ic->ic_headroom 222170530Ssam + sizeof(struct ieee80211_qosframe_addr4) 223170530Ssam + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN 224170530Ssam + IEEE80211_WEP_EXTIVLEN; 225170530Ssam /* XXX no way to recalculate on ifdetach */ 226170530Ssam if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { 227170530Ssam /* XXX sanity check... */ 228170530Ssam max_linkhdr = ALIGN(ifp->if_hdrlen); 229170530Ssam max_hdr = max_linkhdr + max_protohdr; 230170530Ssam max_datalen = MHLEN - max_hdr; 231170530Ssam } 232165569Ssam 233165569Ssam /* 234165569Ssam * Fill in 802.11 available channel set, mark all 235165569Ssam * available channels as active, and pick a default 236165569Ssam * channel if not already specified. 237165569Ssam */ 238165569Ssam ieee80211_chan_init(ic); 239170530Ssam 240170530Ssam if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */ 241170530Ssam ic->ic_flags |= IEEE80211_F_BGSCAN; 242139526Ssam#if 0 243170530Ssam /* XXX not until WME+WPA issues resolved */ 244170530Ssam if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */ 245138568Ssam ic->ic_flags |= IEEE80211_F_WME; 246139526Ssam#endif 247153421Ssam if (ic->ic_caps & IEEE80211_C_BURST) 248153421Ssam ic->ic_flags |= IEEE80211_F_BURST; 249170530Ssam ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */ 250116742Ssam 251155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 252155688Ssam ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 253138568Ssam ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; 254170530Ssam IEEE80211_LOCK_INIT(ic, "ieee80211com"); 255138568Ssam IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); 256116742Ssam 257155688Ssam ic->ic_lintval = ic->ic_bintval; 258138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 259138568Ssam 260170530Ssam ieee80211_crypto_attach(ic); 261138568Ssam ieee80211_node_attach(ic); 262170530Ssam ieee80211_power_attach(ic); 263138568Ssam ieee80211_proto_attach(ic); 264170530Ssam ieee80211_ht_attach(ic); 265170530Ssam ieee80211_scan_attach(ic); 266138568Ssam 267138568Ssam ieee80211_add_vap(ic); 268138568Ssam 269138568Ssam ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ 270140915Ssam 271140915Ssam /* 272140915Ssam * Install a default reset method for the ioctl support. 273140915Ssam * The driver is expected to fill this in before calling us. 274140915Ssam */ 275140915Ssam if (ic->ic_reset == NULL) 276140915Ssam ic->ic_reset = ieee80211_default_reset; 277160690Ssam 278160690Ssam KASSERT(ifp->if_spare2 == NULL, ("oops, hosed")); 279160690Ssam ifp->if_spare2 = ic; /* XXX temp backpointer */ 280116742Ssam} 281116742Ssam 282116742Ssamvoid 283138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 284116742Ssam{ 285138568Ssam struct ifnet *ifp = ic->ic_ifp; 286116742Ssam 287138568Ssam ieee80211_remove_vap(ic); 288138568Ssam 289138568Ssam ieee80211_sysctl_detach(ic); 290170530Ssam ieee80211_scan_detach(ic); 291170530Ssam ieee80211_ht_detach(ic); 292166012Ssam /* NB: must be called before ieee80211_node_detach */ 293138568Ssam ieee80211_proto_detach(ic); 294138568Ssam ieee80211_crypto_detach(ic); 295170530Ssam ieee80211_power_detach(ic); 296138568Ssam ieee80211_node_detach(ic); 297116742Ssam ifmedia_removeall(&ic->ic_media); 298138568Ssam 299170530Ssam IEEE80211_LOCK_DESTROY(ic); 300138568Ssam IEEE80211_BEACON_LOCK_DESTROY(ic); 301138568Ssam 302116742Ssam bpfdetach(ifp); 303116742Ssam ether_ifdetach(ifp); 304116742Ssam} 305116742Ssam 306166012Ssamstatic __inline int 307166012Ssammapgsm(u_int freq, u_int flags) 308166012Ssam{ 309166012Ssam freq *= 10; 310166012Ssam if (flags & IEEE80211_CHAN_QUARTER) 311166012Ssam freq += 5; 312166012Ssam else if (flags & IEEE80211_CHAN_HALF) 313166012Ssam freq += 10; 314166012Ssam else 315166012Ssam freq += 20; 316166012Ssam /* NB: there is no 907/20 wide but leave room */ 317166012Ssam return (freq - 906*10) / 5; 318166012Ssam} 319166012Ssam 320166012Ssamstatic __inline int 321166012Ssammappsb(u_int freq, u_int flags) 322166012Ssam{ 323166012Ssam return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 324166012Ssam} 325166012Ssam 326116742Ssam/* 327116742Ssam * Convert MHz frequency to IEEE channel number. 328116742Ssam */ 329152450Ssamint 330116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 331116742Ssam{ 332167430Ssam#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) 333166012Ssam if (flags & IEEE80211_CHAN_GSM) 334166012Ssam return mapgsm(freq, flags); 335116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 336116742Ssam if (freq == 2484) 337116742Ssam return 14; 338116742Ssam if (freq < 2484) 339152450Ssam return ((int) freq - 2407) / 5; 340116742Ssam else 341116742Ssam return 15 + ((freq - 2512) / 20); 342116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 343165569Ssam if (freq <= 5000) { 344170530Ssam /* XXX check regdomain? */ 345167430Ssam if (IS_FREQ_IN_PSB(freq)) 346166012Ssam return mappsb(freq, flags); 347152450Ssam return (freq - 4000) / 5; 348165569Ssam } else 349152450Ssam return (freq - 5000) / 5; 350116742Ssam } else { /* either, guess */ 351116742Ssam if (freq == 2484) 352116742Ssam return 14; 353166012Ssam if (freq < 2484) { 354166012Ssam if (907 <= freq && freq <= 922) 355166012Ssam return mapgsm(freq, flags); 356152450Ssam return ((int) freq - 2407) / 5; 357166012Ssam } 358152450Ssam if (freq < 5000) { 359167430Ssam if (IS_FREQ_IN_PSB(freq)) 360166012Ssam return mappsb(freq, flags); 361165569Ssam else if (freq > 4900) 362152450Ssam return (freq - 4000) / 5; 363152450Ssam else 364152450Ssam return 15 + ((freq - 2512) / 20); 365152450Ssam } 366116742Ssam return (freq - 5000) / 5; 367116742Ssam } 368167430Ssam#undef IS_FREQ_IN_PSB 369116742Ssam} 370116742Ssam 371116742Ssam/* 372116742Ssam * Convert channel to IEEE channel number. 373116742Ssam */ 374152450Ssamint 375165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) 376116742Ssam{ 377170530Ssam if (c == NULL) { 378138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 379117039Ssam return 0; /* XXX */ 380116742Ssam } 381170530Ssam return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); 382116742Ssam} 383116742Ssam 384116742Ssam/* 385116742Ssam * Convert IEEE channel number to MHz frequency. 386116742Ssam */ 387116742Ssamu_int 388116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 389116742Ssam{ 390166012Ssam if (flags & IEEE80211_CHAN_GSM) 391166012Ssam return 907 + 5 * (chan / 10); 392116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 393116742Ssam if (chan == 14) 394116742Ssam return 2484; 395116742Ssam if (chan < 14) 396116742Ssam return 2407 + chan*5; 397116742Ssam else 398116742Ssam return 2512 + ((chan-15)*20); 399116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 400165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 401165569Ssam chan -= 37; 402165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 403165569Ssam } 404116742Ssam return 5000 + (chan*5); 405116742Ssam } else { /* either, guess */ 406166012Ssam /* XXX can't distinguish PSB+GSM channels */ 407116742Ssam if (chan == 14) 408116742Ssam return 2484; 409116742Ssam if (chan < 14) /* 0-13 */ 410116742Ssam return 2407 + chan*5; 411116742Ssam if (chan < 27) /* 15-26 */ 412116742Ssam return 2512 + ((chan-15)*20); 413116742Ssam return 5000 + (chan*5); 414116742Ssam } 415116742Ssam} 416116742Ssam 417116742Ssam/* 418170530Ssam * Locate a channel given a frequency+flags. We cache 419170530Ssam * the previous lookup to optimize swithing between two 420170530Ssam * channels--as happens with dynamic turbo. 421170530Ssam */ 422170530Ssamstruct ieee80211_channel * 423170530Ssamieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) 424170530Ssam{ 425170530Ssam struct ieee80211_channel *c; 426170530Ssam int i; 427170530Ssam 428170530Ssam flags &= IEEE80211_CHAN_ALLTURBO; 429170530Ssam c = ic->ic_prevchan; 430170530Ssam if (c != NULL && c->ic_freq == freq && 431170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 432170530Ssam return c; 433170530Ssam /* brute force search */ 434170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 435170530Ssam c = &ic->ic_channels[i]; 436170530Ssam if (c->ic_freq == freq && 437170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 438170530Ssam return c; 439170530Ssam } 440170530Ssam return NULL; 441170530Ssam} 442170530Ssam 443173861Ssam/* 444173861Ssam * Locate a channel given a channel number+flags. We cache 445173861Ssam * the previous lookup to optimize switching between two 446173861Ssam * channels--as happens with dynamic turbo. 447173861Ssam */ 448173861Ssamstruct ieee80211_channel * 449173861Ssamieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) 450173861Ssam{ 451173861Ssam struct ieee80211_channel *c; 452173861Ssam int i; 453173861Ssam 454173861Ssam flags &= IEEE80211_CHAN_ALLTURBO; 455173861Ssam c = ic->ic_prevchan; 456173861Ssam if (c != NULL && c->ic_ieee == ieee && 457173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 458173861Ssam return c; 459173861Ssam /* brute force search */ 460173861Ssam for (i = 0; i < ic->ic_nchans; i++) { 461173861Ssam c = &ic->ic_channels[i]; 462173861Ssam if (c->ic_ieee == ieee && 463173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 464173861Ssam return c; 465173861Ssam } 466173861Ssam return NULL; 467173861Ssam} 468173861Ssam 469170530Ssamstatic void 470170530Ssamaddmedia(struct ieee80211com *ic, int mode, int mword) 471170530Ssam{ 472170530Ssam#define TURBO(m) ((m) | IFM_IEEE80211_TURBO) 473170530Ssam#define ADD(_ic, _s, _o) \ 474170530Ssam ifmedia_add(&(_ic)->ic_media, \ 475170530Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 476170530Ssam static const u_int mopts[IEEE80211_MODE_MAX] = { 477170530Ssam IFM_AUTO, /* IEEE80211_MODE_AUTO */ 478170530Ssam IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */ 479170530Ssam IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */ 480170530Ssam IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */ 481170530Ssam IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */ 482170530Ssam TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */ 483170530Ssam TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */ 484170530Ssam TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */ 485170530Ssam IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */ 486170530Ssam IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */ 487170530Ssam }; 488170530Ssam u_int mopt; 489170530Ssam 490170530Ssam KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); 491170530Ssam mopt = mopts[mode]; 492170530Ssam KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO, 493170530Ssam ("no media mapping for mode %u", mode)); 494170530Ssam 495170530Ssam ADD(ic, mword, mopt); /* e.g. 11a auto */ 496170530Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 497170530Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 498170530Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 499170530Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 500170530Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 501170530Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 502170530Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 503170530Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 504170530Ssam#undef ADD 505170530Ssam#undef TURBO 506170530Ssam} 507170530Ssam 508170530Ssam/* 509116742Ssam * Setup the media data structures according to the channel and 510116742Ssam * rate tables. This must be called by the driver after 511116742Ssam * ieee80211_attach and before most anything else. 512116742Ssam */ 513116742Ssamvoid 514138568Ssamieee80211_media_init(struct ieee80211com *ic, 515116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 516116742Ssam{ 517138568Ssam struct ifnet *ifp = ic->ic_ifp; 518170530Ssam int i, j, mode, rate, maxrate, mword, r; 519170530Ssam const struct ieee80211_rateset *rs; 520116742Ssam struct ieee80211_rateset allrates; 521116742Ssam 522165569Ssam /* NB: this works because the structure is initialized to zero */ 523165569Ssam if (LIST_EMPTY(&ic->ic_media.ifm_list)) { 524165569Ssam /* 525165569Ssam * Do late attach work that must wait for any subclass 526165569Ssam * (i.e. driver) work such as overriding methods. 527165569Ssam */ 528165569Ssam ieee80211_node_lateattach(ic); 529165569Ssam } else { 530165569Ssam /* 531165569Ssam * We are re-initializing the channel list; clear 532165569Ssam * the existing media state as the media routines 533165569Ssam * don't suppress duplicates. 534165569Ssam */ 535165569Ssam ifmedia_removeall(&ic->ic_media); 536165569Ssam ieee80211_chan_init(ic); 537165569Ssam } 538170530Ssam ieee80211_power_lateattach(ic); 539118887Ssam 540118887Ssam /* 541116742Ssam * Fill in media characteristics. 542116742Ssam */ 543116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 544116742Ssam maxrate = 0; 545170530Ssam /* 546170530Ssam * Add media for legacy operating modes. 547170530Ssam */ 548116742Ssam memset(&allrates, 0, sizeof(allrates)); 549170530Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { 550167468Ssam if (isclr(ic->ic_modecaps, mode)) 551116742Ssam continue; 552170530Ssam addmedia(ic, mode, IFM_AUTO); 553116742Ssam if (mode == IEEE80211_MODE_AUTO) 554116742Ssam continue; 555116742Ssam rs = &ic->ic_sup_rates[mode]; 556116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 557116742Ssam rate = rs->rs_rates[i]; 558116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 559116742Ssam if (mword == 0) 560116742Ssam continue; 561170530Ssam addmedia(ic, mode, mword); 562116742Ssam /* 563170530Ssam * Add legacy rate to the collection of all rates. 564116742Ssam */ 565116742Ssam r = rate & IEEE80211_RATE_VAL; 566116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 567116742Ssam if (allrates.rs_rates[j] == r) 568116742Ssam break; 569116742Ssam if (j == allrates.rs_nrates) { 570116742Ssam /* unique, add to the set */ 571116742Ssam allrates.rs_rates[j] = r; 572116742Ssam allrates.rs_nrates++; 573116742Ssam } 574116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 575116742Ssam if (rate > maxrate) 576116742Ssam maxrate = rate; 577116742Ssam } 578116742Ssam } 579116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 580116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 581116742Ssam IEEE80211_MODE_AUTO); 582116742Ssam if (mword == 0) 583116742Ssam continue; 584170530Ssam /* NB: remove media options from mword */ 585170530Ssam addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); 586116742Ssam } 587170530Ssam /* 588170530Ssam * Add HT/11n media. Note that we do not have enough 589170530Ssam * bits in the media subtype to express the MCS so we 590170530Ssam * use a "placeholder" media subtype and any fixed MCS 591170530Ssam * must be specified with a different mechanism. 592170530Ssam */ 593170530Ssam for (; mode < IEEE80211_MODE_MAX; mode++) { 594170530Ssam if (isclr(ic->ic_modecaps, mode)) 595170530Ssam continue; 596170530Ssam addmedia(ic, mode, IFM_AUTO); 597170530Ssam addmedia(ic, mode, IFM_IEEE80211_MCS); 598170530Ssam } 599170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 600170530Ssam isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 601170530Ssam addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); 602170530Ssam /* XXX could walk htrates */ 603170530Ssam /* XXX known array size */ 604170530Ssam if (ieee80211_htrates[15] > maxrate) 605170530Ssam maxrate = ieee80211_htrates[15]; 606170530Ssam } 607116742Ssam 608170530Ssam /* NB: strip explicit mode; we're actually in autoselect */ 609170530Ssam ifmedia_set(&ic->ic_media, 610170530Ssam media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK); 611170530Ssam 612116742Ssam if (maxrate) 613116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 614116742Ssam} 615116742Ssam 616165569Ssamconst struct ieee80211_rateset * 617165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 618165569Ssam{ 619166012Ssam if (IEEE80211_IS_CHAN_HALF(c)) 620166012Ssam return &ieee80211_rateset_half; 621166012Ssam if (IEEE80211_IS_CHAN_QUARTER(c)) 622166012Ssam return &ieee80211_rateset_quarter; 623170530Ssam if (IEEE80211_IS_CHAN_HTA(c)) 624170530Ssam return &ic->ic_sup_rates[IEEE80211_MODE_11A]; 625170530Ssam if (IEEE80211_IS_CHAN_HTG(c)) { 626170530Ssam /* XXX does this work for basic rates? */ 627170530Ssam return &ic->ic_sup_rates[IEEE80211_MODE_11G]; 628170530Ssam } 629170530Ssam return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; 630165569Ssam} 631165569Ssam 632138568Ssamvoid 633138568Ssamieee80211_announce(struct ieee80211com *ic) 634138568Ssam{ 635138568Ssam struct ifnet *ifp = ic->ic_ifp; 636138568Ssam int i, mode, rate, mword; 637170530Ssam const struct ieee80211_rateset *rs; 638138568Ssam 639172227Ssam /* NB: skip AUTO since it has no rates */ 640172227Ssam for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { 641167468Ssam if (isclr(ic->ic_modecaps, mode)) 642138568Ssam continue; 643138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 644138568Ssam rs = &ic->ic_sup_rates[mode]; 645138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 646170530Ssam mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 647138568Ssam if (mword == 0) 648138568Ssam continue; 649170530Ssam rate = ieee80211_media2rate(mword); 650138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 651170530Ssam rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 652138568Ssam } 653138568Ssam printf("\n"); 654138568Ssam } 655170530Ssam ieee80211_ht_announce(ic); 656138568Ssam} 657138568Ssam 658170530Ssamvoid 659170530Ssamieee80211_announce_channels(struct ieee80211com *ic) 660116742Ssam{ 661170530Ssam const struct ieee80211_channel *c; 662170530Ssam char type; 663170530Ssam int i, cw; 664170530Ssam 665170530Ssam printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); 666170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 667170530Ssam c = &ic->ic_channels[i]; 668170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 669170530Ssam type = 'S'; 670170530Ssam else if (IEEE80211_IS_CHAN_108A(c)) 671170530Ssam type = 'T'; 672170530Ssam else if (IEEE80211_IS_CHAN_108G(c)) 673170530Ssam type = 'G'; 674170530Ssam else if (IEEE80211_IS_CHAN_HT(c)) 675170530Ssam type = 'n'; 676170530Ssam else if (IEEE80211_IS_CHAN_A(c)) 677170530Ssam type = 'a'; 678170530Ssam else if (IEEE80211_IS_CHAN_ANYG(c)) 679170530Ssam type = 'g'; 680170530Ssam else if (IEEE80211_IS_CHAN_B(c)) 681170530Ssam type = 'b'; 682170530Ssam else 683170530Ssam type = 'f'; 684170530Ssam if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) 685170530Ssam cw = 40; 686170530Ssam else if (IEEE80211_IS_CHAN_HALF(c)) 687170530Ssam cw = 10; 688170530Ssam else if (IEEE80211_IS_CHAN_QUARTER(c)) 689170530Ssam cw = 5; 690170530Ssam else 691170530Ssam cw = 20; 692170530Ssam printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" 693170530Ssam , c->ic_ieee, c->ic_freq, type 694170530Ssam , cw 695170530Ssam , IEEE80211_IS_CHAN_HT40U(c) ? '+' : 696170530Ssam IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' 697170530Ssam , c->ic_maxregpower 698170530Ssam , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 699170530Ssam , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 700170530Ssam ); 701170530Ssam } 702116742Ssam} 703116742Ssam 704116742Ssam/* 705138568Ssam * Find an instance by it's mac address. 706138568Ssam */ 707138568Ssamstruct ieee80211com * 708170530Ssamieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]) 709138568Ssam{ 710138568Ssam struct ieee80211com *ic; 711138568Ssam 712138568Ssam /* XXX lock */ 713138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 714138568Ssam if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) 715138568Ssam return ic; 716138568Ssam return NULL; 717138568Ssam} 718138568Ssam 719138568Ssamstatic struct ieee80211com * 720138568Ssamieee80211_find_instance(struct ifnet *ifp) 721138568Ssam{ 722138568Ssam struct ieee80211com *ic; 723138568Ssam 724138568Ssam /* XXX lock */ 725138568Ssam /* XXX not right for multiple instances but works for now */ 726138568Ssam SLIST_FOREACH(ic, &ieee80211_list, ic_next) 727138568Ssam if (ic->ic_ifp == ifp) 728138568Ssam return ic; 729138568Ssam return NULL; 730138568Ssam} 731138568Ssam 732170530Ssamstatic int 733170530Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 734170530Ssam{ 735170530Ssam#define IEEERATE(_ic,_m,_i) \ 736170530Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 737170530Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 738170530Ssam for (i = 0; i < nrates; i++) 739170530Ssam if (IEEERATE(ic, mode, i) == rate) 740170530Ssam return i; 741170530Ssam return -1; 742170530Ssam#undef IEEERATE 743170530Ssam} 744170530Ssam 745138568Ssam/* 746170530Ssam * Convert a media specification to a rate index and possibly a mode 747170530Ssam * (if the rate is fixed and the mode is specified as ``auto'' then 748170530Ssam * we need to lock down the mode so the index is meanginful). 749170530Ssam */ 750170530Ssamstatic int 751170530Ssamcheckrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 752170530Ssam{ 753170530Ssam 754170530Ssam /* 755170530Ssam * Check the rate table for the specified/current phy. 756170530Ssam */ 757170530Ssam if (mode == IEEE80211_MODE_AUTO) { 758170530Ssam int i; 759170530Ssam /* 760170530Ssam * In autoselect mode search for the rate. 761170530Ssam */ 762170530Ssam for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { 763170530Ssam if (isset(ic->ic_modecaps, i) && 764170530Ssam findrate(ic, i, rate) != -1) 765170530Ssam return 1; 766170530Ssam } 767170530Ssam return 0; 768170530Ssam } else { 769170530Ssam /* 770170530Ssam * Mode is fixed, check for rate. 771170530Ssam */ 772170530Ssam return (findrate(ic, mode, rate) != -1); 773170530Ssam } 774170530Ssam} 775170530Ssam 776170530Ssam/* 777116742Ssam * Handle a media change request. 778116742Ssam */ 779116742Ssamint 780116742Ssamieee80211_media_change(struct ifnet *ifp) 781116742Ssam{ 782138568Ssam struct ieee80211com *ic; 783116742Ssam struct ifmedia_entry *ime; 784116742Ssam enum ieee80211_opmode newopmode; 785116742Ssam enum ieee80211_phymode newphymode; 786170530Ssam int newrate, error = 0; 787116742Ssam 788138568Ssam ic = ieee80211_find_instance(ifp); 789138568Ssam if (!ic) { 790138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 791138568Ssam return EINVAL; 792138568Ssam } 793116742Ssam ime = ic->ic_media.ifm_cur; 794116742Ssam /* 795116742Ssam * First, identify the phy mode. 796116742Ssam */ 797116742Ssam switch (IFM_MODE(ime->ifm_media)) { 798116742Ssam case IFM_IEEE80211_11A: 799116742Ssam newphymode = IEEE80211_MODE_11A; 800116742Ssam break; 801116742Ssam case IFM_IEEE80211_11B: 802116742Ssam newphymode = IEEE80211_MODE_11B; 803116742Ssam break; 804116742Ssam case IFM_IEEE80211_11G: 805116742Ssam newphymode = IEEE80211_MODE_11G; 806116742Ssam break; 807124543Sonoe case IFM_IEEE80211_FH: 808124543Sonoe newphymode = IEEE80211_MODE_FH; 809124543Sonoe break; 810170530Ssam case IFM_IEEE80211_11NA: 811170530Ssam newphymode = IEEE80211_MODE_11NA; 812170530Ssam break; 813170530Ssam case IFM_IEEE80211_11NG: 814170530Ssam newphymode = IEEE80211_MODE_11NG; 815170530Ssam break; 816116742Ssam case IFM_AUTO: 817116742Ssam newphymode = IEEE80211_MODE_AUTO; 818116742Ssam break; 819116742Ssam default: 820116742Ssam return EINVAL; 821116742Ssam } 822116742Ssam /* 823138568Ssam * Turbo mode is an ``option''. 824138568Ssam * XXX does not apply to AUTO 825116742Ssam */ 826116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 827170530Ssam if (newphymode == IEEE80211_MODE_11A) { 828170530Ssam if (ic->ic_flags & IEEE80211_F_TURBOP) 829170530Ssam newphymode = IEEE80211_MODE_TURBO_A; 830170530Ssam else 831170530Ssam newphymode = IEEE80211_MODE_STURBO_A; 832170530Ssam } else if (newphymode == IEEE80211_MODE_11G) 833138568Ssam newphymode = IEEE80211_MODE_TURBO_G; 834138568Ssam else 835116742Ssam return EINVAL; 836116742Ssam } 837170530Ssam /* XXX HT40 +/- */ 838116742Ssam /* 839116742Ssam * Next, the fixed/variable rate. 840116742Ssam */ 841170530Ssam newrate = ic->ic_fixed_rate; 842116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 843116742Ssam /* 844116742Ssam * Convert media subtype to rate. 845116742Ssam */ 846116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 847170530Ssam if (newrate == 0 || !checkrate(ic, newphymode, newrate)) 848116742Ssam return EINVAL; 849170530Ssam } else 850170530Ssam newrate = IEEE80211_FIXED_RATE_NONE; 851116742Ssam 852116742Ssam /* 853116742Ssam * Deduce new operating mode but don't install it just yet. 854116742Ssam */ 855116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 856116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 857116742Ssam newopmode = IEEE80211_M_AHDEMO; 858116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 859116742Ssam newopmode = IEEE80211_M_HOSTAP; 860116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 861116742Ssam newopmode = IEEE80211_M_IBSS; 862117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 863117817Ssam newopmode = IEEE80211_M_MONITOR; 864116742Ssam else 865116742Ssam newopmode = IEEE80211_M_STA; 866116742Ssam 867116742Ssam /* 868116742Ssam * Handle phy mode change. 869116742Ssam */ 870170530Ssam if (ic->ic_des_mode != newphymode) { /* change phy mode */ 871170530Ssam ic->ic_des_mode = newphymode; 872116742Ssam error = ENETRESET; 873116742Ssam } 874116742Ssam 875116742Ssam /* 876116742Ssam * Committed to changes, install the rate setting. 877116742Ssam */ 878170530Ssam if (ic->ic_fixed_rate != newrate) { 879170530Ssam ic->ic_fixed_rate = newrate; /* set fixed tx rate */ 880116742Ssam error = ENETRESET; 881116742Ssam } 882116742Ssam 883116742Ssam /* 884116742Ssam * Handle operating mode change. 885116742Ssam */ 886116742Ssam if (ic->ic_opmode != newopmode) { 887116742Ssam ic->ic_opmode = newopmode; 888116742Ssam switch (newopmode) { 889116742Ssam case IEEE80211_M_AHDEMO: 890116742Ssam case IEEE80211_M_HOSTAP: 891116742Ssam case IEEE80211_M_STA: 892117817Ssam case IEEE80211_M_MONITOR: 893170530Ssam case IEEE80211_M_WDS: 894116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 895116742Ssam break; 896116742Ssam case IEEE80211_M_IBSS: 897116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 898116742Ssam break; 899116742Ssam } 900138568Ssam /* 901138568Ssam * Yech, slot time may change depending on the 902138568Ssam * operating mode so reset it to be sure everything 903138568Ssam * is setup appropriately. 904138568Ssam */ 905138568Ssam ieee80211_reset_erp(ic); 906138568Ssam ieee80211_wme_initparams(ic); /* after opmode change */ 907116742Ssam error = ENETRESET; 908116742Ssam } 909116742Ssam#ifdef notdef 910116742Ssam if (error == 0) 911116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 912116742Ssam#endif 913116742Ssam return error; 914116742Ssam} 915116742Ssam 916170530Ssam/* 917170530Ssam * Common code to calculate the media status word 918170530Ssam * from the operating mode and channel state. 919170530Ssam */ 920170530Ssamstatic int 921170530Ssammedia_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) 922170530Ssam{ 923170530Ssam int status; 924170530Ssam 925170530Ssam status = IFM_IEEE80211; 926170530Ssam switch (opmode) { 927170530Ssam case IEEE80211_M_STA: 928170530Ssam break; 929170530Ssam case IEEE80211_M_IBSS: 930170530Ssam status |= IFM_IEEE80211_ADHOC; 931170530Ssam break; 932170530Ssam case IEEE80211_M_HOSTAP: 933170530Ssam status |= IFM_IEEE80211_HOSTAP; 934170530Ssam break; 935170530Ssam case IEEE80211_M_MONITOR: 936170530Ssam status |= IFM_IEEE80211_MONITOR; 937170530Ssam break; 938170530Ssam case IEEE80211_M_AHDEMO: 939170530Ssam status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; 940170530Ssam break; 941170530Ssam case IEEE80211_M_WDS: 942170530Ssam /* should not come here */ 943170530Ssam break; 944170530Ssam } 945170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) { 946170530Ssam status |= IFM_IEEE80211_11NA; 947170530Ssam } else if (IEEE80211_IS_CHAN_HTG(chan)) { 948170530Ssam status |= IFM_IEEE80211_11NG; 949170530Ssam } else if (IEEE80211_IS_CHAN_A(chan)) { 950170530Ssam status |= IFM_IEEE80211_11A; 951170530Ssam } else if (IEEE80211_IS_CHAN_B(chan)) { 952170530Ssam status |= IFM_IEEE80211_11B; 953170530Ssam } else if (IEEE80211_IS_CHAN_ANYG(chan)) { 954170530Ssam status |= IFM_IEEE80211_11G; 955170530Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) { 956170530Ssam status |= IFM_IEEE80211_FH; 957170530Ssam } 958170530Ssam /* XXX else complain? */ 959170530Ssam 960170530Ssam if (IEEE80211_IS_CHAN_TURBO(chan)) 961170530Ssam status |= IFM_IEEE80211_TURBO; 962170530Ssam 963170530Ssam return status; 964170530Ssam} 965170530Ssam 966116742Ssamvoid 967116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 968116742Ssam{ 969138568Ssam struct ieee80211com *ic; 970170530Ssam enum ieee80211_phymode mode; 971165569Ssam const struct ieee80211_rateset *rs; 972116742Ssam 973138568Ssam ic = ieee80211_find_instance(ifp); 974138568Ssam if (!ic) { 975138568Ssam if_printf(ifp, "%s: no 802.11 instance!\n", __func__); 976138568Ssam return; 977138568Ssam } 978116742Ssam imr->ifm_status = IFM_AVALID; 979170530Ssam /* 980170530Ssam * NB: use the current channel's mode to lock down a xmit 981170530Ssam * rate only when running; otherwise we may have a mismatch 982170530Ssam * in which case the rate will not be convertible. 983170530Ssam */ 984170530Ssam if (ic->ic_state == IEEE80211_S_RUN) { 985116742Ssam imr->ifm_status |= IFM_ACTIVE; 986170530Ssam mode = ieee80211_chan2mode(ic->ic_curchan); 987170530Ssam } else 988170530Ssam mode = IEEE80211_MODE_AUTO; 989170530Ssam imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); 990138568Ssam /* 991138568Ssam * Calculate a current rate if possible. 992138568Ssam */ 993148290Ssam if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 994138568Ssam /* 995138568Ssam * A fixed rate is set, report that. 996138568Ssam */ 997138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 998170530Ssam ic->ic_fixed_rate, mode); 999138568Ssam } else if (ic->ic_opmode == IEEE80211_M_STA) { 1000138568Ssam /* 1001138568Ssam * In station mode report the current transmit rate. 1002170530Ssam * XXX HT rate 1003138568Ssam */ 1004138568Ssam rs = &ic->ic_bss->ni_rates; 1005138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1006170530Ssam rs->rs_rates[ic->ic_bss->ni_txrate], mode); 1007128966Sandre } else 1008138568Ssam imr->ifm_active |= IFM_AUTO; 1009116742Ssam} 1010116742Ssam 1011116742Ssam/* 1012116742Ssam * Set the current phy mode and recalculate the active channel 1013116742Ssam * set based on the available channels for this mode. Also 1014116742Ssam * select a new default/current channel if the current one is 1015116742Ssam * inappropriate for this mode. 1016116742Ssam */ 1017116742Ssamint 1018116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 1019116742Ssam{ 1020116742Ssam /* 1021166012Ssam * Adjust basic rates in 11b/11g supported rate set. 1022166012Ssam * Note that if operating on a hal/quarter rate channel 1023166012Ssam * this is a noop as those rates sets are different 1024166012Ssam * and used instead. 1025116742Ssam */ 1026166012Ssam if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 1027166012Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode); 1028166012Ssam 1029116742Ssam ic->ic_curmode = mode; 1030138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 1031138568Ssam ieee80211_wme_initparams(ic); /* reset WME stat */ 1032138568Ssam 1033116742Ssam return 0; 1034116742Ssam} 1035116742Ssam 1036116742Ssam/* 1037170530Ssam * Return the phy mode for with the specified channel. 1038116742Ssam */ 1039116742Ssamenum ieee80211_phymode 1040170530Ssamieee80211_chan2mode(const struct ieee80211_channel *chan) 1041116742Ssam{ 1042170530Ssam 1043170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) 1044170530Ssam return IEEE80211_MODE_11NA; 1045170530Ssam else if (IEEE80211_IS_CHAN_HTG(chan)) 1046170530Ssam return IEEE80211_MODE_11NG; 1047170530Ssam else if (IEEE80211_IS_CHAN_108G(chan)) 1048170530Ssam return IEEE80211_MODE_TURBO_G; 1049170530Ssam else if (IEEE80211_IS_CHAN_ST(chan)) 1050170530Ssam return IEEE80211_MODE_STURBO_A; 1051170530Ssam else if (IEEE80211_IS_CHAN_TURBO(chan)) 1052153350Ssam return IEEE80211_MODE_TURBO_A; 1053170530Ssam else if (IEEE80211_IS_CHAN_A(chan)) 1054116742Ssam return IEEE80211_MODE_11A; 1055170530Ssam else if (IEEE80211_IS_CHAN_ANYG(chan)) 1056116742Ssam return IEEE80211_MODE_11G; 1057170530Ssam else if (IEEE80211_IS_CHAN_B(chan)) 1058116742Ssam return IEEE80211_MODE_11B; 1059170530Ssam else if (IEEE80211_IS_CHAN_FHSS(chan)) 1060170530Ssam return IEEE80211_MODE_FH; 1061170530Ssam 1062170530Ssam /* NB: should not get here */ 1063170530Ssam printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", 1064170530Ssam __func__, chan->ic_freq, chan->ic_flags); 1065170530Ssam return IEEE80211_MODE_11B; 1066116742Ssam} 1067116742Ssam 1068170530Ssamstruct ratemedia { 1069170530Ssam u_int match; /* rate + mode */ 1070170530Ssam u_int media; /* if_media rate */ 1071170530Ssam}; 1072170530Ssam 1073170530Ssamstatic int 1074170530Ssamfindmedia(const struct ratemedia rates[], int n, u_int match) 1075170530Ssam{ 1076170530Ssam int i; 1077170530Ssam 1078170530Ssam for (i = 0; i < n; i++) 1079170530Ssam if (rates[i].match == match) 1080170530Ssam return rates[i].media; 1081170530Ssam return IFM_AUTO; 1082170530Ssam} 1083170530Ssam 1084116742Ssam/* 1085170530Ssam * Convert IEEE80211 rate value to ifmedia subtype. 1086170530Ssam * Rate is either a legacy rate in units of 0.5Mbps 1087170530Ssam * or an MCS index. 1088116742Ssam */ 1089116742Ssamint 1090116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1091116742Ssam{ 1092116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1093170530Ssam static const struct ratemedia rates[] = { 1094124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1095124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1096124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1097124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1098124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1099124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1100124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1101124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1102124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1103124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1104124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1105124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1106124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1107124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1108124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1109124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1110124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1111124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1112124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1113124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1114124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1115124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1116124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1117124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1118124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1119124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1120124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1121165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1122165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1123165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1124116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 1125116742Ssam }; 1126170530Ssam static const struct ratemedia htrates[] = { 1127170530Ssam { 0, IFM_IEEE80211_MCS }, 1128170530Ssam { 1, IFM_IEEE80211_MCS }, 1129170530Ssam { 2, IFM_IEEE80211_MCS }, 1130170530Ssam { 3, IFM_IEEE80211_MCS }, 1131170530Ssam { 4, IFM_IEEE80211_MCS }, 1132170530Ssam { 5, IFM_IEEE80211_MCS }, 1133170530Ssam { 6, IFM_IEEE80211_MCS }, 1134170530Ssam { 7, IFM_IEEE80211_MCS }, 1135170530Ssam { 8, IFM_IEEE80211_MCS }, 1136170530Ssam { 9, IFM_IEEE80211_MCS }, 1137170530Ssam { 10, IFM_IEEE80211_MCS }, 1138170530Ssam { 11, IFM_IEEE80211_MCS }, 1139170530Ssam { 12, IFM_IEEE80211_MCS }, 1140170530Ssam { 13, IFM_IEEE80211_MCS }, 1141170530Ssam { 14, IFM_IEEE80211_MCS }, 1142170530Ssam { 15, IFM_IEEE80211_MCS }, 1143170530Ssam }; 1144170530Ssam int m; 1145116742Ssam 1146170530Ssam /* 1147170530Ssam * Check 11n rates first for match as an MCS. 1148170530Ssam */ 1149170530Ssam if (mode == IEEE80211_MODE_11NA) { 1150172226Ssam if (rate & IEEE80211_RATE_MCS) { 1151172226Ssam rate &= ~IEEE80211_RATE_MCS; 1152170530Ssam m = findmedia(htrates, N(htrates), rate); 1153170530Ssam if (m != IFM_AUTO) 1154170530Ssam return m | IFM_IEEE80211_11NA; 1155170530Ssam } 1156170530Ssam } else if (mode == IEEE80211_MODE_11NG) { 1157170530Ssam /* NB: 12 is ambiguous, it will be treated as an MCS */ 1158172226Ssam if (rate & IEEE80211_RATE_MCS) { 1159172226Ssam rate &= ~IEEE80211_RATE_MCS; 1160170530Ssam m = findmedia(htrates, N(htrates), rate); 1161170530Ssam if (m != IFM_AUTO) 1162170530Ssam return m | IFM_IEEE80211_11NG; 1163170530Ssam } 1164170530Ssam } 1165170530Ssam rate &= IEEE80211_RATE_VAL; 1166116742Ssam switch (mode) { 1167116742Ssam case IEEE80211_MODE_11A: 1168170530Ssam case IEEE80211_MODE_11NA: 1169138568Ssam case IEEE80211_MODE_TURBO_A: 1170170530Ssam case IEEE80211_MODE_STURBO_A: 1171170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); 1172116742Ssam case IEEE80211_MODE_11B: 1173170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); 1174124543Sonoe case IEEE80211_MODE_FH: 1175170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); 1176116742Ssam case IEEE80211_MODE_AUTO: 1177116742Ssam /* NB: ic may be NULL for some drivers */ 1178170530Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) 1179170530Ssam return findmedia(rates, N(rates), 1180170530Ssam rate | IFM_IEEE80211_FH); 1181116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1182116742Ssam /* fall thru... */ 1183116742Ssam case IEEE80211_MODE_11G: 1184170530Ssam case IEEE80211_MODE_11NG: 1185138568Ssam case IEEE80211_MODE_TURBO_G: 1186170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); 1187116742Ssam } 1188116742Ssam return IFM_AUTO; 1189116742Ssam#undef N 1190116742Ssam} 1191116742Ssam 1192116742Ssamint 1193116742Ssamieee80211_media2rate(int mword) 1194116742Ssam{ 1195116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1196116742Ssam static const int ieeerates[] = { 1197116742Ssam -1, /* IFM_AUTO */ 1198116742Ssam 0, /* IFM_MANUAL */ 1199116742Ssam 0, /* IFM_NONE */ 1200116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1201116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1202116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1203116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1204116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1205116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1206116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1207116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1208116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1209116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1210116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1211116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1212116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1213116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1214116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1215116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1216165569Ssam 0, /* IFM_IEEE80211_DS354k */ 1217165569Ssam 0, /* IFM_IEEE80211_DS512k */ 1218165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 1219165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 1220165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 1221170530Ssam -1, /* IFM_IEEE80211_MCS */ 1222116742Ssam }; 1223116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1224116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1225116742Ssam#undef N 1226116742Ssam} 1227