ieee80211.c revision 117817
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 117817 2003-07-21 02:49:42Z 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 */ 87116742Ssam "turbo", /* IEEE80211_MODE_TURBO */ 88116742Ssam}; 89116742Ssam 90116742Ssamvoid 91116742Ssamieee80211_ifattach(struct ifnet *ifp) 92116742Ssam{ 93116742Ssam struct ieee80211com *ic = (void *)ifp; 94116742Ssam struct ieee80211_channel *c; 95116742Ssam int i; 96116742Ssam 97116742Ssam ether_ifattach(ifp, ic->ic_myaddr); 98116742Ssam bpfattach2(ifp, DLT_IEEE802_11, 99116742Ssam sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); 100116742Ssam ieee80211_crypto_attach(ifp); 101116742Ssam 102116742Ssam /* 103116742Ssam * Fill in 802.11 available channel set, mark 104116742Ssam * all available channels as active, and pick 105116742Ssam * a default channel if not already specified. 106116742Ssam */ 107116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 108116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO; 109116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 110116742Ssam c = &ic->ic_channels[i]; 111116742Ssam if (c->ic_flags) { 112116742Ssam /* 113116742Ssam * Verify driver passed us valid data. 114116742Ssam */ 115116742Ssam if (i != ieee80211_chan2ieee(ic, c)) { 116116742Ssam if_printf(ifp, "bad channel ignored; " 117116742Ssam "freq %u flags %x number %u\n", 118116742Ssam c->ic_freq, c->ic_flags, i); 119116742Ssam c->ic_flags = 0; /* NB: remove */ 120116742Ssam continue; 121116742Ssam } 122116742Ssam setbit(ic->ic_chan_avail, i); 123116742Ssam /* 124116742Ssam * Identify mode capabilities. 125116742Ssam */ 126116742Ssam if (IEEE80211_IS_CHAN_A(c)) 127116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11A; 128116742Ssam if (IEEE80211_IS_CHAN_B(c)) 129116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11B; 130116742Ssam if (IEEE80211_IS_CHAN_PUREG(c)) 131116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; 132116742Ssam if (IEEE80211_IS_CHAN_T(c)) 133116742Ssam ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO; 134116742Ssam } 135116742Ssam } 136116742Ssam /* validate ic->ic_curmode */ 137116742Ssam if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0) 138116742Ssam ic->ic_curmode = IEEE80211_MODE_AUTO; 139116742Ssam 140116742Ssam (void) ieee80211_setmode(ic, ic->ic_curmode); 141116742Ssam 142116742Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 143116742Ssam if (ic->ic_lintval == 0) 144116742Ssam ic->ic_lintval = 100; /* default sleep */ 145116742Ssam ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ 146116742Ssam 147116742Ssam ieee80211_node_attach(ifp); 148116742Ssam ieee80211_proto_attach(ifp); 149116742Ssam} 150116742Ssam 151116742Ssamvoid 152116742Ssamieee80211_ifdetach(struct ifnet *ifp) 153116742Ssam{ 154116742Ssam struct ieee80211com *ic = (void *)ifp; 155116742Ssam 156116742Ssam ieee80211_proto_detach(ifp); 157116742Ssam ieee80211_crypto_detach(ifp); 158116742Ssam ieee80211_node_detach(ifp); 159116742Ssam ifmedia_removeall(&ic->ic_media); 160116742Ssam bpfdetach(ifp); 161116742Ssam ether_ifdetach(ifp); 162116742Ssam} 163116742Ssam 164116742Ssam/* 165116742Ssam * Convert MHz frequency to IEEE channel number. 166116742Ssam */ 167116742Ssamu_int 168116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 169116742Ssam{ 170116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 171116742Ssam if (freq == 2484) 172116742Ssam return 14; 173116742Ssam if (freq < 2484) 174116742Ssam return (freq - 2407) / 5; 175116742Ssam else 176116742Ssam return 15 + ((freq - 2512) / 20); 177116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 178116742Ssam return (freq - 5000) / 5; 179116742Ssam } else { /* either, guess */ 180116742Ssam if (freq == 2484) 181116742Ssam return 14; 182116742Ssam if (freq < 2484) 183116742Ssam return (freq - 2407) / 5; 184116742Ssam if (freq < 5000) 185116742Ssam return 15 + ((freq - 2512) / 20); 186116742Ssam return (freq - 5000) / 5; 187116742Ssam } 188116742Ssam} 189116742Ssam 190116742Ssam/* 191116742Ssam * Convert channel to IEEE channel number. 192116742Ssam */ 193116742Ssamu_int 194116742Ssamieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) 195116742Ssam{ 196116742Ssam if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) 197116742Ssam return c - ic->ic_channels; 198116742Ssam else if (c == IEEE80211_CHAN_ANYC) 199116742Ssam return IEEE80211_CHAN_ANY; 200117039Ssam else if (c != NULL) { 201116742Ssam if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n", 202116742Ssam c->ic_freq, c->ic_flags); 203116742Ssam return 0; /* XXX */ 204117039Ssam } else { 205117039Ssam if_printf(&ic->ic_if, "invalid channel (NULL)\n"); 206117039Ssam return 0; /* XXX */ 207116742Ssam } 208116742Ssam} 209116742Ssam 210116742Ssam/* 211116742Ssam * Convert IEEE channel number to MHz frequency. 212116742Ssam */ 213116742Ssamu_int 214116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 215116742Ssam{ 216116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 217116742Ssam if (chan == 14) 218116742Ssam return 2484; 219116742Ssam if (chan < 14) 220116742Ssam return 2407 + chan*5; 221116742Ssam else 222116742Ssam return 2512 + ((chan-15)*20); 223116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 224116742Ssam return 5000 + (chan*5); 225116742Ssam } else { /* either, guess */ 226116742Ssam if (chan == 14) 227116742Ssam return 2484; 228116742Ssam if (chan < 14) /* 0-13 */ 229116742Ssam return 2407 + chan*5; 230116742Ssam if (chan < 27) /* 15-26 */ 231116742Ssam return 2512 + ((chan-15)*20); 232116742Ssam return 5000 + (chan*5); 233116742Ssam } 234116742Ssam} 235116742Ssam 236116742Ssam/* 237116742Ssam * Setup the media data structures according to the channel and 238116742Ssam * rate tables. This must be called by the driver after 239116742Ssam * ieee80211_attach and before most anything else. 240116742Ssam */ 241116742Ssamvoid 242116742Ssamieee80211_media_init(struct ifnet *ifp, 243116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 244116742Ssam{ 245116742Ssam#define ADD(_ic, _s, _o) \ 246116742Ssam ifmedia_add(&(_ic)->ic_media, \ 247116742Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 248116742Ssam struct ieee80211com *ic = (void *)ifp; 249116742Ssam struct ifmediareq imr; 250116742Ssam int i, j, mode, rate, maxrate, mword, mopt, r; 251116742Ssam struct ieee80211_rateset *rs; 252116742Ssam struct ieee80211_rateset allrates; 253116742Ssam 254116742Ssam /* 255116742Ssam * Fill in media characteristics. 256116742Ssam */ 257116742Ssam ifmedia_init(&ic->ic_media, 0, media_change, media_stat); 258116742Ssam maxrate = 0; 259116742Ssam memset(&allrates, 0, sizeof(allrates)); 260116742Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { 261116742Ssam static const u_int mopts[] = { 262116742Ssam IFM_AUTO, 263116742Ssam IFM_MAKEMODE(IFM_IEEE80211_11A), 264116742Ssam IFM_MAKEMODE(IFM_IEEE80211_11B), 265116742Ssam IFM_MAKEMODE(IFM_IEEE80211_11G), 266116742Ssam IFM_MAKEMODE(IFM_IEEE80211_11A) | IFM_IEEE80211_TURBO, 267116742Ssam }; 268116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) 269116742Ssam continue; 270116742Ssam mopt = mopts[mode]; 271116742Ssam ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ 272116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 273116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); 274116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 275116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); 276116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 277116742Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 278117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 279117817Ssam ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); 280116742Ssam if (mode == IEEE80211_MODE_AUTO) 281116742Ssam continue; 282116742Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 283116742Ssam rs = &ic->ic_sup_rates[mode]; 284116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 285116742Ssam rate = rs->rs_rates[i]; 286116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 287116742Ssam if (mword == 0) 288116742Ssam continue; 289116742Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 290116742Ssam (rate & IEEE80211_RATE_VAL) / 2, 291116742Ssam ((rate & 0x1) != 0 ? ".5" : "")); 292116742Ssam ADD(ic, mword, mopt); 293116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 294116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); 295116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 296116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); 297116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 298116742Ssam ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 299117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 300117817Ssam ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); 301116742Ssam /* 302116742Ssam * Add rate to the collection of all rates. 303116742Ssam */ 304116742Ssam r = rate & IEEE80211_RATE_VAL; 305116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 306116742Ssam if (allrates.rs_rates[j] == r) 307116742Ssam break; 308116742Ssam if (j == allrates.rs_nrates) { 309116742Ssam /* unique, add to the set */ 310116742Ssam allrates.rs_rates[j] = r; 311116742Ssam allrates.rs_nrates++; 312116742Ssam } 313116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 314116742Ssam if (rate > maxrate) 315116742Ssam maxrate = rate; 316116742Ssam } 317116742Ssam printf("\n"); 318116742Ssam } 319116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 320116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 321116742Ssam IEEE80211_MODE_AUTO); 322116742Ssam if (mword == 0) 323116742Ssam continue; 324116742Ssam mword = IFM_SUBTYPE(mword); /* remove media options */ 325116742Ssam ADD(ic, mword, 0); 326116742Ssam if (ic->ic_caps & IEEE80211_C_IBSS) 327116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC); 328116742Ssam if (ic->ic_caps & IEEE80211_C_HOSTAP) 329116742Ssam ADD(ic, mword, IFM_IEEE80211_HOSTAP); 330116742Ssam if (ic->ic_caps & IEEE80211_C_AHDEMO) 331116742Ssam ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); 332117817Ssam if (ic->ic_caps & IEEE80211_C_MONITOR) 333117817Ssam ADD(ic, mword, IFM_IEEE80211_MONITOR); 334116742Ssam } 335116742Ssam ieee80211_media_status(ifp, &imr); 336116742Ssam ifmedia_set(&ic->ic_media, imr.ifm_active); 337116742Ssam 338116742Ssam if (maxrate) 339116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 340116742Ssam#undef ADD 341116742Ssam} 342116742Ssam 343116742Ssamstatic int 344116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 345116742Ssam{ 346116742Ssam#define IEEERATE(_ic,_m,_i) \ 347116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 348116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 349116742Ssam for (i = 0; i < nrates; i++) 350116742Ssam if (IEEERATE(ic, mode, i) == rate) 351116742Ssam return i; 352116742Ssam return -1; 353116742Ssam#undef IEEERATE 354116742Ssam} 355116742Ssam 356116742Ssam/* 357116742Ssam * Handle a media change request. 358116742Ssam */ 359116742Ssamint 360116742Ssamieee80211_media_change(struct ifnet *ifp) 361116742Ssam{ 362116742Ssam struct ieee80211com *ic = (void *)ifp; 363116742Ssam struct ifmedia_entry *ime; 364116742Ssam enum ieee80211_opmode newopmode; 365116742Ssam enum ieee80211_phymode newphymode; 366116742Ssam int i, j, newrate, error = 0; 367116742Ssam 368116742Ssam ime = ic->ic_media.ifm_cur; 369116742Ssam /* 370116742Ssam * First, identify the phy mode. 371116742Ssam */ 372116742Ssam switch (IFM_MODE(ime->ifm_media)) { 373116742Ssam case IFM_IEEE80211_11A: 374116742Ssam newphymode = IEEE80211_MODE_11A; 375116742Ssam break; 376116742Ssam case IFM_IEEE80211_11B: 377116742Ssam newphymode = IEEE80211_MODE_11B; 378116742Ssam break; 379116742Ssam case IFM_IEEE80211_11G: 380116742Ssam newphymode = IEEE80211_MODE_11G; 381116742Ssam break; 382116742Ssam case IFM_AUTO: 383116742Ssam newphymode = IEEE80211_MODE_AUTO; 384116742Ssam break; 385116742Ssam default: 386116742Ssam return EINVAL; 387116742Ssam } 388116742Ssam /* 389116742Ssam * Turbo mode is an ``option''. Eventually it 390116742Ssam * needs to be applied to 11g too. 391116742Ssam */ 392116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 393116742Ssam if (newphymode != IEEE80211_MODE_11A) 394116742Ssam return EINVAL; 395116742Ssam newphymode = IEEE80211_MODE_TURBO; 396116742Ssam } 397116742Ssam /* 398116742Ssam * Validate requested mode is available. 399116742Ssam */ 400116742Ssam if ((ic->ic_modecaps & (1<<newphymode)) == 0) 401116742Ssam return EINVAL; 402116742Ssam 403116742Ssam /* 404116742Ssam * Next, the fixed/variable rate. 405116742Ssam */ 406116742Ssam i = -1; 407116742Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 408116742Ssam /* 409116742Ssam * Convert media subtype to rate. 410116742Ssam */ 411116742Ssam newrate = ieee80211_media2rate(ime->ifm_media); 412116742Ssam if (newrate == 0) 413116742Ssam return EINVAL; 414116742Ssam /* 415116742Ssam * Check the rate table for the specified/current phy. 416116742Ssam */ 417116742Ssam if (newphymode == IEEE80211_MODE_AUTO) { 418116742Ssam /* 419116742Ssam * In autoselect mode search for the rate. 420116742Ssam */ 421116742Ssam for (j = IEEE80211_MODE_11A; 422116742Ssam j < IEEE80211_MODE_MAX; j++) { 423116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 424116742Ssam continue; 425116742Ssam i = findrate(ic, j, newrate); 426116742Ssam if (i != -1) { 427116742Ssam /* lock mode too */ 428116742Ssam newphymode = j; 429116742Ssam break; 430116742Ssam } 431116742Ssam } 432116742Ssam } else { 433116742Ssam i = findrate(ic, newphymode, newrate); 434116742Ssam } 435116742Ssam if (i == -1) /* mode/rate mismatch */ 436116742Ssam return EINVAL; 437116742Ssam } 438116742Ssam /* NB: defer rate setting to later */ 439116742Ssam 440116742Ssam /* 441116742Ssam * Deduce new operating mode but don't install it just yet. 442116742Ssam */ 443116742Ssam if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == 444116742Ssam (IFM_IEEE80211_ADHOC|IFM_FLAG0)) 445116742Ssam newopmode = IEEE80211_M_AHDEMO; 446116742Ssam else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) 447116742Ssam newopmode = IEEE80211_M_HOSTAP; 448116742Ssam else if (ime->ifm_media & IFM_IEEE80211_ADHOC) 449116742Ssam newopmode = IEEE80211_M_IBSS; 450117817Ssam else if (ime->ifm_media & IFM_IEEE80211_MONITOR) 451117817Ssam newopmode = IEEE80211_M_MONITOR; 452116742Ssam else 453116742Ssam newopmode = IEEE80211_M_STA; 454116742Ssam 455116742Ssam /* 456116742Ssam * Autoselect doesn't make sense when operating as an AP. 457116742Ssam * If no phy mode has been selected, pick one and lock it 458116742Ssam * down so rate tables can be used in forming beacon frames 459116742Ssam * and the like. 460116742Ssam */ 461116742Ssam if (newopmode == IEEE80211_M_HOSTAP && 462116742Ssam newphymode == IEEE80211_MODE_AUTO) { 463116742Ssam for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) 464116742Ssam if (ic->ic_modecaps & (1<<j)) { 465116742Ssam newphymode = j; 466116742Ssam break; 467116742Ssam } 468116742Ssam } 469116742Ssam 470116742Ssam /* 471116742Ssam * Handle phy mode change. 472116742Ssam */ 473116742Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 474116742Ssam error = ieee80211_setmode(ic, newphymode); 475116742Ssam if (error != 0) 476116742Ssam return error; 477116742Ssam error = ENETRESET; 478116742Ssam } 479116742Ssam 480116742Ssam /* 481116742Ssam * Committed to changes, install the rate setting. 482116742Ssam */ 483116742Ssam if (ic->ic_fixed_rate != i) { 484116742Ssam ic->ic_fixed_rate = i; /* set fixed tx rate */ 485116742Ssam error = ENETRESET; 486116742Ssam } 487116742Ssam 488116742Ssam /* 489116742Ssam * Handle operating mode change. 490116742Ssam */ 491116742Ssam if (ic->ic_opmode != newopmode) { 492116742Ssam ic->ic_opmode = newopmode; 493116742Ssam switch (newopmode) { 494116742Ssam case IEEE80211_M_AHDEMO: 495116742Ssam case IEEE80211_M_HOSTAP: 496116742Ssam case IEEE80211_M_STA: 497117817Ssam case IEEE80211_M_MONITOR: 498116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 499116742Ssam break; 500116742Ssam case IEEE80211_M_IBSS: 501116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 502116742Ssam#ifdef notdef 503116742Ssam if (ic->ic_curmode == IEEE80211_MODE_11G) 504116742Ssam ieee80211_set11gbasicrates( 505116742Ssam &ic->ic_suprates[newphymode], 506116742Ssam IEEE80211_MODE_11B); 507116742Ssam#endif 508116742Ssam break; 509116742Ssam } 510116742Ssam error = ENETRESET; 511116742Ssam } 512116742Ssam#ifdef notdef 513116742Ssam if (error == 0) 514116742Ssam ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); 515116742Ssam#endif 516116742Ssam return error; 517116742Ssam} 518116742Ssam 519116742Ssamvoid 520116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 521116742Ssam{ 522116742Ssam struct ieee80211com *ic = (void *)ifp; 523116742Ssam struct ieee80211_node *ni = NULL; 524116742Ssam 525116742Ssam imr->ifm_status = IFM_AVALID; 526116742Ssam imr->ifm_active = IFM_IEEE80211; 527116742Ssam if (ic->ic_state == IEEE80211_S_RUN) 528116742Ssam imr->ifm_status |= IFM_ACTIVE; 529116742Ssam imr->ifm_active |= IFM_AUTO; 530116742Ssam switch (ic->ic_opmode) { 531116742Ssam case IEEE80211_M_STA: 532116742Ssam ni = ic->ic_bss; 533116742Ssam /* calculate rate subtype */ 534116742Ssam imr->ifm_active |= ieee80211_rate2media(ic, 535116742Ssam ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); 536116742Ssam break; 537116742Ssam case IEEE80211_M_IBSS: 538116742Ssam imr->ifm_active |= IFM_IEEE80211_ADHOC; 539116742Ssam break; 540116742Ssam case IEEE80211_M_AHDEMO: 541116742Ssam /* should not come here */ 542116742Ssam break; 543116742Ssam case IEEE80211_M_HOSTAP: 544116742Ssam imr->ifm_active |= IFM_IEEE80211_HOSTAP; 545116742Ssam break; 546117817Ssam case IEEE80211_M_MONITOR: 547117817Ssam imr->ifm_active |= IFM_IEEE80211_MONITOR; 548117817Ssam break; 549116742Ssam } 550116742Ssam switch (ic->ic_curmode) { 551116742Ssam case IEEE80211_MODE_11A: 552116742Ssam imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A); 553116742Ssam break; 554116742Ssam case IEEE80211_MODE_11B: 555116742Ssam imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B); 556116742Ssam break; 557116742Ssam case IEEE80211_MODE_11G: 558116742Ssam imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G); 559116742Ssam break; 560116742Ssam case IEEE80211_MODE_TURBO: 561116742Ssam imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A) 562116742Ssam | IFM_IEEE80211_TURBO; 563116742Ssam break; 564116742Ssam } 565116742Ssam} 566116742Ssam 567116742Ssamvoid 568116742Ssamieee80211_watchdog(struct ifnet *ifp) 569116742Ssam{ 570116742Ssam struct ieee80211com *ic = (void *)ifp; 571116742Ssam 572116742Ssam if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) 573117811Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 574116742Ssam if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) 575116742Ssam ieee80211_timeout_nodes(ic); 576116742Ssam 577116742Ssam if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) 578116742Ssam ifp->if_timer = 1; 579116742Ssam} 580116742Ssam 581116742Ssam/* 582116742Ssam * Mark the basic rates for the 11g rate table based on the 583116742Ssam * operating mode. For real 11g we mark all the 11b rates 584116742Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 585116742Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 586116742Ssam * the basic OFDM rates. 587116742Ssam */ 588116742Ssamstatic void 589116742Ssamieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 590116742Ssam{ 591116742Ssam static const struct ieee80211_rateset basic[] = { 592116742Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 593116742Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */ 594116742Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */ 595116742Ssam { 0 }, /* IEEE80211_MODE_TURBO */ 596116742Ssam }; 597116742Ssam int i, j; 598116742Ssam 599116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 600116742Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 601116742Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 602116742Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 603116742Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 604116742Ssam break; 605116742Ssam } 606116742Ssam } 607116742Ssam} 608116742Ssam 609116742Ssam/* 610116742Ssam * Set the current phy mode and recalculate the active channel 611116742Ssam * set based on the available channels for this mode. Also 612116742Ssam * select a new default/current channel if the current one is 613116742Ssam * inappropriate for this mode. 614116742Ssam */ 615116742Ssamint 616116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 617116742Ssam{ 618116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 619116742Ssam static const u_int chanflags[] = { 620116742Ssam 0, /* IEEE80211_MODE_AUTO */ 621116742Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 622116742Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 623116742Ssam IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 624116742Ssam IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */ 625116742Ssam }; 626116742Ssam struct ieee80211_channel *c; 627116742Ssam u_int modeflags; 628116742Ssam int i; 629116742Ssam 630116742Ssam /* validate new mode */ 631116742Ssam if ((ic->ic_modecaps & (1<<mode)) == 0) { 632116742Ssam IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n", 633116742Ssam __func__, mode, ic->ic_modecaps)); 634116742Ssam return EINVAL; 635116742Ssam } 636116742Ssam 637116742Ssam /* 638116742Ssam * Verify at least one channel is present in the available 639116742Ssam * channel list before committing to the new mode. 640116742Ssam */ 641116742Ssam KASSERT(mode < N(chanflags), ("Unexpected mode %u\n", mode)); 642116742Ssam modeflags = chanflags[mode]; 643116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 644116742Ssam c = &ic->ic_channels[i]; 645116742Ssam if (mode == IEEE80211_MODE_AUTO) { 646116742Ssam /* ignore turbo channels for autoselect */ 647116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 648116742Ssam break; 649116742Ssam } else { 650116742Ssam if ((c->ic_flags & modeflags) == modeflags) 651116742Ssam break; 652116742Ssam } 653116742Ssam } 654116742Ssam if (i > IEEE80211_CHAN_MAX) { 655116742Ssam IEEE80211_DPRINTF(("%s: no channels found for mode %u\n", 656116742Ssam __func__, mode)); 657116742Ssam return EINVAL; 658116742Ssam } 659116742Ssam 660116742Ssam /* 661116742Ssam * Calculate the active channel set. 662116742Ssam */ 663116742Ssam memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); 664116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 665116742Ssam c = &ic->ic_channels[i]; 666116742Ssam if (mode == IEEE80211_MODE_AUTO) { 667116742Ssam /* take anything but pure turbo channels */ 668116742Ssam if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) 669116742Ssam setbit(ic->ic_chan_active, i); 670116742Ssam } else { 671116742Ssam if ((c->ic_flags & modeflags) == modeflags) 672116742Ssam setbit(ic->ic_chan_active, i); 673116742Ssam } 674116742Ssam } 675116742Ssam /* 676116742Ssam * If no current/default channel is setup or the current 677116742Ssam * channel is wrong for the mode then pick the first 678116742Ssam * available channel from the active list. This is likely 679116742Ssam * not the right one. 680116742Ssam */ 681116742Ssam if (ic->ic_ibss_chan == NULL || 682116742Ssam isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 683116742Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 684116742Ssam if (isset(ic->ic_chan_active, i)) { 685116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 686116742Ssam break; 687116742Ssam } 688116742Ssam } 689116742Ssam 690116742Ssam /* 691116742Ssam * Set/reset state flags that influence beacon contents, etc. 692116742Ssam * 693116742Ssam * XXX what if we have stations already associated??? 694116742Ssam * XXX probably not right for autoselect? 695116742Ssam */ 696116742Ssam if (mode == IEEE80211_MODE_11G) { 697116742Ssam if (ic->ic_caps & IEEE80211_C_SHSLOT) 698116742Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 699116742Ssam if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) 700116742Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 701116742Ssam ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 702116742Ssam IEEE80211_MODE_11G); 703116742Ssam } else { 704116742Ssam ic->ic_flags &= ~(IEEE80211_F_SHSLOT | IEEE80211_F_SHPREAMBLE); 705116742Ssam } 706116742Ssam 707116742Ssam ic->ic_curmode = mode; 708116742Ssam return 0; 709116742Ssam#undef N 710116742Ssam} 711116742Ssam 712116742Ssam/* 713116742Ssam * Return the phy mode for with the specified channel so the 714116742Ssam * caller can select a rate set. This is problematic and the 715116742Ssam * work here assumes how things work elsewhere in this code. 716116742Ssam */ 717116742Ssamenum ieee80211_phymode 718116742Ssamieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) 719116742Ssam{ 720116742Ssam /* 721116742Ssam * NB: this assumes the channel would not be supplied to us 722116742Ssam * unless it was already compatible with the current mode. 723116742Ssam */ 724116742Ssam if (ic->ic_curmode != IEEE80211_MODE_AUTO) 725116742Ssam return ic->ic_curmode; 726116742Ssam /* 727116742Ssam * In autoselect mode; deduce a mode based on the channel 728116742Ssam * characteristics. We assume that turbo-only channels 729116742Ssam * are not considered when the channel set is constructed. 730116742Ssam */ 731116742Ssam if (IEEE80211_IS_CHAN_5GHZ(chan)) 732116742Ssam return IEEE80211_MODE_11A; 733116742Ssam else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) 734116742Ssam return IEEE80211_MODE_11G; 735116742Ssam else 736116742Ssam return IEEE80211_MODE_11B; 737116742Ssam} 738116742Ssam 739116742Ssam/* 740116742Ssam * convert IEEE80211 rate value to ifmedia subtype. 741116742Ssam * ieee80211 rate is in unit of 0.5Mbps. 742116742Ssam */ 743116742Ssamint 744116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 745116742Ssam{ 746116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 747116742Ssam static const struct { 748116742Ssam u_int m; /* rate + mode */ 749116742Ssam u_int r; /* if_media rate */ 750116742Ssam } rates[] = { 751116742Ssam { 2 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS1 }, 752116742Ssam { 4 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS2 }, 753116742Ssam { 11 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS5 }, 754116742Ssam { 22 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS11 }, 755116742Ssam { 44 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS22 }, 756116742Ssam { 12 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM6 }, 757116742Ssam { 18 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM9 }, 758116742Ssam { 24 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM12 }, 759116742Ssam { 36 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM18 }, 760116742Ssam { 48 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM24 }, 761116742Ssam { 72 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM36 }, 762116742Ssam { 96 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM48 }, 763116742Ssam { 108 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM54 }, 764116742Ssam { 2 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS1 }, 765116742Ssam { 4 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS2 }, 766116742Ssam { 11 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS5 }, 767116742Ssam { 22 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS11 }, 768116742Ssam { 12 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM6 }, 769116742Ssam { 18 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM9 }, 770116742Ssam { 24 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM12 }, 771116742Ssam { 36 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM18 }, 772116742Ssam { 48 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM24 }, 773116742Ssam { 72 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM36 }, 774116742Ssam { 96 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM48 }, 775116742Ssam { 108 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM54 }, 776116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 777116742Ssam }; 778116742Ssam u_int mask, i; 779116742Ssam 780116742Ssam mask = rate & IEEE80211_RATE_VAL; 781116742Ssam switch (mode) { 782116742Ssam case IEEE80211_MODE_11A: 783116742Ssam case IEEE80211_MODE_TURBO: 784116742Ssam mask |= IFM_MAKEMODE(IFM_IEEE80211_11A); 785116742Ssam break; 786116742Ssam case IEEE80211_MODE_11B: 787116742Ssam mask |= IFM_MAKEMODE(IFM_IEEE80211_11B); 788116742Ssam break; 789116742Ssam case IEEE80211_MODE_AUTO: 790116742Ssam /* NB: ic may be NULL for some drivers */ 791116742Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) { 792116742Ssam /* must handle these specially */ 793116742Ssam switch (mask) { 794116742Ssam case 2: return IFM_IEEE80211_FH1; 795116742Ssam case 4: return IFM_IEEE80211_FH2; 796116742Ssam } 797116742Ssam return IFM_AUTO; 798116742Ssam } 799116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 800116742Ssam /* fall thru... */ 801116742Ssam case IEEE80211_MODE_11G: 802116742Ssam mask |= IFM_MAKEMODE(IFM_IEEE80211_11G); 803116742Ssam break; 804116742Ssam } 805116742Ssam for (i = 0; i < N(rates); i++) 806116742Ssam if (rates[i].m == mask) 807116742Ssam return rates[i].r; 808116742Ssam return IFM_AUTO; 809116742Ssam#undef N 810116742Ssam} 811116742Ssam 812116742Ssamint 813116742Ssamieee80211_media2rate(int mword) 814116742Ssam{ 815116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 816116742Ssam static const int ieeerates[] = { 817116742Ssam -1, /* IFM_AUTO */ 818116742Ssam 0, /* IFM_MANUAL */ 819116742Ssam 0, /* IFM_NONE */ 820116742Ssam 2, /* IFM_IEEE80211_FH1 */ 821116742Ssam 4, /* IFM_IEEE80211_FH2 */ 822116742Ssam 2, /* IFM_IEEE80211_DS1 */ 823116742Ssam 4, /* IFM_IEEE80211_DS2 */ 824116742Ssam 11, /* IFM_IEEE80211_DS5 */ 825116742Ssam 22, /* IFM_IEEE80211_DS11 */ 826116742Ssam 44, /* IFM_IEEE80211_DS22 */ 827116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 828116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 829116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 830116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 831116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 832116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 833116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 834116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 835116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 836116742Ssam }; 837116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 838116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 839116742Ssam#undef N 840116742Ssam} 841116742Ssam 842116742Ssam/* 843116742Ssam * Module glue. 844116742Ssam * 845116742Ssam * NB: the module name is "wlan" for compatibility with NetBSD. 846116742Ssam */ 847116742Ssam 848116742Ssamstatic int 849116742Ssamieee80211_modevent(module_t mod, int type, void *unused) 850116742Ssam{ 851116742Ssam switch (type) { 852116742Ssam case MOD_LOAD: 853116742Ssam if (bootverbose) 854116742Ssam printf("wlan: <802.11 Link Layer>\n"); 855116742Ssam return 0; 856116742Ssam case MOD_UNLOAD: 857116742Ssam return 0; 858116742Ssam } 859116742Ssam return EINVAL; 860116742Ssam} 861116742Ssam 862116742Ssamstatic moduledata_t ieee80211_mod = { 863116742Ssam "wlan", 864116742Ssam ieee80211_modevent, 865116742Ssam 0 866116742Ssam}; 867116742SsamDECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 868116742SsamMODULE_VERSION(wlan, 1); 869116742SsamMODULE_DEPEND(wlan, rc4, 1, 1, 1); 870