ieee80211.c revision 127760
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3116742Ssam * Copyright (c) 2002, 2003 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 127760 2004-04-02 20:19:20Z sam $"); 35116742Ssam 36116742Ssam/* 37116742Ssam * IEEE 802.11 generic handler 38116742Ssam */ 39116742Ssam 40116742Ssam#include "opt_inet.h" 41116742Ssam 42116742Ssam#include <sys/param.h> 43116742Ssam#include <sys/systm.h> 44116742Ssam#include <sys/mbuf.h> 45116742Ssam#include <sys/malloc.h> 46116742Ssam#include <sys/kernel.h> 47116742Ssam#include <sys/socket.h> 48116742Ssam#include <sys/sockio.h> 49116742Ssam#include <sys/endian.h> 50116742Ssam#include <sys/errno.h> 51116742Ssam#include <sys/bus.h> 52116742Ssam#include <sys/proc.h> 53116742Ssam#include <sys/sysctl.h> 54116742Ssam 55116742Ssam#include <machine/atomic.h> 56116742Ssam 57116742Ssam#include <net/if.h> 58116742Ssam#include <net/if_dl.h> 59116742Ssam#include <net/if_media.h> 60116742Ssam#include <net/if_arp.h> 61116742Ssam#include <net/ethernet.h> 62116742Ssam#include <net/if_llc.h> 63116742Ssam 64116742Ssam#include <net80211/ieee80211_var.h> 65116742Ssam 66116742Ssam#include <net/bpf.h> 67116742Ssam 68116742Ssam#ifdef INET 69116742Ssam#include <netinet/in.h> 70116742Ssam#include <netinet/if_ether.h> 71116742Ssam#endif 72116742Ssam 73116742Ssam#ifdef IEEE80211_DEBUG 74116742Ssamint ieee80211_debug = 0; 75116742SsamSYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug, 76116742Ssam 0, "IEEE 802.11 media debugging printfs"); 77116742Ssam#endif 78116742Ssam 79116742Ssamstatic void ieee80211_set11gbasicrates(struct ieee80211_rateset *, 80116742Ssam enum ieee80211_phymode); 81116742Ssam 82116742Ssamstatic const char *ieee80211_phymode_name[] = { 83116742Ssam "auto", /* IEEE80211_MODE_AUTO */ 84116742Ssam "11a", /* IEEE80211_MODE_11A */ 85116742Ssam "11b", /* IEEE80211_MODE_11B */ 86116742Ssam "11g", /* IEEE80211_MODE_11G */ 87124543Sonoe "FH", /* IEEE80211_MODE_FH */ 88124543Sonoe "turbo", /* IEEE80211_MODE_TURBO */ 89116742Ssam}; 90116742Ssam 91116742Ssamvoid 92116742Ssamieee80211_ifattach(struct ifnet *ifp) 93116742Ssam{ 94116742Ssam struct ieee80211com *ic = (void *)ifp; 95116742Ssam struct ieee80211_channel *c; 96116742Ssam int i; 97116742Ssam 98116742Ssam ether_ifattach(ifp, ic->ic_myaddr); 99116742Ssam bpfattach2(ifp, DLT_IEEE802_11, 100116742Ssam sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); 101116742Ssam ieee80211_crypto_attach(ifp); 102116742Ssam 103116742Ssam /* 104116742Ssam * Fill in 802.11 available channel set, mark 105116742Ssam * all available channels as active, and pick 106116742Ssam * a default channel if not already specified. 107116742Ssam */ 108116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 109116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO; 110116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 111116742Ssam c = &ic->ic_channels[i]; 112116742Ssam if (c->ic_flags) { 113116742Ssam /* 114116742Ssam * Verify driver passed us valid data. 115116742Ssam */ 116116742Ssam if (i != ieee80211_chan2ieee(ic, c)) { 117116742Ssam if_printf(ifp, "bad channel ignored; " 118116742Ssam "freq %u flags %x number %u\n", 119116742Ssam c->ic_freq, c->ic_flags, i); 120116742Ssam c->ic_flags = 0; /* NB: remove */ 121116742Ssam continue; 122116742Ssam } 123116742Ssam setbit(ic->ic_chan_avail, i); 124116742Ssam /* 125116742Ssam * Identify mode capabilities. 126116742Ssam */ 127116742Ssam if (IEEE80211_IS_CHAN_A(c)) 128116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11A; 129116742Ssam if (IEEE80211_IS_CHAN_B(c)) 130116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11B; 131116742Ssam if (IEEE80211_IS_CHAN_PUREG(c)) 132116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; 133124543Sonoe if (IEEE80211_IS_CHAN_FHSS(c)) 134124543Sonoe ic->ic_modecaps |= 1<<IEEE80211_MODE_FH; 135116742Ssam if (IEEE80211_IS_CHAN_T(c)) 136116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO; 137116742Ssam } 138116742Ssam } 139116742Ssam /* validate ic->ic_curmode */ 140116742Ssam if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0) 141116742Ssam ic->ic_curmode = IEEE80211_MODE_AUTO; 142127760Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 143116742Ssam 144116742Ssam (void) ieee80211_setmode(ic, ic->ic_curmode); 145116742Ssam 146116742Ssam if (ic->ic_lintval == 0) 147116742Ssam ic->ic_lintval = 100; /* default sleep */ 148116742Ssam ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ 149116742Ssam 150116742Ssam ieee80211_node_attach(ifp); 151116742Ssam ieee80211_proto_attach(ifp); 152116742Ssam} 153116742Ssam 154116742Ssamvoid 155116742Ssamieee80211_ifdetach(struct ifnet *ifp) 156116742Ssam{ 157116742Ssam struct ieee80211com *ic = (void *)ifp; 158116742Ssam 159116742Ssam ieee80211_proto_detach(ifp); 160116742Ssam ieee80211_crypto_detach(ifp); 161116742Ssam ieee80211_node_detach(ifp); 162116742Ssam ifmedia_removeall(&ic->ic_media); 163116742Ssam bpfdetach(ifp); 164116742Ssam ether_ifdetach(ifp); 165116742Ssam} 166116742Ssam 167116742Ssam/* 168116742Ssam * Convert MHz frequency to IEEE channel number. 169116742Ssam */ 170116742Ssamu_int 171116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 172116742Ssam{ 173116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 174116742Ssam if (freq == 2484) 175116742Ssam return 14; 176116742Ssam if (freq < 2484) 177116742Ssam return (freq - 2407) / 5; 178116742Ssam else 179116742Ssam return 15 + ((freq - 2512) / 20); 180116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 181116742Ssam return (freq - 5000) / 5; 182116742Ssam } else { /* either, guess */ 183116742Ssam if (freq == 2484) 184116742Ssam return 14; 185116742Ssam if (freq < 2484) 186116742Ssam return (freq - 2407) / 5; 187116742Ssam if (freq < 5000) 188116742Ssam return 15 + ((freq - 2512) / 20); 189116742Ssam return (freq - 5000) / 5; 190116742Ssam } 191116742Ssam} 192116742Ssam 193116742Ssam/* 194116742Ssam * Convert channel to IEEE channel number. 195116742Ssam */ 196116742Ssamu_int 197116742Ssamieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) 198116742Ssam{ 199116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 200116742Ssam return c - ic->ic_channels; 201116742Ssam else if (c == IEEE80211_CHAN_ANYC) 202116742Ssam return IEEE80211_CHAN_ANY; 203117039Ssam else if (c != NULL) { 204116742Ssam if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n", 205116742Ssam c->ic_freq, c->ic_flags); 206116742Ssam return 0; /* XXX */ 207117039Ssam } else { 208117039Ssam if_printf(&ic->ic_if, "invalid channel (NULL)\n"); 209117039Ssam return 0; /* XXX */ 210116742Ssam } 211116742Ssam} 212116742Ssam 213116742Ssam/* 214116742Ssam * Convert IEEE channel number to MHz frequency. 215116742Ssam */ 216116742Ssamu_int 217116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 218116742Ssam{ 219116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 220116742Ssam if (chan == 14) 221116742Ssam return 2484; 222116742Ssam if (chan < 14) 223116742Ssam return 2407 + chan*5; 224116742Ssam else 225116742Ssam return 2512 + ((chan-15)*20); 226116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 227116742Ssam return 5000 + (chan*5); 228116742Ssam } else { /* either, guess */ 229116742Ssam if (chan == 14) 230116742Ssam return 2484; 231116742Ssam if (chan < 14) /* 0-13 */ 232116742Ssam return 2407 + chan*5; 233116742Ssam if (chan < 27) /* 15-26 */ 234116742Ssam return 2512 + ((chan-15)*20); 235116742Ssam return 5000 + (chan*5); 236116742Ssam } 237116742Ssam} 238116742Ssam 239116742Ssam/* 240116742Ssam * Setup the media data structures according to the channel and 241116742Ssam * rate tables. This must be called by the driver after 242116742Ssam * ieee80211_attach and before most anything else. 243116742Ssam */ 244116742Ssamvoid 245116742Ssamieee80211_media_init(struct ifnet *ifp, 246116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 247116742Ssam{ 248116742Ssam#define ADD(_ic, _s, _o) \ 249116742Ssam ifmedia_add(&(_ic)->ic_media, \ 250116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 251116742Ssam struct ieee80211com *ic = (void *)ifp; 252116742Ssam struct ifmediareq imr; 253116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 254116742Ssam struct ieee80211_rateset *rs; 255116742Ssam struct ieee80211_rateset allrates; 256116742Ssam 257116742Ssam /* 258118887Ssam * Do late attach work that must wait for any subclass 259118887Ssam * (i.e. driver) work such as overriding methods. 260118887Ssam */ 261118887Ssam ieee80211_node_lateattach(ifp); 262118887Ssam 263118887Ssam /* 264116742Ssam * Fill in media characteristics. 265116742Ssam */ 266116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 267116742Ssam maxrate = 0; 268116742Ssam memset(&allrates, 0, sizeof(allrates)); 269116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 270116742Ssam static const u_int mopts[] = { 271116742Ssam IFM_AUTO, 272124543Sonoe IFM_IEEE80211_11A, 273124543Sonoe IFM_IEEE80211_11B, 274124543Sonoe IFM_IEEE80211_11G, 275124543Sonoe IFM_IEEE80211_FH, 276124543Sonoe IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 277116742Ssam }; 278116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 279116742Ssam continue; 280116742Ssam mopt = mopts[mode]; 281116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 282116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 283116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 284116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 285116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 286116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 287116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 288117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 289117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 290116742Ssam if (mode == IEEE80211_MODE_AUTO) 291116742Ssam continue; 292116742Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 293116742Ssam rs = &ic->ic_sup_rates[mode]; 294116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 295116742Ssam rate = rs->rs_rates[i]; 296116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 297116742Ssam if (mword == 0) 298116742Ssam continue; 299116742Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 300116742Ssam (rate & IEEE80211_RATE_VAL) / 2, 301116742Ssam ((rate & 0x1) != 0 ? ".5" : "")); 302116742Ssam ADD(ic, mword, mopt); 303116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 304116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 305116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 306116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 307116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 308116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 309117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 310117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 311116742Ssam /* 312116742Ssam * Add rate to the collection of all rates. 313116742Ssam */ 314116742Ssam r = rate & IEEE80211_RATE_VAL; 315116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 316116742Ssam if (allrates.rs_rates[j] == r) 317116742Ssam break; 318116742Ssam if (j == allrates.rs_nrates) { 319116742Ssam /* unique, add to the set */ 320116742Ssam allrates.rs_rates[j] = r; 321116742Ssam allrates.rs_nrates++; 322116742Ssam } 323116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 324116742Ssam if (rate > maxrate) 325116742Ssam maxrate = rate; 326116742Ssam } 327116742Ssam printf("\n"); 328116742Ssam } 329116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 330116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 331116742Ssam IEEE80211_MODE_AUTO); 332116742Ssam if (mword == 0) 333116742Ssam continue; 334116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 335116742Ssam ADD(ic, mword, 0); 336116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 337116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 338116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 339116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 340116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 341116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 342117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 343117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 344116742Ssam } 345116742Ssam ieee80211_media_status(ifp, &imr); 346116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 347116742Ssam 348116742Ssam if (maxrate) 349116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 350116742Ssam#undef ADD 351116742Ssam} 352116742Ssam 353116742Ssamstatic int 354116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 355116742Ssam{ 356116742Ssam#define IEEERATE(_ic,_m,_i) \ 357116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 358116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 359116742Ssam for (i = 0; i < nrates; i++) 360116742Ssam if (IEEERATE(ic, mode, i) == rate) 361116742Ssam return i; 362116742Ssam return -1; 363116742Ssam#undef IEEERATE 364116742Ssam} 365116742Ssam 366116742Ssam/* 367116742Ssam * Handle a media change request. 368116742Ssam */ 369116742Ssamint 370116742Ssamieee80211_media_change(struct ifnet *ifp) 371116742Ssam{ 372116742Ssam struct ieee80211com *ic = (void *)ifp; 373116742Ssam struct ifmedia_entry *ime; 374116742Ssam enum ieee80211_opmode newopmode; 375116742Ssam enum ieee80211_phymode newphymode; 376116742Ssam int i, j, newrate, error = 0; 377116742Ssam 378116742Ssam ime = ic->ic_media.ifm_cur; 379116742Ssam /* 380116742Ssam * First, identify the phy mode. 381116742Ssam */ 382116742Ssam switch (IFM_MODE(ime->ifm_media)) { 383116742Ssam case IFM_IEEE80211_11A: 384116742Ssam newphymode = IEEE80211_MODE_11A; 385116742Ssam break; 386116742Ssam case IFM_IEEE80211_11B: 387116742Ssam newphymode = IEEE80211_MODE_11B; 388116742Ssam break; 389116742Ssam case IFM_IEEE80211_11G: 390116742Ssam newphymode = IEEE80211_MODE_11G; 391116742Ssam break; 392124543Sonoe case IFM_IEEE80211_FH: 393124543Sonoe newphymode = IEEE80211_MODE_FH; 394124543Sonoe break; 395116742Ssam case IFM_AUTO: 396116742Ssam newphymode = IEEE80211_MODE_AUTO; 397116742Ssam break; 398116742Ssam default: 399116742Ssam return EINVAL; 400116742Ssam } 401116742Ssam /* 402116742Ssam * Turbo mode is an ``option''. Eventually it 403116742Ssam * needs to be applied to 11g too. 404116742Ssam */ 405116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 406116742Ssam if (newphymode != IEEE80211_MODE_11A) 407116742Ssam return EINVAL; 408116742Ssam newphymode = IEEE80211_MODE_TURBO; 409116742Ssam } 410116742Ssam /* 411116742Ssam * Validate requested mode is available. 412116742Ssam */ 413116742Ssam if ((ic->ic_modecaps & (1<<newphymode)) == 0) 414116742Ssam return EINVAL; 415116742Ssam 416116742Ssam /* 417116742Ssam * Next, the fixed/variable rate. 418116742Ssam */ 419116742Ssam i = -1; 420116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 421116742Ssam /* 422116742Ssam * Convert media subtype to rate. 423116742Ssam */ 424116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 425116742Ssam if (newrate == 0) 426116742Ssam return EINVAL; 427116742Ssam /* 428116742Ssam * Check the rate table for the specified/current phy. 429116742Ssam */ 430116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 431116742Ssam /* 432116742Ssam * In autoselect mode search for the rate. 433116742Ssam */ 434116742Ssam for (j = IEEE80211_MODE_11A; 435116742Ssam j < IEEE80211_MODE_MAX; j++) { 436116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 437116742Ssam continue; 438116742Ssam i = findrate(ic, j, newrate); 439116742Ssam if (i != -1) { 440116742Ssam /* lock mode too */ 441116742Ssam newphymode = j; 442116742Ssam break; 443116742Ssam } 444116742Ssam } 445116742Ssam } else { 446116742Ssam i = findrate(ic, newphymode, newrate); 447116742Ssam } 448116742Ssam if (i == -1) /* mode/rate mismatch */ 449116742Ssam return EINVAL; 450116742Ssam } 451116742Ssam /* NB: defer rate setting to later */ 452116742Ssam 453116742Ssam /* 454116742Ssam * Deduce new operating mode but don't install it just yet. 455116742Ssam */ 456116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 457116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 458116742Ssam newopmode = IEEE80211_M_AHDEMO; 459116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 460116742Ssam newopmode = IEEE80211_M_HOSTAP; 461116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 462116742Ssam newopmode = IEEE80211_M_IBSS; 463117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 464117817Ssam newopmode = IEEE80211_M_MONITOR; 465116742Ssam else 466116742Ssam newopmode = IEEE80211_M_STA; 467116742Ssam 468116742Ssam /* 469116742Ssam * Autoselect doesn't make sense when operating as an AP. 470116742Ssam * If no phy mode has been selected, pick one and lock it 471116742Ssam * down so rate tables can be used in forming beacon frames 472116742Ssam * and the like. 473116742Ssam */ 474116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 475116742Ssam newphymode == IEEE80211_MODE_AUTO) { 476116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 477116742Ssam if (ic->ic_modecaps & (1<<j)) { 478116742Ssam newphymode = j; 479116742Ssam break; 480116742Ssam } 481116742Ssam } 482116742Ssam 483116742Ssam /* 484116742Ssam * Handle phy mode change. 485116742Ssam */ 486116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 487116742Ssam error = ieee80211_setmode(ic, newphymode); 488116742Ssam if (error != 0) 489116742Ssam return error; 490116742Ssam error = ENETRESET; 491116742Ssam } 492116742Ssam 493116742Ssam /* 494116742Ssam * Committed to changes, install the rate setting. 495116742Ssam */ 496116742Ssam if (ic->ic_fixed_rate != i) { 497116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 498116742Ssam error = ENETRESET; 499116742Ssam } 500116742Ssam 501116742Ssam /* 502116742Ssam * Handle operating mode change. 503116742Ssam */ 504116742Ssam if (ic->ic_opmode != newopmode) { 505116742Ssam ic->ic_opmode = newopmode; 506116742Ssam switch (newopmode) { 507116742Ssam case IEEE80211_M_AHDEMO: 508116742Ssam case IEEE80211_M_HOSTAP: 509116742Ssam case IEEE80211_M_STA: 510117817Ssam case IEEE80211_M_MONITOR: 511116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 512116742Ssam break; 513116742Ssam case IEEE80211_M_IBSS: 514116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 515116742Ssam#ifdef notdef 516116742Ssam if (ic->ic_curmode == IEEE80211_MODE_11G) 517116742Ssam ieee80211_set11gbasicrates( 518116742Ssam &ic->ic_suprates[newphymode], 519116742Ssam IEEE80211_MODE_11B); 520116742Ssam#endif 521116742Ssam break; 522116742Ssam } 523116742Ssam error = ENETRESET; 524116742Ssam } 525116742Ssam#ifdef notdef 526116742Ssam if (error == 0) 527116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 528116742Ssam#endif 529116742Ssam return error; 530116742Ssam} 531116742Ssam 532116742Ssamvoid 533116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 534116742Ssam{ 535116742Ssam struct ieee80211com *ic = (void *)ifp; 536116742Ssam struct ieee80211_node *ni = NULL; 537116742Ssam 538116742Ssam imr->ifm_status = IFM_AVALID; 539116742Ssam imr->ifm_active = IFM_IEEE80211; 540116742Ssam if (ic->ic_state == IEEE80211_S_RUN) 541116742Ssam imr->ifm_status |= IFM_ACTIVE; 542116742Ssam imr->ifm_active |= IFM_AUTO; 543116742Ssam switch (ic->ic_opmode) { 544116742Ssam case IEEE80211_M_STA: 545116742Ssam ni = ic->ic_bss; 546116742Ssam /* calculate rate subtype */ 547116742Ssam imr->ifm_active |= ieee80211_rate2media(ic, 548116742Ssam ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); 549116742Ssam break; 550116742Ssam case IEEE80211_M_IBSS: 551116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 552116742Ssam break; 553116742Ssam case IEEE80211_M_AHDEMO: 554116742Ssam /* should not come here */ 555116742Ssam break; 556116742Ssam case IEEE80211_M_HOSTAP: 557116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 558116742Ssam break; 559117817Ssam case IEEE80211_M_MONITOR: 560117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 561117817Ssam break; 562116742Ssam } 563116742Ssam switch (ic->ic_curmode) { 564116742Ssam case IEEE80211_MODE_11A: 565124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A; 566116742Ssam break; 567116742Ssam case IEEE80211_MODE_11B: 568124543Sonoe imr->ifm_active |= IFM_IEEE80211_11B; 569116742Ssam break; 570116742Ssam case IEEE80211_MODE_11G: 571124543Sonoe imr->ifm_active |= IFM_IEEE80211_11G; 572116742Ssam break; 573124543Sonoe case IEEE80211_MODE_FH: 574124543Sonoe imr->ifm_active |= IFM_IEEE80211_FH; 575124543Sonoe break; 576116742Ssam case IEEE80211_MODE_TURBO: 577124543Sonoe imr->ifm_active |= IFM_IEEE80211_11A 578116742Ssam | IFM_IEEE80211_TURBO; 579116742Ssam break; 580116742Ssam } 581116742Ssam} 582116742Ssam 583116742Ssamvoid 584116742Ssamieee80211_watchdog(struct ifnet *ifp) 585116742Ssam{ 586116742Ssam struct ieee80211com *ic = (void *)ifp; 587116742Ssam 588116742Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 589117811Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 590116742Ssam if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) 591116742Ssam ieee80211_timeout_nodes(ic); 592116742Ssam 593116742Ssam if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) 594116742Ssam ifp->if_timer = 1; 595116742Ssam} 596116742Ssam 597116742Ssam/* 598116742Ssam * Mark the basic rates for the 11g rate table based on the 599116742Ssam * operating mode. For real 11g we mark all the 11b rates 600116742Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 601116742Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 602116742Ssam * the basic OFDM rates. 603116742Ssam */ 604116742Ssamstatic void 605116742Ssamieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 606116742Ssam{ 607116742Ssam static const struct ieee80211_rateset basic[] = { 608116742Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 609116742Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */ 610116742Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */ 611124543Sonoe { 0 }, /* IEEE80211_MODE_FH */ 612116742Ssam { 0 }, /* IEEE80211_MODE_TURBO */ 613116742Ssam }; 614116742Ssam int i, j; 615116742Ssam 616116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 617116742Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 618116742Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 619116742Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 620116742Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 621116742Ssam break; 622116742Ssam } 623116742Ssam } 624116742Ssam} 625116742Ssam 626116742Ssam/* 627116742Ssam * Set the current phy mode and recalculate the active channel 628116742Ssam * set based on the available channels for this mode. Also 629116742Ssam * select a new default/current channel if the current one is 630116742Ssam * inappropriate for this mode. 631116742Ssam */ 632116742Ssamint 633116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 634116742Ssam{ 635116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 636116742Ssam static const u_int chanflags[] = { 637116742Ssam 0, /* IEEE80211_MODE_AUTO */ 638116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 639116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 640116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 641124543Sonoe IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 642116742Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */ 643116742Ssam }; 644116742Ssam struct ieee80211_channel *c; 645116742Ssam u_int modeflags; 646116742Ssam int i; 647116742Ssam 648116742Ssam /* validate new mode */ 649116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) { 650116742Ssam IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n", 651116742Ssam __func__, mode, ic->ic_modecaps)); 652116742Ssam return EINVAL; 653116742Ssam } 654116742Ssam 655116742Ssam /* 656116742Ssam * Verify at least one channel is present in the available 657116742Ssam * channel list before committing to the new mode. 658116742Ssam */ 659127760Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); 660116742Ssam modeflags = chanflags[mode]; 661116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 662116742Ssam c = &ic->ic_channels[i]; 663116742Ssam if (mode == IEEE80211_MODE_AUTO) { 664116742Ssam /* ignore turbo channels for autoselect */ 665116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 666116742Ssam break; 667116742Ssam } else { 668116742Ssam if ((c->ic_flags & modeflags) == modeflags) 669116742Ssam break; 670116742Ssam } 671116742Ssam } 672116742Ssam if (i > IEEE80211_CHAN_MAX) { 673116742Ssam IEEE80211_DPRINTF(("%s: no channels found for mode %u\n", 674116742Ssam __func__, mode)); 675116742Ssam return EINVAL; 676116742Ssam } 677116742Ssam 678116742Ssam /* 679116742Ssam * Calculate the active channel set. 680116742Ssam */ 681116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 682116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 683116742Ssam c = &ic->ic_channels[i]; 684116742Ssam if (mode == IEEE80211_MODE_AUTO) { 685116742Ssam /* take anything but pure turbo channels */ 686116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 687116742Ssam setbit(ic->ic_chan_active, i); 688116742Ssam } else { 689116742Ssam if ((c->ic_flags & modeflags) == modeflags) 690116742Ssam setbit(ic->ic_chan_active, i); 691116742Ssam } 692116742Ssam } 693116742Ssam /* 694116742Ssam * If no current/default channel is setup or the current 695116742Ssam * channel is wrong for the mode then pick the first 696116742Ssam * available channel from the active list. This is likely 697116742Ssam * not the right one. 698116742Ssam */ 699116742Ssam if (ic->ic_ibss_chan == NULL || 700116742Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 701116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 702116742Ssam if (isset(ic->ic_chan_active, i)) { 703116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 704116742Ssam break; 705116742Ssam } 706116742Ssam } 707116742Ssam 708116742Ssam /* 709116742Ssam * Set/reset state flags that influence beacon contents, etc. 710116742Ssam * 711116742Ssam * XXX what if we have stations already associated??? 712116742Ssam * XXX probably not right for autoselect? 713116742Ssam */ 714120067Ssam if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) 715120067Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 716116742Ssam if (mode == IEEE80211_MODE_11G) { 717116742Ssam if (ic->ic_caps & IEEE80211_C_SHSLOT) 718116742Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 719116742Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 720116742Ssam IEEE80211_MODE_11G); 721116742Ssam } else { 722120067Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 723116742Ssam } 724116742Ssam 725116742Ssam ic->ic_curmode = mode; 726116742Ssam return 0; 727116742Ssam#undef N 728116742Ssam} 729116742Ssam 730116742Ssam/* 731116742Ssam * Return the phy mode for with the specified channel so the 732116742Ssam * caller can select a rate set. This is problematic and the 733116742Ssam * work here assumes how things work elsewhere in this code. 734116742Ssam */ 735116742Ssamenum ieee80211_phymode 736116742Ssamieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) 737116742Ssam{ 738116742Ssam /* 739116742Ssam * NB: this assumes the channel would not be supplied to us 740116742Ssam * unless it was already compatible with the current mode. 741116742Ssam */ 742116742Ssam if (ic->ic_curmode != IEEE80211_MODE_AUTO) 743116742Ssam return ic->ic_curmode; 744116742Ssam /* 745116742Ssam * In autoselect mode; deduce a mode based on the channel 746116742Ssam * characteristics. We assume that turbo-only channels 747116742Ssam * are not considered when the channel set is constructed. 748116742Ssam */ 749116742Ssam if (IEEE80211_IS_CHAN_5GHZ(chan)) 750116742Ssam return IEEE80211_MODE_11A; 751124543Sonoe else if (IEEE80211_IS_CHAN_FHSS(chan)) 752124543Sonoe return IEEE80211_MODE_FH; 753116742Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) 754116742Ssam return IEEE80211_MODE_11G; 755116742Ssam else 756116742Ssam return IEEE80211_MODE_11B; 757116742Ssam} 758116742Ssam 759116742Ssam/* 760116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 761116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 762116742Ssam */ 763116742Ssamint 764116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 765116742Ssam{ 766116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 767116742Ssam static const struct { 768116742Ssam u_int m; /* rate + mode */ 769116742Ssam u_int r; /* if_media rate */ 770116742Ssam } rates[] = { 771124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 772124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 773124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 774124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 775124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 776124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 777124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 778124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 779124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 780124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 781124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 782124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 783124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 784124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 785124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 786124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 787124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 788124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 789124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 790124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 791124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 792124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 793124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 794124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 795124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 796124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 797124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 798116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 799116742Ssam }; 800116742Ssam u_int mask, i; 801116742Ssam 802116742Ssam mask = rate & IEEE80211_RATE_VAL; 803116742Ssam switch (mode) { 804116742Ssam case IEEE80211_MODE_11A: 805116742Ssam case IEEE80211_MODE_TURBO: 806124543Sonoe mask |= IFM_IEEE80211_11A; 807116742Ssam break; 808116742Ssam case IEEE80211_MODE_11B: 809124543Sonoe mask |= IFM_IEEE80211_11B; 810116742Ssam break; 811124543Sonoe case IEEE80211_MODE_FH: 812124543Sonoe mask |= IFM_IEEE80211_FH; 813124543Sonoe break; 814116742Ssam case IEEE80211_MODE_AUTO: 815116742Ssam /* NB: ic may be NULL for some drivers */ 816116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 817124543Sonoe mask |= IFM_IEEE80211_FH; 818124543Sonoe break; 819116742Ssam } 820116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 821116742Ssam /* fall thru... */ 822116742Ssam case IEEE80211_MODE_11G: 823124543Sonoe mask |= IFM_IEEE80211_11G; 824116742Ssam break; 825116742Ssam } 826116742Ssam for (i = 0; i < N(rates); i++) 827116742Ssam if (rates[i].m == mask) 828116742Ssam return rates[i].r; 829116742Ssam return IFM_AUTO; 830116742Ssam#undef N 831116742Ssam} 832116742Ssam 833116742Ssamint 834116742Ssamieee80211_media2rate(int mword) 835116742Ssam{ 836116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 837116742Ssam static const int ieeerates[] = { 838116742Ssam -1, /* IFM_AUTO */ 839116742Ssam 0, /* IFM_MANUAL */ 840116742Ssam 0, /* IFM_NONE */ 841116742Ssam 2, /* IFM_IEEE80211_FH1 */ 842116742Ssam 4, /* IFM_IEEE80211_FH2 */ 843116742Ssam 2, /* IFM_IEEE80211_DS1 */ 844116742Ssam 4, /* IFM_IEEE80211_DS2 */ 845116742Ssam 11, /* IFM_IEEE80211_DS5 */ 846116742Ssam 22, /* IFM_IEEE80211_DS11 */ 847116742Ssam 44, /* IFM_IEEE80211_DS22 */ 848116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 849116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 850116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 851116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 852116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 853116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 854116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 855116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 856116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 857116742Ssam }; 858116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 859116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 860116742Ssam#undef N 861116742Ssam} 862116742Ssam 863116742Ssam/* 864116742Ssam * Module glue. 865116742Ssam * 866116742Ssam * NB: the module name is "wlan" for compatibility with NetBSD. 867116742Ssam */ 868116742Ssam 869116742Ssamstatic int 870116742Ssamieee80211_modevent(module_t mod, int type, void *unused) 871116742Ssam{ 872116742Ssam switch (type) { 873116742Ssam case MOD_LOAD: 874116742Ssam if (bootverbose) 875116742Ssam printf("wlan: <802.11 Link Layer>\n"); 876116742Ssam return 0; 877116742Ssam case MOD_UNLOAD: 878116742Ssam return 0; 879116742Ssam } 880116742Ssam return EINVAL; 881116742Ssam} 882116742Ssam 883116742Ssamstatic moduledata_t ieee80211_mod = { 884116742Ssam "wlan", 885116742Ssam ieee80211_modevent, 886116742Ssam 0 887116742Ssam}; 888116742SsamDECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 889116742SsamMODULE_VERSION(wlan, 1); 890116742SsamMODULE_DEPEND(wlan, rc4, 1, 1, 1); 891127087SmuxMODULE_DEPEND(wlan, ether, 1, 1, 1); 892