ieee80211.c revision 179388
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3178354Ssam * Copyright (c) 2002-2008 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 179388 2008-05-28 23:10:53Z sam $"); 29116742Ssam 30116742Ssam/* 31116742Ssam * IEEE 802.11 generic handler 32116742Ssam */ 33178354Ssam#include "opt_wlan.h" 34116742Ssam 35116742Ssam#include <sys/param.h> 36116742Ssam#include <sys/systm.h> 37116742Ssam#include <sys/kernel.h> 38138568Ssam 39116742Ssam#include <sys/socket.h> 40116742Ssam 41116742Ssam#include <net/if.h> 42178354Ssam#include <net/if_dl.h> 43116742Ssam#include <net/if_media.h> 44178354Ssam#include <net/if_types.h> 45116742Ssam#include <net/ethernet.h> 46116742Ssam 47116742Ssam#include <net80211/ieee80211_var.h> 48178354Ssam#include <net80211/ieee80211_regdomain.h> 49116742Ssam 50116742Ssam#include <net/bpf.h> 51116742Ssam 52178955Ssamconst char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = { 53178955Ssam [IEEE80211_MODE_AUTO] = "auto", 54178955Ssam [IEEE80211_MODE_11A] = "11a", 55178955Ssam [IEEE80211_MODE_11B] = "11b", 56178955Ssam [IEEE80211_MODE_11G] = "11g", 57178955Ssam [IEEE80211_MODE_FH] = "FH", 58178955Ssam [IEEE80211_MODE_TURBO_A] = "turboA", 59178955Ssam [IEEE80211_MODE_TURBO_G] = "turboG", 60178955Ssam [IEEE80211_MODE_STURBO_A] = "sturboA", 61178955Ssam [IEEE80211_MODE_11NA] = "11na", 62178955Ssam [IEEE80211_MODE_11NG] = "11ng", 63116742Ssam}; 64178957Ssam/* map ieee80211_opmode to the corresponding capability bit */ 65178957Ssamconst int ieee80211_opcap[IEEE80211_OPMODE_MAX] = { 66178957Ssam [IEEE80211_M_IBSS] = IEEE80211_C_IBSS, 67178957Ssam [IEEE80211_M_WDS] = IEEE80211_C_WDS, 68178957Ssam [IEEE80211_M_STA] = IEEE80211_C_STA, 69178957Ssam [IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO, 70178957Ssam [IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP, 71178957Ssam [IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR, 72178957Ssam}; 73178957Ssam 74178354Ssamstatic const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = 75178354Ssam { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 76116742Ssam 77178354Ssamstatic void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); 78178354Ssamstatic void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); 79178354Ssamstatic int ieee80211_media_setup(struct ieee80211com *ic, 80178354Ssam struct ifmedia *media, int caps, int addsta, 81178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); 82178354Ssamstatic void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); 83178354Ssamstatic int ieee80211com_media_change(struct ifnet *); 84178354Ssamstatic int media_status(enum ieee80211_opmode, 85178354Ssam const struct ieee80211_channel *); 86178354Ssam 87178354SsamMALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); 88178354Ssam 89164645Ssam/* 90164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units). 91164645Ssam */ 92164645Ssam#define B(r) ((r) | IEEE80211_RATE_BASIC) 93164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a = 94164645Ssam { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; 95165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half = 96165569Ssam { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; 97165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter = 98165569Ssam { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; 99164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b = 100164645Ssam { 4, { B(2), B(4), B(11), B(22) } }; 101164645Ssam/* NB: OFDM rates are handled specially based on mode */ 102164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g = 103164645Ssam { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; 104164645Ssam#undef B 105164645Ssam 106140915Ssam/* 107165569Ssam * Fill in 802.11 available channel set, mark 108165569Ssam * all available channels as active, and pick 109165569Ssam * a default channel if not already specified. 110165569Ssam */ 111165569Ssamstatic void 112165569Ssamieee80211_chan_init(struct ieee80211com *ic) 113116742Ssam{ 114165569Ssam#define DEFAULTRATES(m, def) do { \ 115167468Ssam if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \ 116165574Ssam ic->ic_sup_rates[m] = def; \ 117165569Ssam} while (0) 118116742Ssam struct ieee80211_channel *c; 119116742Ssam int i; 120116742Ssam 121170530Ssam KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX, 122170530Ssam ("invalid number of channels specified: %u", ic->ic_nchans)); 123116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 124178354Ssam memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); 125167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); 126170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 127116742Ssam c = &ic->ic_channels[i]; 128170530Ssam KASSERT(c->ic_flags != 0, ("channel with no flags")); 129170530Ssam KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX, 130170530Ssam ("channel with bogus ieee number %u", c->ic_ieee)); 131170530Ssam setbit(ic->ic_chan_avail, c->ic_ieee); 132170530Ssam /* 133170530Ssam * Identify mode capabilities. 134170530Ssam */ 135170530Ssam if (IEEE80211_IS_CHAN_A(c)) 136170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11A); 137170530Ssam if (IEEE80211_IS_CHAN_B(c)) 138170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11B); 139170530Ssam if (IEEE80211_IS_CHAN_ANYG(c)) 140170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11G); 141170530Ssam if (IEEE80211_IS_CHAN_FHSS(c)) 142170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_FH); 143170530Ssam if (IEEE80211_IS_CHAN_108A(c)) 144170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); 145170530Ssam if (IEEE80211_IS_CHAN_108G(c)) 146170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); 147170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 148170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); 149170530Ssam if (IEEE80211_IS_CHAN_HTA(c)) 150170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); 151170530Ssam if (IEEE80211_IS_CHAN_HTG(c)) 152170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); 153116742Ssam } 154170530Ssam /* initialize candidate channels to all available */ 155170530Ssam memcpy(ic->ic_chan_active, ic->ic_chan_avail, 156170530Ssam sizeof(ic->ic_chan_avail)); 157164645Ssam 158178354Ssam /* sort channel table to allow lookup optimizations */ 159178354Ssam ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); 160178354Ssam 161178354Ssam /* invalidate any previous state */ 162170530Ssam ic->ic_bsschan = IEEE80211_CHAN_ANYC; 163172233Ssam ic->ic_prevchan = NULL; 164178354Ssam ic->ic_csa_newchan = NULL; 165170530Ssam /* arbitrarily pick the first channel */ 166170530Ssam ic->ic_curchan = &ic->ic_channels[0]; 167170530Ssam 168164645Ssam /* fillin well-known rate sets if driver has not specified */ 169165569Ssam DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); 170165569Ssam DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); 171165569Ssam DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); 172165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); 173165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); 174165569Ssam 175165569Ssam /* 176165569Ssam * Set auto mode to reset active channel state and any desired channel. 177165569Ssam */ 178165569Ssam (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); 179165569Ssam#undef DEFAULTRATES 180165569Ssam} 181165569Ssam 182178354Ssamstatic void 183178354Ssamnull_update_mcast(struct ifnet *ifp) 184178354Ssam{ 185178354Ssam if_printf(ifp, "need multicast update callback\n"); 186178354Ssam} 187178354Ssam 188178354Ssamstatic void 189178354Ssamnull_update_promisc(struct ifnet *ifp) 190178354Ssam{ 191178354Ssam if_printf(ifp, "need promiscuous mode update callback\n"); 192178354Ssam} 193178354Ssam 194178521Ssamstatic int 195178521Ssamnull_output(struct ifnet *ifp, struct mbuf *m, 196178521Ssam struct sockaddr *dst, struct rtentry *rt0) 197178521Ssam{ 198178521Ssam if_printf(ifp, "discard raw packet\n"); 199178521Ssam m_freem(m); 200178521Ssam return EIO; 201178521Ssam} 202178521Ssam 203178521Ssamstatic void 204178521Ssamnull_input(struct ifnet *ifp, struct mbuf *m) 205178521Ssam{ 206178521Ssam if_printf(ifp, "if_input should not be called\n"); 207178521Ssam m_freem(m); 208178521Ssam} 209178521Ssam 210178354Ssam/* 211178354Ssam * Attach/setup the common net80211 state. Called by 212178354Ssam * the driver on attach to prior to creating any vap's. 213178354Ssam */ 214165569Ssamvoid 215165569Ssamieee80211_ifattach(struct ieee80211com *ic) 216165569Ssam{ 217165569Ssam struct ifnet *ifp = ic->ic_ifp; 218178354Ssam struct sockaddr_dl *sdl; 219178354Ssam struct ifaddr *ifa; 220165569Ssam 221178354Ssam KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type)); 222165569Ssam 223179388Ssam IEEE80211_LOCK_INIT(ic, ifp->if_xname); 224178354Ssam TAILQ_INIT(&ic->ic_vaps); 225165569Ssam /* 226165569Ssam * Fill in 802.11 available channel set, mark all 227165569Ssam * available channels as active, and pick a default 228165569Ssam * channel if not already specified. 229165569Ssam */ 230178354Ssam ieee80211_media_init(ic); 231170530Ssam 232178354Ssam ic->ic_update_mcast = null_update_mcast; 233178354Ssam ic->ic_update_promisc = null_update_promisc; 234116742Ssam 235155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 236155688Ssam ic->ic_lintval = ic->ic_bintval; 237138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 238138568Ssam 239170530Ssam ieee80211_crypto_attach(ic); 240138568Ssam ieee80211_node_attach(ic); 241170530Ssam ieee80211_power_attach(ic); 242138568Ssam ieee80211_proto_attach(ic); 243170530Ssam ieee80211_ht_attach(ic); 244170530Ssam ieee80211_scan_attach(ic); 245178354Ssam ieee80211_regdomain_attach(ic); 246138568Ssam 247178354Ssam ieee80211_sysctl_attach(ic); 248138568Ssam 249178354Ssam ifp->if_addrlen = IEEE80211_ADDR_LEN; 250178354Ssam ifp->if_hdrlen = 0; 251178354Ssam if_attach(ifp); 252178354Ssam ifp->if_mtu = IEEE80211_MTU_MAX; 253178354Ssam ifp->if_broadcastaddr = ieee80211broadcastaddr; 254178521Ssam ifp->if_output = null_output; 255178521Ssam ifp->if_input = null_input; /* just in case */ 256178521Ssam ifp->if_resolvemulti = NULL; /* NB: callers check */ 257140915Ssam 258178354Ssam ifa = ifaddr_byindex(ifp->if_index); 259178354Ssam KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); 260178354Ssam sdl = (struct sockaddr_dl *)ifa->ifa_addr; 261178354Ssam sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ 262178354Ssam sdl->sdl_alen = IEEE80211_ADDR_LEN; 263178354Ssam IEEE80211_ADDR_COPY(LLADDR(sdl), ic->ic_myaddr); 264116742Ssam} 265116742Ssam 266178354Ssam/* 267178354Ssam * Detach net80211 state on device detach. Tear down 268178354Ssam * all vap's and reclaim all common state prior to the 269178354Ssam * device state going away. Note we may call back into 270178354Ssam * driver; it must be prepared for this. 271178354Ssam */ 272116742Ssamvoid 273138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 274116742Ssam{ 275138568Ssam struct ifnet *ifp = ic->ic_ifp; 276178354Ssam struct ieee80211vap *vap; 277116742Ssam 278178354Ssam /* XXX ieee80211_stop_all? */ 279178354Ssam while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) 280178354Ssam ieee80211_vap_destroy(vap); 281138568Ssam 282138568Ssam ieee80211_sysctl_detach(ic); 283178354Ssam ieee80211_regdomain_detach(ic); 284170530Ssam ieee80211_scan_detach(ic); 285170530Ssam ieee80211_ht_detach(ic); 286166012Ssam /* NB: must be called before ieee80211_node_detach */ 287138568Ssam ieee80211_proto_detach(ic); 288138568Ssam ieee80211_crypto_detach(ic); 289170530Ssam ieee80211_power_detach(ic); 290138568Ssam ieee80211_node_detach(ic); 291116742Ssam ifmedia_removeall(&ic->ic_media); 292138568Ssam 293170530Ssam IEEE80211_LOCK_DESTROY(ic); 294178354Ssam if_detach(ifp); 295178354Ssam} 296138568Ssam 297178354Ssam/* 298178354Ssam * Default reset method for use with the ioctl support. This 299178354Ssam * method is invoked after any state change in the 802.11 300178354Ssam * layer that should be propagated to the hardware but not 301178354Ssam * require re-initialization of the 802.11 state machine (e.g 302178354Ssam * rescanning for an ap). We always return ENETRESET which 303178354Ssam * should cause the driver to re-initialize the device. Drivers 304178354Ssam * can override this method to implement more optimized support. 305178354Ssam */ 306178354Ssamstatic int 307178354Ssamdefault_reset(struct ieee80211vap *vap, u_long cmd) 308178354Ssam{ 309178354Ssam return ENETRESET; 310178354Ssam} 311178354Ssam 312178354Ssam/* 313178354Ssam * Prepare a vap for use. Drivers use this call to 314178354Ssam * setup net80211 state in new vap's prior attaching 315178354Ssam * them with ieee80211_vap_attach (below). 316178354Ssam */ 317178354Ssamint 318178354Ssamieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, 319178354Ssam const char name[IFNAMSIZ], int unit, int opmode, int flags, 320178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 321178354Ssam const uint8_t macaddr[IEEE80211_ADDR_LEN]) 322178354Ssam{ 323178354Ssam struct ifnet *ifp; 324178354Ssam 325178354Ssam ifp = if_alloc(IFT_ETHER); 326178354Ssam if (ifp == NULL) { 327178354Ssam if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n", 328178354Ssam __func__); 329178354Ssam return ENOMEM; 330178354Ssam } 331178354Ssam if_initname(ifp, name, unit); 332178354Ssam ifp->if_softc = vap; /* back pointer */ 333178354Ssam ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 334178354Ssam ifp->if_start = ieee80211_start; 335178354Ssam ifp->if_ioctl = ieee80211_ioctl; 336178354Ssam ifp->if_watchdog = NULL; /* NB: no watchdog routine */ 337178354Ssam ifp->if_init = ieee80211_init; 338178354Ssam /* NB: input+output filled in by ether_ifattach */ 339178354Ssam IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 340178354Ssam ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 341178354Ssam IFQ_SET_READY(&ifp->if_snd); 342178354Ssam 343178354Ssam vap->iv_ifp = ifp; 344178354Ssam vap->iv_ic = ic; 345178354Ssam vap->iv_flags = ic->ic_flags; /* propagate common flags */ 346178354Ssam vap->iv_flags_ext = ic->ic_flags_ext; 347178354Ssam vap->iv_flags_ven = ic->ic_flags_ven; 348178354Ssam vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; 349178354Ssam vap->iv_htcaps = ic->ic_htcaps; 350178354Ssam vap->iv_opmode = opmode; 351178957Ssam vap->iv_caps |= ieee80211_opcap[opmode]; 352178354Ssam switch (opmode) { 353178354Ssam case IEEE80211_M_STA: 354178354Ssam /* auto-enable s/w beacon miss support */ 355178354Ssam if (flags & IEEE80211_CLONE_NOBEACONS) 356178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; 357178354Ssam break; 358178354Ssam case IEEE80211_M_WDS: 359178354Ssam /* 360178354Ssam * WDS links must specify the bssid of the far end. 361178354Ssam * For legacy operation this is a static relationship. 362178354Ssam * For non-legacy operation the station must associate 363178354Ssam * and be authorized to pass traffic. Plumbing the 364178354Ssam * vap to the proper node happens when the vap 365178354Ssam * transitions to RUN state. 366178354Ssam */ 367178354Ssam IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); 368178354Ssam vap->iv_flags |= IEEE80211_F_DESBSSID; 369178354Ssam if (flags & IEEE80211_CLONE_WDSLEGACY) 370178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; 371178354Ssam break; 372178354Ssam } 373178354Ssam /* 374178354Ssam * Enable various functionality by default if we're 375178354Ssam * capable; the driver can override us if it knows better. 376178354Ssam */ 377178354Ssam if (vap->iv_caps & IEEE80211_C_WME) 378178354Ssam vap->iv_flags |= IEEE80211_F_WME; 379178354Ssam if (vap->iv_caps & IEEE80211_C_BURST) 380178354Ssam vap->iv_flags |= IEEE80211_F_BURST; 381178354Ssam if (vap->iv_caps & IEEE80211_C_FF) 382178354Ssam vap->iv_flags |= IEEE80211_F_FF; 383178354Ssam if (vap->iv_caps & IEEE80211_C_TURBOP) 384178354Ssam vap->iv_flags |= IEEE80211_F_TURBOP; 385178354Ssam /* NB: bg scanning only makes sense for station mode right now */ 386178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 387178354Ssam (vap->iv_caps & IEEE80211_C_BGSCAN)) 388178354Ssam vap->iv_flags |= IEEE80211_F_BGSCAN; 389178957Ssam vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ 390178954Ssam /* NB: DFS support only makes sense for ap mode right now */ 391178954Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 392178954Ssam (vap->iv_caps & IEEE80211_C_DFS)) 393178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_DFS; 394178354Ssam 395178354Ssam vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 396178354Ssam vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 397178354Ssam vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; 398178354Ssam /* 399178354Ssam * Install a default reset method for the ioctl support; 400178354Ssam * the driver can override this. 401178354Ssam */ 402178354Ssam vap->iv_reset = default_reset; 403178354Ssam 404178354Ssam IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); 405178354Ssam 406178354Ssam ieee80211_sysctl_vattach(vap); 407178354Ssam ieee80211_crypto_vattach(vap); 408178354Ssam ieee80211_node_vattach(vap); 409178354Ssam ieee80211_power_vattach(vap); 410178354Ssam ieee80211_proto_vattach(vap); 411178354Ssam ieee80211_ht_vattach(vap); 412178354Ssam ieee80211_scan_vattach(vap); 413178354Ssam ieee80211_regdomain_vattach(vap); 414178354Ssam 415178354Ssam return 0; 416178354Ssam} 417178354Ssam 418178354Ssam/* 419178354Ssam * Activate a vap. State should have been prepared with a 420178354Ssam * call to ieee80211_vap_setup and by the driver. On return 421178354Ssam * from this call the vap is ready for use. 422178354Ssam */ 423178354Ssamint 424178354Ssamieee80211_vap_attach(struct ieee80211vap *vap, 425178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 426178354Ssam{ 427178354Ssam struct ifnet *ifp = vap->iv_ifp; 428178354Ssam struct ieee80211com *ic = vap->iv_ic; 429178354Ssam struct ifmediareq imr; 430178354Ssam int maxrate; 431178354Ssam 432178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 433178354Ssam "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", 434178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 435178354Ssam ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); 436178354Ssam 437178354Ssam /* 438178354Ssam * Do late attach work that cannot happen until after 439178354Ssam * the driver has had a chance to override defaults. 440178354Ssam */ 441178354Ssam ieee80211_node_latevattach(vap); 442178354Ssam ieee80211_power_latevattach(vap); 443178354Ssam 444178354Ssam maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, 445178354Ssam vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); 446178354Ssam ieee80211_media_status(ifp, &imr); 447178354Ssam /* NB: strip explicit mode; we're actually in autoselect */ 448178354Ssam ifmedia_set(&vap->iv_media, imr.ifm_active &~ IFM_MMASK); 449178354Ssam if (maxrate) 450178354Ssam ifp->if_baudrate = IF_Mbps(maxrate); 451178354Ssam 452178354Ssam ether_ifattach(ifp, vap->iv_myaddr); 453178354Ssam /* hook output method setup by ether_ifattach */ 454178354Ssam vap->iv_output = ifp->if_output; 455178354Ssam ifp->if_output = ieee80211_output; 456178354Ssam /* NB: if_mtu set by ether_ifattach to ETHERMTU */ 457178354Ssam bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf); 458178354Ssam 459178354Ssam IEEE80211_LOCK(ic); 460178354Ssam TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); 461178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 462178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 463178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 464178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 465178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); 466178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); 467178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 468178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 469178354Ssam IEEE80211_UNLOCK(ic); 470178354Ssam 471178354Ssam return 1; 472178354Ssam} 473178354Ssam 474178354Ssam/* 475178354Ssam * Tear down vap state and reclaim the ifnet. 476178354Ssam * The driver is assumed to have prepared for 477178354Ssam * this; e.g. by turning off interrupts for the 478178354Ssam * underlying device. 479178354Ssam */ 480178354Ssamvoid 481178354Ssamieee80211_vap_detach(struct ieee80211vap *vap) 482178354Ssam{ 483178354Ssam struct ieee80211com *ic = vap->iv_ic; 484178354Ssam struct ifnet *ifp = vap->iv_ifp; 485178354Ssam 486178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", 487178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 488178354Ssam ic->ic_ifp->if_xname); 489178354Ssam 490178354Ssam IEEE80211_LOCK(ic); 491178354Ssam /* block traffic from above */ 492178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 493178354Ssam /* 494178354Ssam * Evil hack. Clear the backpointer from the ifnet to the 495178354Ssam * vap so any requests from above will return an error or 496178354Ssam * be ignored. In particular this short-circuits requests 497178354Ssam * by the bridge to turn off promiscuous mode as a result 498178354Ssam * of calling ether_ifdetach. 499178354Ssam */ 500178354Ssam ifp->if_softc = NULL; 501178354Ssam /* 502178354Ssam * Stop the vap before detaching the ifnet. Ideally we'd 503178354Ssam * do this in the other order so the ifnet is inaccessible 504178354Ssam * while we cleanup internal state but that is hard. 505178354Ssam */ 506178354Ssam ieee80211_stop_locked(vap); 507178354Ssam 508178354Ssam /* XXX accumulate iv_stats in ic_stats? */ 509178354Ssam TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); 510178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 511178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 512178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 513178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 514178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); 515178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); 516178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 517178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 518178354Ssam IEEE80211_UNLOCK(ic); 519178354Ssam 520178354Ssam /* XXX can't hold com lock */ 521178354Ssam /* NB: bpfattach is called by ether_ifdetach and claims all taps */ 522116742Ssam ether_ifdetach(ifp); 523178354Ssam 524178354Ssam ifmedia_removeall(&vap->iv_media); 525178354Ssam 526178354Ssam ieee80211_regdomain_vdetach(vap); 527178354Ssam ieee80211_scan_vdetach(vap); 528178354Ssam ieee80211_ht_vdetach(vap); 529178354Ssam /* NB: must be before ieee80211_node_vdetach */ 530178354Ssam ieee80211_proto_vdetach(vap); 531178354Ssam ieee80211_crypto_vdetach(vap); 532178354Ssam ieee80211_power_vdetach(vap); 533178354Ssam ieee80211_node_vdetach(vap); 534178354Ssam ieee80211_sysctl_vdetach(vap); 535116742Ssam} 536116742Ssam 537178354Ssam/* 538178354Ssam * Synchronize flag bit state in the parent ifnet structure 539178354Ssam * according to the state of all vap ifnet's. This is used, 540178354Ssam * for example, to handle IFF_PROMISC and IFF_ALLMULTI. 541178354Ssam */ 542178354Ssamvoid 543178354Ssamieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) 544178354Ssam{ 545178354Ssam struct ifnet *ifp = ic->ic_ifp; 546178354Ssam struct ieee80211vap *vap; 547178354Ssam int bit, oflags; 548178354Ssam 549178354Ssam IEEE80211_LOCK_ASSERT(ic); 550178354Ssam 551178354Ssam bit = 0; 552178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 553178354Ssam if (vap->iv_ifp->if_flags & flag) { 554178354Ssam /* 555178354Ssam * XXX the bridge sets PROMISC but we don't want to 556178354Ssam * enable it on the device, discard here so all the 557178354Ssam * drivers don't need to special-case it 558178354Ssam */ 559178354Ssam if (flag == IFF_PROMISC && 560178354Ssam vap->iv_opmode == IEEE80211_M_HOSTAP) 561178354Ssam continue; 562178354Ssam bit = 1; 563178354Ssam break; 564178354Ssam } 565178354Ssam oflags = ifp->if_flags; 566178354Ssam if (bit) 567178354Ssam ifp->if_flags |= flag; 568178354Ssam else 569178354Ssam ifp->if_flags &= ~flag; 570178354Ssam if ((ifp->if_flags ^ oflags) & flag) { 571178354Ssam /* XXX should we return 1/0 and let caller do this? */ 572178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 573178354Ssam if (flag == IFF_PROMISC) 574178354Ssam ic->ic_update_promisc(ifp); 575178354Ssam else if (flag == IFF_ALLMULTI) 576178354Ssam ic->ic_update_mcast(ifp); 577178354Ssam } 578178354Ssam } 579178354Ssam} 580178354Ssam 581178354Ssam/* 582178354Ssam * Synchronize flag bit state in the com structure 583178354Ssam * according to the state of all vap's. This is used, 584178354Ssam * for example, to handle state changes via ioctls. 585178354Ssam */ 586178354Ssamstatic void 587178354Ssamieee80211_syncflag_locked(struct ieee80211com *ic, int flag) 588178354Ssam{ 589178354Ssam struct ieee80211vap *vap; 590178354Ssam int bit; 591178354Ssam 592178354Ssam IEEE80211_LOCK_ASSERT(ic); 593178354Ssam 594178354Ssam bit = 0; 595178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 596178354Ssam if (vap->iv_flags & flag) { 597178354Ssam bit = 1; 598178354Ssam break; 599178354Ssam } 600178354Ssam if (bit) 601178354Ssam ic->ic_flags |= flag; 602178354Ssam else 603178354Ssam ic->ic_flags &= ~flag; 604178354Ssam} 605178354Ssam 606178354Ssamvoid 607178354Ssamieee80211_syncflag(struct ieee80211vap *vap, int flag) 608178354Ssam{ 609178354Ssam struct ieee80211com *ic = vap->iv_ic; 610178354Ssam 611178354Ssam IEEE80211_LOCK(ic); 612178354Ssam if (flag < 0) { 613178354Ssam flag = -flag; 614178354Ssam vap->iv_flags &= ~flag; 615178354Ssam } else 616178354Ssam vap->iv_flags |= flag; 617178354Ssam ieee80211_syncflag_locked(ic, flag); 618178354Ssam IEEE80211_UNLOCK(ic); 619178354Ssam} 620178354Ssam 621178354Ssam/* 622178354Ssam * Synchronize flag bit state in the com structure 623178354Ssam * according to the state of all vap's. This is used, 624178354Ssam * for example, to handle state changes via ioctls. 625178354Ssam */ 626178354Ssamstatic void 627178354Ssamieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) 628178354Ssam{ 629178354Ssam struct ieee80211vap *vap; 630178354Ssam int bit; 631178354Ssam 632178354Ssam IEEE80211_LOCK_ASSERT(ic); 633178354Ssam 634178354Ssam bit = 0; 635178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 636178354Ssam if (vap->iv_flags_ext & flag) { 637178354Ssam bit = 1; 638178354Ssam break; 639178354Ssam } 640178354Ssam if (bit) 641178354Ssam ic->ic_flags_ext |= flag; 642178354Ssam else 643178354Ssam ic->ic_flags_ext &= ~flag; 644178354Ssam} 645178354Ssam 646178354Ssamvoid 647178354Ssamieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) 648178354Ssam{ 649178354Ssam struct ieee80211com *ic = vap->iv_ic; 650178354Ssam 651178354Ssam IEEE80211_LOCK(ic); 652178354Ssam if (flag < 0) { 653178354Ssam flag = -flag; 654178354Ssam vap->iv_flags_ext &= ~flag; 655178354Ssam } else 656178354Ssam vap->iv_flags_ext |= flag; 657178354Ssam ieee80211_syncflag_ext_locked(ic, flag); 658178354Ssam IEEE80211_UNLOCK(ic); 659178354Ssam} 660178354Ssam 661166012Ssamstatic __inline int 662166012Ssammapgsm(u_int freq, u_int flags) 663166012Ssam{ 664166012Ssam freq *= 10; 665166012Ssam if (flags & IEEE80211_CHAN_QUARTER) 666166012Ssam freq += 5; 667166012Ssam else if (flags & IEEE80211_CHAN_HALF) 668166012Ssam freq += 10; 669166012Ssam else 670166012Ssam freq += 20; 671166012Ssam /* NB: there is no 907/20 wide but leave room */ 672166012Ssam return (freq - 906*10) / 5; 673166012Ssam} 674166012Ssam 675166012Ssamstatic __inline int 676166012Ssammappsb(u_int freq, u_int flags) 677166012Ssam{ 678166012Ssam return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 679166012Ssam} 680166012Ssam 681116742Ssam/* 682116742Ssam * Convert MHz frequency to IEEE channel number. 683116742Ssam */ 684152450Ssamint 685116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 686116742Ssam{ 687167430Ssam#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) 688166012Ssam if (flags & IEEE80211_CHAN_GSM) 689166012Ssam return mapgsm(freq, flags); 690116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 691116742Ssam if (freq == 2484) 692116742Ssam return 14; 693116742Ssam if (freq < 2484) 694152450Ssam return ((int) freq - 2407) / 5; 695116742Ssam else 696116742Ssam return 15 + ((freq - 2512) / 20); 697116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 698165569Ssam if (freq <= 5000) { 699170530Ssam /* XXX check regdomain? */ 700167430Ssam if (IS_FREQ_IN_PSB(freq)) 701166012Ssam return mappsb(freq, flags); 702152450Ssam return (freq - 4000) / 5; 703165569Ssam } else 704152450Ssam return (freq - 5000) / 5; 705116742Ssam } else { /* either, guess */ 706116742Ssam if (freq == 2484) 707116742Ssam return 14; 708166012Ssam if (freq < 2484) { 709166012Ssam if (907 <= freq && freq <= 922) 710166012Ssam return mapgsm(freq, flags); 711152450Ssam return ((int) freq - 2407) / 5; 712166012Ssam } 713152450Ssam if (freq < 5000) { 714167430Ssam if (IS_FREQ_IN_PSB(freq)) 715166012Ssam return mappsb(freq, flags); 716165569Ssam else if (freq > 4900) 717152450Ssam return (freq - 4000) / 5; 718152450Ssam else 719152450Ssam return 15 + ((freq - 2512) / 20); 720152450Ssam } 721116742Ssam return (freq - 5000) / 5; 722116742Ssam } 723167430Ssam#undef IS_FREQ_IN_PSB 724116742Ssam} 725116742Ssam 726116742Ssam/* 727116742Ssam * Convert channel to IEEE channel number. 728116742Ssam */ 729152450Ssamint 730165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) 731116742Ssam{ 732170530Ssam if (c == NULL) { 733138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 734117039Ssam return 0; /* XXX */ 735116742Ssam } 736170530Ssam return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); 737116742Ssam} 738116742Ssam 739116742Ssam/* 740116742Ssam * Convert IEEE channel number to MHz frequency. 741116742Ssam */ 742116742Ssamu_int 743116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 744116742Ssam{ 745166012Ssam if (flags & IEEE80211_CHAN_GSM) 746166012Ssam return 907 + 5 * (chan / 10); 747116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 748116742Ssam if (chan == 14) 749116742Ssam return 2484; 750116742Ssam if (chan < 14) 751116742Ssam return 2407 + chan*5; 752116742Ssam else 753116742Ssam return 2512 + ((chan-15)*20); 754116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 755165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 756165569Ssam chan -= 37; 757165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 758165569Ssam } 759116742Ssam return 5000 + (chan*5); 760116742Ssam } else { /* either, guess */ 761166012Ssam /* XXX can't distinguish PSB+GSM channels */ 762116742Ssam if (chan == 14) 763116742Ssam return 2484; 764116742Ssam if (chan < 14) /* 0-13 */ 765116742Ssam return 2407 + chan*5; 766116742Ssam if (chan < 27) /* 15-26 */ 767116742Ssam return 2512 + ((chan-15)*20); 768116742Ssam return 5000 + (chan*5); 769116742Ssam } 770116742Ssam} 771116742Ssam 772116742Ssam/* 773170530Ssam * Locate a channel given a frequency+flags. We cache 774178354Ssam * the previous lookup to optimize switching between two 775170530Ssam * channels--as happens with dynamic turbo. 776170530Ssam */ 777170530Ssamstruct ieee80211_channel * 778170530Ssamieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) 779170530Ssam{ 780170530Ssam struct ieee80211_channel *c; 781170530Ssam int i; 782170530Ssam 783170530Ssam flags &= IEEE80211_CHAN_ALLTURBO; 784170530Ssam c = ic->ic_prevchan; 785170530Ssam if (c != NULL && c->ic_freq == freq && 786170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 787170530Ssam return c; 788170530Ssam /* brute force search */ 789170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 790170530Ssam c = &ic->ic_channels[i]; 791170530Ssam if (c->ic_freq == freq && 792170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 793170530Ssam return c; 794170530Ssam } 795170530Ssam return NULL; 796170530Ssam} 797170530Ssam 798173861Ssam/* 799173861Ssam * Locate a channel given a channel number+flags. We cache 800173861Ssam * the previous lookup to optimize switching between two 801173861Ssam * channels--as happens with dynamic turbo. 802173861Ssam */ 803173861Ssamstruct ieee80211_channel * 804173861Ssamieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) 805173861Ssam{ 806173861Ssam struct ieee80211_channel *c; 807173861Ssam int i; 808173861Ssam 809173861Ssam flags &= IEEE80211_CHAN_ALLTURBO; 810173861Ssam c = ic->ic_prevchan; 811173861Ssam if (c != NULL && c->ic_ieee == ieee && 812173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 813173861Ssam return c; 814173861Ssam /* brute force search */ 815173861Ssam for (i = 0; i < ic->ic_nchans; i++) { 816173861Ssam c = &ic->ic_channels[i]; 817173861Ssam if (c->ic_ieee == ieee && 818173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 819173861Ssam return c; 820173861Ssam } 821173861Ssam return NULL; 822173861Ssam} 823173861Ssam 824170530Ssamstatic void 825178354Ssamaddmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) 826170530Ssam{ 827170530Ssam#define ADD(_ic, _s, _o) \ 828178354Ssam ifmedia_add(media, \ 829170530Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 830170530Ssam static const u_int mopts[IEEE80211_MODE_MAX] = { 831178354Ssam IFM_AUTO, 832178354Ssam IFM_IEEE80211_11A, 833178354Ssam IFM_IEEE80211_11B, 834178354Ssam IFM_IEEE80211_11G, 835178354Ssam IFM_IEEE80211_FH, 836178354Ssam IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 837178354Ssam IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, 838178354Ssam IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, 839178354Ssam IFM_IEEE80211_11NA, 840178354Ssam IFM_IEEE80211_11NG, 841170530Ssam }; 842170530Ssam u_int mopt; 843170530Ssam 844170530Ssam mopt = mopts[mode]; 845178354Ssam if (addsta) 846178354Ssam ADD(ic, mword, mopt); /* STA mode has no cap */ 847178354Ssam if (caps & IEEE80211_C_IBSS) 848178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); 849178354Ssam if (caps & IEEE80211_C_HOSTAP) 850178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); 851178354Ssam if (caps & IEEE80211_C_AHDEMO) 852178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 853178354Ssam if (caps & IEEE80211_C_MONITOR) 854178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); 855178354Ssam if (caps & IEEE80211_C_WDS) 856178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_WDS); 857170530Ssam#undef ADD 858170530Ssam} 859170530Ssam 860170530Ssam/* 861116742Ssam * Setup the media data structures according to the channel and 862178354Ssam * rate tables. 863116742Ssam */ 864178354Ssamstatic int 865178354Ssamieee80211_media_setup(struct ieee80211com *ic, 866178354Ssam struct ifmedia *media, int caps, int addsta, 867116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 868116742Ssam{ 869170530Ssam int i, j, mode, rate, maxrate, mword, r; 870170530Ssam const struct ieee80211_rateset *rs; 871116742Ssam struct ieee80211_rateset allrates; 872116742Ssam 873118887Ssam /* 874116742Ssam * Fill in media characteristics. 875116742Ssam */ 876178354Ssam ifmedia_init(media, 0, media_change, media_stat); 877116742Ssam maxrate = 0; 878170530Ssam /* 879170530Ssam * Add media for legacy operating modes. 880170530Ssam */ 881116742Ssam memset(&allrates, 0, sizeof(allrates)); 882170530Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { 883167468Ssam if (isclr(ic->ic_modecaps, mode)) 884116742Ssam continue; 885178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 886116742Ssam if (mode == IEEE80211_MODE_AUTO) 887116742Ssam continue; 888116742Ssam rs = &ic->ic_sup_rates[mode]; 889116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 890116742Ssam rate = rs->rs_rates[i]; 891116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 892116742Ssam if (mword == 0) 893116742Ssam continue; 894178354Ssam addmedia(media, caps, addsta, mode, mword); 895116742Ssam /* 896170530Ssam * Add legacy rate to the collection of all rates. 897116742Ssam */ 898116742Ssam r = rate & IEEE80211_RATE_VAL; 899116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 900116742Ssam if (allrates.rs_rates[j] == r) 901116742Ssam break; 902116742Ssam if (j == allrates.rs_nrates) { 903116742Ssam /* unique, add to the set */ 904116742Ssam allrates.rs_rates[j] = r; 905116742Ssam allrates.rs_nrates++; 906116742Ssam } 907116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 908116742Ssam if (rate > maxrate) 909116742Ssam maxrate = rate; 910116742Ssam } 911116742Ssam } 912116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 913116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 914116742Ssam IEEE80211_MODE_AUTO); 915116742Ssam if (mword == 0) 916116742Ssam continue; 917170530Ssam /* NB: remove media options from mword */ 918178354Ssam addmedia(media, caps, addsta, 919178354Ssam IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); 920116742Ssam } 921170530Ssam /* 922170530Ssam * Add HT/11n media. Note that we do not have enough 923170530Ssam * bits in the media subtype to express the MCS so we 924170530Ssam * use a "placeholder" media subtype and any fixed MCS 925170530Ssam * must be specified with a different mechanism. 926170530Ssam */ 927170530Ssam for (; mode < IEEE80211_MODE_MAX; mode++) { 928170530Ssam if (isclr(ic->ic_modecaps, mode)) 929170530Ssam continue; 930178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 931178354Ssam addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); 932170530Ssam } 933170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 934170530Ssam isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 935178354Ssam addmedia(media, caps, addsta, 936178354Ssam IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); 937170530Ssam /* XXX could walk htrates */ 938170530Ssam /* XXX known array size */ 939178354Ssam if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) 940178354Ssam maxrate = ieee80211_htrates[15].ht40_rate_400ns; 941170530Ssam } 942178354Ssam return maxrate; 943178354Ssam} 944116742Ssam 945178354Ssamvoid 946178354Ssamieee80211_media_init(struct ieee80211com *ic) 947178354Ssam{ 948178354Ssam struct ifnet *ifp = ic->ic_ifp; 949178354Ssam int maxrate; 950178354Ssam 951178354Ssam /* NB: this works because the structure is initialized to zero */ 952178354Ssam if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { 953178354Ssam /* 954178354Ssam * We are re-initializing the channel list; clear 955178354Ssam * the existing media state as the media routines 956178354Ssam * don't suppress duplicates. 957178354Ssam */ 958178354Ssam ifmedia_removeall(&ic->ic_media); 959178354Ssam } 960178354Ssam ieee80211_chan_init(ic); 961178354Ssam 962178354Ssam /* 963178354Ssam * Recalculate media settings in case new channel list changes 964178354Ssam * the set of available modes. 965178354Ssam */ 966178354Ssam maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, 967178354Ssam ieee80211com_media_change, ieee80211com_media_status); 968170530Ssam /* NB: strip explicit mode; we're actually in autoselect */ 969170530Ssam ifmedia_set(&ic->ic_media, 970170530Ssam media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK); 971116742Ssam if (maxrate) 972116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 973178354Ssam 974178354Ssam /* XXX need to propagate new media settings to vap's */ 975116742Ssam} 976116742Ssam 977165569Ssamconst struct ieee80211_rateset * 978165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 979165569Ssam{ 980166012Ssam if (IEEE80211_IS_CHAN_HALF(c)) 981166012Ssam return &ieee80211_rateset_half; 982166012Ssam if (IEEE80211_IS_CHAN_QUARTER(c)) 983166012Ssam return &ieee80211_rateset_quarter; 984170530Ssam if (IEEE80211_IS_CHAN_HTA(c)) 985170530Ssam return &ic->ic_sup_rates[IEEE80211_MODE_11A]; 986170530Ssam if (IEEE80211_IS_CHAN_HTG(c)) { 987170530Ssam /* XXX does this work for basic rates? */ 988170530Ssam return &ic->ic_sup_rates[IEEE80211_MODE_11G]; 989170530Ssam } 990170530Ssam return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; 991165569Ssam} 992165569Ssam 993138568Ssamvoid 994138568Ssamieee80211_announce(struct ieee80211com *ic) 995138568Ssam{ 996138568Ssam struct ifnet *ifp = ic->ic_ifp; 997138568Ssam int i, mode, rate, mword; 998170530Ssam const struct ieee80211_rateset *rs; 999138568Ssam 1000172227Ssam /* NB: skip AUTO since it has no rates */ 1001172227Ssam for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { 1002167468Ssam if (isclr(ic->ic_modecaps, mode)) 1003138568Ssam continue; 1004138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 1005138568Ssam rs = &ic->ic_sup_rates[mode]; 1006138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 1007170530Ssam mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 1008138568Ssam if (mword == 0) 1009138568Ssam continue; 1010170530Ssam rate = ieee80211_media2rate(mword); 1011138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 1012170530Ssam rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 1013138568Ssam } 1014138568Ssam printf("\n"); 1015138568Ssam } 1016170530Ssam ieee80211_ht_announce(ic); 1017138568Ssam} 1018138568Ssam 1019170530Ssamvoid 1020170530Ssamieee80211_announce_channels(struct ieee80211com *ic) 1021116742Ssam{ 1022170530Ssam const struct ieee80211_channel *c; 1023170530Ssam char type; 1024170530Ssam int i, cw; 1025170530Ssam 1026170530Ssam printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); 1027170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 1028170530Ssam c = &ic->ic_channels[i]; 1029170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 1030170530Ssam type = 'S'; 1031170530Ssam else if (IEEE80211_IS_CHAN_108A(c)) 1032170530Ssam type = 'T'; 1033170530Ssam else if (IEEE80211_IS_CHAN_108G(c)) 1034170530Ssam type = 'G'; 1035170530Ssam else if (IEEE80211_IS_CHAN_HT(c)) 1036170530Ssam type = 'n'; 1037170530Ssam else if (IEEE80211_IS_CHAN_A(c)) 1038170530Ssam type = 'a'; 1039170530Ssam else if (IEEE80211_IS_CHAN_ANYG(c)) 1040170530Ssam type = 'g'; 1041170530Ssam else if (IEEE80211_IS_CHAN_B(c)) 1042170530Ssam type = 'b'; 1043170530Ssam else 1044170530Ssam type = 'f'; 1045170530Ssam if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) 1046170530Ssam cw = 40; 1047170530Ssam else if (IEEE80211_IS_CHAN_HALF(c)) 1048170530Ssam cw = 10; 1049170530Ssam else if (IEEE80211_IS_CHAN_QUARTER(c)) 1050170530Ssam cw = 5; 1051170530Ssam else 1052170530Ssam cw = 20; 1053170530Ssam printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" 1054170530Ssam , c->ic_ieee, c->ic_freq, type 1055170530Ssam , cw 1056170530Ssam , IEEE80211_IS_CHAN_HT40U(c) ? '+' : 1057170530Ssam IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' 1058170530Ssam , c->ic_maxregpower 1059170530Ssam , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 1060170530Ssam , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 1061170530Ssam ); 1062170530Ssam } 1063116742Ssam} 1064116742Ssam 1065170530Ssamstatic int 1066178354Ssammedia2mode(const struct ieee80211com *ic, 1067178354Ssam const struct ifmedia_entry *ime, enum ieee80211_phymode *mode) 1068170530Ssam{ 1069116742Ssam switch (IFM_MODE(ime->ifm_media)) { 1070116742Ssam case IFM_IEEE80211_11A: 1071178354Ssam *mode = IEEE80211_MODE_11A; 1072116742Ssam break; 1073116742Ssam case IFM_IEEE80211_11B: 1074178354Ssam *mode = IEEE80211_MODE_11B; 1075116742Ssam break; 1076116742Ssam case IFM_IEEE80211_11G: 1077178354Ssam *mode = IEEE80211_MODE_11G; 1078116742Ssam break; 1079124543Sonoe case IFM_IEEE80211_FH: 1080178354Ssam *mode = IEEE80211_MODE_FH; 1081124543Sonoe break; 1082170530Ssam case IFM_IEEE80211_11NA: 1083178354Ssam *mode = IEEE80211_MODE_11NA; 1084170530Ssam break; 1085170530Ssam case IFM_IEEE80211_11NG: 1086178354Ssam *mode = IEEE80211_MODE_11NG; 1087170530Ssam break; 1088116742Ssam case IFM_AUTO: 1089178354Ssam *mode = IEEE80211_MODE_AUTO; 1090116742Ssam break; 1091116742Ssam default: 1092178354Ssam return 0; 1093116742Ssam } 1094116742Ssam /* 1095138568Ssam * Turbo mode is an ``option''. 1096138568Ssam * XXX does not apply to AUTO 1097116742Ssam */ 1098116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 1099178354Ssam if (*mode == IEEE80211_MODE_11A) { 1100170530Ssam if (ic->ic_flags & IEEE80211_F_TURBOP) 1101178354Ssam *mode = IEEE80211_MODE_TURBO_A; 1102170530Ssam else 1103178354Ssam *mode = IEEE80211_MODE_STURBO_A; 1104178354Ssam } else if (*mode == IEEE80211_MODE_11G) 1105178354Ssam *mode = IEEE80211_MODE_TURBO_G; 1106138568Ssam else 1107178354Ssam return 0; 1108116742Ssam } 1109170530Ssam /* XXX HT40 +/- */ 1110178354Ssam return 1; 1111178354Ssam} 1112116742Ssam 1113178354Ssam/* 1114178354Ssam * Handle a media change request on the underlying 1115178354Ssam * interface; we accept mode changes only. 1116178354Ssam */ 1117178354Ssamint 1118178354Ssamieee80211com_media_change(struct ifnet *ifp) 1119178354Ssam{ 1120178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1121178354Ssam struct ifmedia_entry *ime = ic->ic_media.ifm_cur; 1122178354Ssam enum ieee80211_phymode newphymode; 1123178354Ssam int error = 0; 1124178354Ssam 1125116742Ssam /* 1126178354Ssam * First, identify the phy mode. 1127116742Ssam */ 1128178354Ssam if (!media2mode(ic, ime, &newphymode)) 1129178354Ssam return EINVAL; 1130178354Ssam /* NB: mode must be supported, no need to check */ 1131116742Ssam 1132116742Ssam /* 1133116742Ssam * Handle phy mode change. 1134116742Ssam */ 1135178354Ssam IEEE80211_LOCK(ic); 1136178354Ssam if (ic->ic_curmode != newphymode) { /* change phy mode */ 1137178354Ssam struct ieee80211vap *vap; 1138116742Ssam 1139178354Ssam (void) ieee80211_setmode(ic, newphymode); 1140178354Ssam /* 1141178354Ssam * Propagate new state to each vap. 1142178354Ssam */ 1143178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1144178354Ssam } 1145116742Ssam } 1146178354Ssam IEEE80211_UNLOCK(ic); 1147178354Ssam return error; 1148178354Ssam} 1149116742Ssam 1150178354Ssamstatic int 1151178354Ssamfindrate(const struct ieee80211com *ic, enum ieee80211_phymode m, int r) 1152178354Ssam{ 1153178354Ssam int i, nrates; 1154178354Ssam 1155178354Ssam for (i = 0, nrates = ic->ic_sup_rates[m].rs_nrates; i < nrates; i++) 1156178354Ssam if ((ic->ic_sup_rates[m].rs_rates[i] & IEEE80211_RATE_VAL) == r) 1157178354Ssam return i; 1158178354Ssam return -1; 1159178354Ssam} 1160178354Ssam 1161178354Ssam/* 1162178354Ssam * Handle a media change request on the vap interface. 1163178354Ssam */ 1164178354Ssamint 1165178354Ssamieee80211_media_change(struct ifnet *ifp) 1166178354Ssam{ 1167178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1168178354Ssam struct ifmedia_entry *ime = vap->iv_media.ifm_cur; 1169178354Ssam struct ieee80211com *ic = vap->iv_ic; 1170178354Ssam int newrate; 1171178354Ssam 1172178354Ssam /* XXX this won't work unless ic_curmode is != IEEE80211_MODE_AUTO */ 1173178354Ssam if (ic->ic_curmode == IEEE80211_MODE_AUTO) 1174178354Ssam return EINVAL; 1175178354Ssam if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { 1176138568Ssam /* 1177178354Ssam * NB: this can only be used to specify a legacy rate. 1178138568Ssam */ 1179178354Ssam newrate = ieee80211_media2rate(ime->ifm_media); 1180178354Ssam if (newrate == 0) 1181178354Ssam return EINVAL; 1182178354Ssam if (findrate(ic, ic->ic_curmode, newrate) == -1) 1183178354Ssam return EINVAL; 1184178354Ssam } else { 1185178354Ssam newrate = IEEE80211_FIXED_RATE_NONE; 1186116742Ssam } 1187178354Ssam if (newrate != vap->iv_txparms[ic->ic_curmode].ucastrate) { 1188178354Ssam vap->iv_txparms[ic->ic_curmode].ucastrate = newrate; 1189178354Ssam return ENETRESET; 1190178354Ssam } 1191178354Ssam return 0; 1192116742Ssam} 1193116742Ssam 1194170530Ssam/* 1195170530Ssam * Common code to calculate the media status word 1196170530Ssam * from the operating mode and channel state. 1197170530Ssam */ 1198170530Ssamstatic int 1199170530Ssammedia_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) 1200170530Ssam{ 1201170530Ssam int status; 1202170530Ssam 1203170530Ssam status = IFM_IEEE80211; 1204170530Ssam switch (opmode) { 1205170530Ssam case IEEE80211_M_STA: 1206170530Ssam break; 1207170530Ssam case IEEE80211_M_IBSS: 1208170530Ssam status |= IFM_IEEE80211_ADHOC; 1209170530Ssam break; 1210170530Ssam case IEEE80211_M_HOSTAP: 1211170530Ssam status |= IFM_IEEE80211_HOSTAP; 1212170530Ssam break; 1213170530Ssam case IEEE80211_M_MONITOR: 1214170530Ssam status |= IFM_IEEE80211_MONITOR; 1215170530Ssam break; 1216170530Ssam case IEEE80211_M_AHDEMO: 1217170530Ssam status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; 1218170530Ssam break; 1219170530Ssam case IEEE80211_M_WDS: 1220178354Ssam status |= IFM_IEEE80211_WDS; 1221170530Ssam break; 1222170530Ssam } 1223170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) { 1224170530Ssam status |= IFM_IEEE80211_11NA; 1225170530Ssam } else if (IEEE80211_IS_CHAN_HTG(chan)) { 1226170530Ssam status |= IFM_IEEE80211_11NG; 1227170530Ssam } else if (IEEE80211_IS_CHAN_A(chan)) { 1228170530Ssam status |= IFM_IEEE80211_11A; 1229170530Ssam } else if (IEEE80211_IS_CHAN_B(chan)) { 1230170530Ssam status |= IFM_IEEE80211_11B; 1231170530Ssam } else if (IEEE80211_IS_CHAN_ANYG(chan)) { 1232170530Ssam status |= IFM_IEEE80211_11G; 1233170530Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) { 1234170530Ssam status |= IFM_IEEE80211_FH; 1235170530Ssam } 1236170530Ssam /* XXX else complain? */ 1237170530Ssam 1238170530Ssam if (IEEE80211_IS_CHAN_TURBO(chan)) 1239170530Ssam status |= IFM_IEEE80211_TURBO; 1240178354Ssam#if 0 1241178354Ssam if (IEEE80211_IS_CHAN_HT20(chan)) 1242178354Ssam status |= IFM_IEEE80211_HT20; 1243178354Ssam if (IEEE80211_IS_CHAN_HT40(chan)) 1244178354Ssam status |= IFM_IEEE80211_HT40; 1245178354Ssam#endif 1246170530Ssam return status; 1247170530Ssam} 1248170530Ssam 1249178354Ssamstatic void 1250178354Ssamieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1251178354Ssam{ 1252178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1253178354Ssam struct ieee80211vap *vap; 1254178354Ssam 1255178354Ssam imr->ifm_status = IFM_AVALID; 1256178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1257178354Ssam if (vap->iv_ifp->if_flags & IFF_UP) { 1258178354Ssam imr->ifm_status |= IFM_ACTIVE; 1259178354Ssam break; 1260178354Ssam } 1261178354Ssam imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); 1262178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1263178354Ssam imr->ifm_current = imr->ifm_active; 1264178354Ssam} 1265178354Ssam 1266116742Ssamvoid 1267116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1268116742Ssam{ 1269178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1270178354Ssam struct ieee80211com *ic = vap->iv_ic; 1271170530Ssam enum ieee80211_phymode mode; 1272116742Ssam 1273116742Ssam imr->ifm_status = IFM_AVALID; 1274170530Ssam /* 1275170530Ssam * NB: use the current channel's mode to lock down a xmit 1276170530Ssam * rate only when running; otherwise we may have a mismatch 1277170530Ssam * in which case the rate will not be convertible. 1278170530Ssam */ 1279178354Ssam if (vap->iv_state == IEEE80211_S_RUN) { 1280116742Ssam imr->ifm_status |= IFM_ACTIVE; 1281170530Ssam mode = ieee80211_chan2mode(ic->ic_curchan); 1282170530Ssam } else 1283170530Ssam mode = IEEE80211_MODE_AUTO; 1284178354Ssam imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); 1285138568Ssam /* 1286138568Ssam * Calculate a current rate if possible. 1287138568Ssam */ 1288178354Ssam if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { 1289138568Ssam /* 1290138568Ssam * A fixed rate is set, report that. 1291138568Ssam */ 1292138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1293178354Ssam vap->iv_txparms[mode].ucastrate, mode); 1294178354Ssam } else if (vap->iv_opmode == IEEE80211_M_STA) { 1295138568Ssam /* 1296138568Ssam * In station mode report the current transmit rate. 1297138568Ssam */ 1298138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1299178354Ssam vap->iv_bss->ni_txrate, mode); 1300128966Sandre } else 1301138568Ssam imr->ifm_active |= IFM_AUTO; 1302178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1303178354Ssam imr->ifm_current = imr->ifm_active; 1304116742Ssam} 1305116742Ssam 1306116742Ssam/* 1307116742Ssam * Set the current phy mode and recalculate the active channel 1308116742Ssam * set based on the available channels for this mode. Also 1309116742Ssam * select a new default/current channel if the current one is 1310116742Ssam * inappropriate for this mode. 1311116742Ssam */ 1312116742Ssamint 1313116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 1314116742Ssam{ 1315116742Ssam /* 1316166012Ssam * Adjust basic rates in 11b/11g supported rate set. 1317166012Ssam * Note that if operating on a hal/quarter rate channel 1318166012Ssam * this is a noop as those rates sets are different 1319166012Ssam * and used instead. 1320116742Ssam */ 1321166012Ssam if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 1322178354Ssam ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); 1323166012Ssam 1324116742Ssam ic->ic_curmode = mode; 1325138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 1326138568Ssam 1327116742Ssam return 0; 1328116742Ssam} 1329116742Ssam 1330116742Ssam/* 1331170530Ssam * Return the phy mode for with the specified channel. 1332116742Ssam */ 1333116742Ssamenum ieee80211_phymode 1334170530Ssamieee80211_chan2mode(const struct ieee80211_channel *chan) 1335116742Ssam{ 1336170530Ssam 1337170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) 1338170530Ssam return IEEE80211_MODE_11NA; 1339170530Ssam else if (IEEE80211_IS_CHAN_HTG(chan)) 1340170530Ssam return IEEE80211_MODE_11NG; 1341170530Ssam else if (IEEE80211_IS_CHAN_108G(chan)) 1342170530Ssam return IEEE80211_MODE_TURBO_G; 1343170530Ssam else if (IEEE80211_IS_CHAN_ST(chan)) 1344170530Ssam return IEEE80211_MODE_STURBO_A; 1345170530Ssam else if (IEEE80211_IS_CHAN_TURBO(chan)) 1346153350Ssam return IEEE80211_MODE_TURBO_A; 1347170530Ssam else if (IEEE80211_IS_CHAN_A(chan)) 1348116742Ssam return IEEE80211_MODE_11A; 1349170530Ssam else if (IEEE80211_IS_CHAN_ANYG(chan)) 1350116742Ssam return IEEE80211_MODE_11G; 1351170530Ssam else if (IEEE80211_IS_CHAN_B(chan)) 1352116742Ssam return IEEE80211_MODE_11B; 1353170530Ssam else if (IEEE80211_IS_CHAN_FHSS(chan)) 1354170530Ssam return IEEE80211_MODE_FH; 1355170530Ssam 1356170530Ssam /* NB: should not get here */ 1357170530Ssam printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", 1358170530Ssam __func__, chan->ic_freq, chan->ic_flags); 1359170530Ssam return IEEE80211_MODE_11B; 1360116742Ssam} 1361116742Ssam 1362170530Ssamstruct ratemedia { 1363170530Ssam u_int match; /* rate + mode */ 1364170530Ssam u_int media; /* if_media rate */ 1365170530Ssam}; 1366170530Ssam 1367170530Ssamstatic int 1368170530Ssamfindmedia(const struct ratemedia rates[], int n, u_int match) 1369170530Ssam{ 1370170530Ssam int i; 1371170530Ssam 1372170530Ssam for (i = 0; i < n; i++) 1373170530Ssam if (rates[i].match == match) 1374170530Ssam return rates[i].media; 1375170530Ssam return IFM_AUTO; 1376170530Ssam} 1377170530Ssam 1378116742Ssam/* 1379170530Ssam * Convert IEEE80211 rate value to ifmedia subtype. 1380170530Ssam * Rate is either a legacy rate in units of 0.5Mbps 1381170530Ssam * or an MCS index. 1382116742Ssam */ 1383116742Ssamint 1384116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1385116742Ssam{ 1386116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1387170530Ssam static const struct ratemedia rates[] = { 1388124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1389124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1390124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1391124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1392124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1393124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1394124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1395124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1396124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1397124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1398124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1399124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1400124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1401124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1402124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1403124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1404124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1405124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1406124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1407124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1408124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1409124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1410124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1411124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1412124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1413124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1414124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1415165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1416165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1417165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1418116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 1419116742Ssam }; 1420170530Ssam static const struct ratemedia htrates[] = { 1421170530Ssam { 0, IFM_IEEE80211_MCS }, 1422170530Ssam { 1, IFM_IEEE80211_MCS }, 1423170530Ssam { 2, IFM_IEEE80211_MCS }, 1424170530Ssam { 3, IFM_IEEE80211_MCS }, 1425170530Ssam { 4, IFM_IEEE80211_MCS }, 1426170530Ssam { 5, IFM_IEEE80211_MCS }, 1427170530Ssam { 6, IFM_IEEE80211_MCS }, 1428170530Ssam { 7, IFM_IEEE80211_MCS }, 1429170530Ssam { 8, IFM_IEEE80211_MCS }, 1430170530Ssam { 9, IFM_IEEE80211_MCS }, 1431170530Ssam { 10, IFM_IEEE80211_MCS }, 1432170530Ssam { 11, IFM_IEEE80211_MCS }, 1433170530Ssam { 12, IFM_IEEE80211_MCS }, 1434170530Ssam { 13, IFM_IEEE80211_MCS }, 1435170530Ssam { 14, IFM_IEEE80211_MCS }, 1436170530Ssam { 15, IFM_IEEE80211_MCS }, 1437170530Ssam }; 1438170530Ssam int m; 1439116742Ssam 1440170530Ssam /* 1441170530Ssam * Check 11n rates first for match as an MCS. 1442170530Ssam */ 1443170530Ssam if (mode == IEEE80211_MODE_11NA) { 1444172226Ssam if (rate & IEEE80211_RATE_MCS) { 1445172226Ssam rate &= ~IEEE80211_RATE_MCS; 1446170530Ssam m = findmedia(htrates, N(htrates), rate); 1447170530Ssam if (m != IFM_AUTO) 1448170530Ssam return m | IFM_IEEE80211_11NA; 1449170530Ssam } 1450170530Ssam } else if (mode == IEEE80211_MODE_11NG) { 1451170530Ssam /* NB: 12 is ambiguous, it will be treated as an MCS */ 1452172226Ssam if (rate & IEEE80211_RATE_MCS) { 1453172226Ssam rate &= ~IEEE80211_RATE_MCS; 1454170530Ssam m = findmedia(htrates, N(htrates), rate); 1455170530Ssam if (m != IFM_AUTO) 1456170530Ssam return m | IFM_IEEE80211_11NG; 1457170530Ssam } 1458170530Ssam } 1459170530Ssam rate &= IEEE80211_RATE_VAL; 1460116742Ssam switch (mode) { 1461116742Ssam case IEEE80211_MODE_11A: 1462170530Ssam case IEEE80211_MODE_11NA: 1463138568Ssam case IEEE80211_MODE_TURBO_A: 1464170530Ssam case IEEE80211_MODE_STURBO_A: 1465170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); 1466116742Ssam case IEEE80211_MODE_11B: 1467170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); 1468124543Sonoe case IEEE80211_MODE_FH: 1469170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); 1470116742Ssam case IEEE80211_MODE_AUTO: 1471116742Ssam /* NB: ic may be NULL for some drivers */ 1472170530Ssam if (ic && ic->ic_phytype == IEEE80211_T_FH) 1473170530Ssam return findmedia(rates, N(rates), 1474170530Ssam rate | IFM_IEEE80211_FH); 1475116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1476116742Ssam /* fall thru... */ 1477116742Ssam case IEEE80211_MODE_11G: 1478170530Ssam case IEEE80211_MODE_11NG: 1479138568Ssam case IEEE80211_MODE_TURBO_G: 1480170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); 1481116742Ssam } 1482116742Ssam return IFM_AUTO; 1483116742Ssam#undef N 1484116742Ssam} 1485116742Ssam 1486116742Ssamint 1487116742Ssamieee80211_media2rate(int mword) 1488116742Ssam{ 1489116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1490116742Ssam static const int ieeerates[] = { 1491116742Ssam -1, /* IFM_AUTO */ 1492116742Ssam 0, /* IFM_MANUAL */ 1493116742Ssam 0, /* IFM_NONE */ 1494116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1495116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1496116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1497116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1498116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1499116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1500116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1501116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1502116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1503116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1504116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1505116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1506116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1507116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1508116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1509116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1510165569Ssam 0, /* IFM_IEEE80211_DS354k */ 1511165569Ssam 0, /* IFM_IEEE80211_DS512k */ 1512165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 1513165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 1514165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 1515170530Ssam -1, /* IFM_IEEE80211_MCS */ 1516116742Ssam }; 1517116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1518116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1519116742Ssam#undef N 1520116742Ssam} 1521