ieee80211.c revision 192468
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3186904Ssam * Copyright (c) 2002-2009 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 192468 2009-05-20 20:00:40Z sam $"); 29116742Ssam 30116742Ssam/* 31116742Ssam * IEEE 802.11 generic handler 32116742Ssam */ 33178354Ssam#include "opt_wlan.h" 34116742Ssam 35116742Ssam#include <sys/param.h> 36191746Sthompsa#include <sys/systm.h> 37116742Ssam#include <sys/kernel.h> 38182742Sbrooks 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> 49190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 50190391Ssam#include <net80211/ieee80211_superg.h> 51190391Ssam#endif 52116742Ssam 53116742Ssam#include <net/bpf.h> 54116742Ssam 55178955Ssamconst char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = { 56178955Ssam [IEEE80211_MODE_AUTO] = "auto", 57178955Ssam [IEEE80211_MODE_11A] = "11a", 58178955Ssam [IEEE80211_MODE_11B] = "11b", 59178955Ssam [IEEE80211_MODE_11G] = "11g", 60178955Ssam [IEEE80211_MODE_FH] = "FH", 61178955Ssam [IEEE80211_MODE_TURBO_A] = "turboA", 62178955Ssam [IEEE80211_MODE_TURBO_G] = "turboG", 63178955Ssam [IEEE80211_MODE_STURBO_A] = "sturboA", 64188782Ssam [IEEE80211_MODE_HALF] = "half", 65188782Ssam [IEEE80211_MODE_QUARTER] = "quarter", 66178955Ssam [IEEE80211_MODE_11NA] = "11na", 67178955Ssam [IEEE80211_MODE_11NG] = "11ng", 68116742Ssam}; 69178957Ssam/* map ieee80211_opmode to the corresponding capability bit */ 70178957Ssamconst int ieee80211_opcap[IEEE80211_OPMODE_MAX] = { 71178957Ssam [IEEE80211_M_IBSS] = IEEE80211_C_IBSS, 72178957Ssam [IEEE80211_M_WDS] = IEEE80211_C_WDS, 73178957Ssam [IEEE80211_M_STA] = IEEE80211_C_STA, 74178957Ssam [IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO, 75178957Ssam [IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP, 76178957Ssam [IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR, 77178957Ssam}; 78178957Ssam 79178354Ssamstatic const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = 80178354Ssam { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 81116742Ssam 82178354Ssamstatic void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); 83178354Ssamstatic void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); 84178354Ssamstatic int ieee80211_media_setup(struct ieee80211com *ic, 85178354Ssam struct ifmedia *media, int caps, int addsta, 86178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); 87178354Ssamstatic void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); 88178354Ssamstatic int ieee80211com_media_change(struct ifnet *); 89178354Ssamstatic int media_status(enum ieee80211_opmode, 90178354Ssam const struct ieee80211_channel *); 91178354Ssam 92178354SsamMALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); 93178354Ssam 94164645Ssam/* 95164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units). 96164645Ssam */ 97164645Ssam#define B(r) ((r) | IEEE80211_RATE_BASIC) 98164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a = 99164645Ssam { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; 100165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half = 101165569Ssam { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; 102165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter = 103165569Ssam { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; 104164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b = 105164645Ssam { 4, { B(2), B(4), B(11), B(22) } }; 106164645Ssam/* NB: OFDM rates are handled specially based on mode */ 107164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g = 108164645Ssam { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; 109164645Ssam#undef B 110164645Ssam 111140915Ssam/* 112165569Ssam * Fill in 802.11 available channel set, mark 113165569Ssam * all available channels as active, and pick 114165569Ssam * a default channel if not already specified. 115165569Ssam */ 116165569Ssamstatic void 117165569Ssamieee80211_chan_init(struct ieee80211com *ic) 118116742Ssam{ 119165569Ssam#define DEFAULTRATES(m, def) do { \ 120188782Ssam if (ic->ic_sup_rates[m].rs_nrates == 0) \ 121165574Ssam ic->ic_sup_rates[m] = def; \ 122165569Ssam} while (0) 123116742Ssam struct ieee80211_channel *c; 124116742Ssam int i; 125116742Ssam 126186107Ssam KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, 127170530Ssam ("invalid number of channels specified: %u", ic->ic_nchans)); 128116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 129178354Ssam memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); 130167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); 131170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 132116742Ssam c = &ic->ic_channels[i]; 133170530Ssam KASSERT(c->ic_flags != 0, ("channel with no flags")); 134187796Ssam /* 135187796Ssam * Help drivers that work only with frequencies by filling 136187796Ssam * in IEEE channel #'s if not already calculated. Note this 137187796Ssam * mimics similar work done in ieee80211_setregdomain when 138187796Ssam * changing regulatory state. 139187796Ssam */ 140187796Ssam if (c->ic_ieee == 0) 141187796Ssam c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); 142187796Ssam if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) 143187796Ssam c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + 144187796Ssam (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), 145187796Ssam c->ic_flags); 146187796Ssam /* default max tx power to max regulatory */ 147187796Ssam if (c->ic_maxpower == 0) 148187796Ssam c->ic_maxpower = 2*c->ic_maxregpower; 149170530Ssam setbit(ic->ic_chan_avail, c->ic_ieee); 150170530Ssam /* 151170530Ssam * Identify mode capabilities. 152170530Ssam */ 153170530Ssam if (IEEE80211_IS_CHAN_A(c)) 154170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11A); 155170530Ssam if (IEEE80211_IS_CHAN_B(c)) 156170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11B); 157170530Ssam if (IEEE80211_IS_CHAN_ANYG(c)) 158170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11G); 159170530Ssam if (IEEE80211_IS_CHAN_FHSS(c)) 160170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_FH); 161170530Ssam if (IEEE80211_IS_CHAN_108A(c)) 162170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); 163170530Ssam if (IEEE80211_IS_CHAN_108G(c)) 164170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); 165170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 166170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); 167188782Ssam if (IEEE80211_IS_CHAN_HALF(c)) 168188782Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_HALF); 169188782Ssam if (IEEE80211_IS_CHAN_QUARTER(c)) 170188782Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_QUARTER); 171170530Ssam if (IEEE80211_IS_CHAN_HTA(c)) 172170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); 173170530Ssam if (IEEE80211_IS_CHAN_HTG(c)) 174170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); 175116742Ssam } 176170530Ssam /* initialize candidate channels to all available */ 177170530Ssam memcpy(ic->ic_chan_active, ic->ic_chan_avail, 178170530Ssam sizeof(ic->ic_chan_avail)); 179164645Ssam 180178354Ssam /* sort channel table to allow lookup optimizations */ 181178354Ssam ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); 182178354Ssam 183178354Ssam /* invalidate any previous state */ 184170530Ssam ic->ic_bsschan = IEEE80211_CHAN_ANYC; 185172233Ssam ic->ic_prevchan = NULL; 186178354Ssam ic->ic_csa_newchan = NULL; 187170530Ssam /* arbitrarily pick the first channel */ 188170530Ssam ic->ic_curchan = &ic->ic_channels[0]; 189190532Ssam ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); 190170530Ssam 191164645Ssam /* fillin well-known rate sets if driver has not specified */ 192165569Ssam DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); 193165569Ssam DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); 194165569Ssam DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); 195165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); 196165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); 197187897Ssam DEFAULTRATES(IEEE80211_MODE_STURBO_A, ieee80211_rateset_11a); 198188782Ssam DEFAULTRATES(IEEE80211_MODE_HALF, ieee80211_rateset_half); 199188782Ssam DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter); 200188774Ssam DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); 201188774Ssam DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); 202165569Ssam 203165569Ssam /* 204165569Ssam * Set auto mode to reset active channel state and any desired channel. 205165569Ssam */ 206165569Ssam (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); 207165569Ssam#undef DEFAULTRATES 208165569Ssam} 209165569Ssam 210178354Ssamstatic void 211178354Ssamnull_update_mcast(struct ifnet *ifp) 212178354Ssam{ 213178354Ssam if_printf(ifp, "need multicast update callback\n"); 214178354Ssam} 215178354Ssam 216178354Ssamstatic void 217178354Ssamnull_update_promisc(struct ifnet *ifp) 218178354Ssam{ 219178354Ssam if_printf(ifp, "need promiscuous mode update callback\n"); 220178354Ssam} 221178354Ssam 222178521Ssamstatic int 223178521Ssamnull_output(struct ifnet *ifp, struct mbuf *m, 224191148Skmacy struct sockaddr *dst, struct route *ro) 225178521Ssam{ 226178521Ssam if_printf(ifp, "discard raw packet\n"); 227178521Ssam m_freem(m); 228178521Ssam return EIO; 229178521Ssam} 230178521Ssam 231178521Ssamstatic void 232178521Ssamnull_input(struct ifnet *ifp, struct mbuf *m) 233178521Ssam{ 234178521Ssam if_printf(ifp, "if_input should not be called\n"); 235178521Ssam m_freem(m); 236178521Ssam} 237178521Ssam 238178354Ssam/* 239178354Ssam * Attach/setup the common net80211 state. Called by 240178354Ssam * the driver on attach to prior to creating any vap's. 241178354Ssam */ 242165569Ssamvoid 243190526Ssamieee80211_ifattach(struct ieee80211com *ic, 244190526Ssam const uint8_t macaddr[IEEE80211_ADDR_LEN]) 245165569Ssam{ 246165569Ssam struct ifnet *ifp = ic->ic_ifp; 247178354Ssam struct sockaddr_dl *sdl; 248178354Ssam struct ifaddr *ifa; 249165569Ssam 250178354Ssam KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type)); 251165569Ssam 252179388Ssam IEEE80211_LOCK_INIT(ic, ifp->if_xname); 253178354Ssam TAILQ_INIT(&ic->ic_vaps); 254191746Sthompsa 255191746Sthompsa /* Create a taskqueue for all state changes */ 256191746Sthompsa ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO, 257191746Sthompsa taskqueue_thread_enqueue, &ic->ic_tq); 258191746Sthompsa taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s taskq", 259191746Sthompsa ifp->if_xname); 260165569Ssam /* 261165569Ssam * Fill in 802.11 available channel set, mark all 262165569Ssam * available channels as active, and pick a default 263165569Ssam * channel if not already specified. 264165569Ssam */ 265178354Ssam ieee80211_media_init(ic); 266170530Ssam 267178354Ssam ic->ic_update_mcast = null_update_mcast; 268178354Ssam ic->ic_update_promisc = null_update_promisc; 269116742Ssam 270155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 271155688Ssam ic->ic_lintval = ic->ic_bintval; 272138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 273138568Ssam 274170530Ssam ieee80211_crypto_attach(ic); 275138568Ssam ieee80211_node_attach(ic); 276170530Ssam ieee80211_power_attach(ic); 277138568Ssam ieee80211_proto_attach(ic); 278190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 279190391Ssam ieee80211_superg_attach(ic); 280190391Ssam#endif 281170530Ssam ieee80211_ht_attach(ic); 282170530Ssam ieee80211_scan_attach(ic); 283178354Ssam ieee80211_regdomain_attach(ic); 284138568Ssam 285178354Ssam ieee80211_sysctl_attach(ic); 286138568Ssam 287178354Ssam ifp->if_addrlen = IEEE80211_ADDR_LEN; 288178354Ssam ifp->if_hdrlen = 0; 289178354Ssam if_attach(ifp); 290178354Ssam ifp->if_mtu = IEEE80211_MTU_MAX; 291178354Ssam ifp->if_broadcastaddr = ieee80211broadcastaddr; 292178521Ssam ifp->if_output = null_output; 293178521Ssam ifp->if_input = null_input; /* just in case */ 294178521Ssam ifp->if_resolvemulti = NULL; /* NB: callers check */ 295140915Ssam 296178354Ssam ifa = ifaddr_byindex(ifp->if_index); 297178354Ssam KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); 298178354Ssam sdl = (struct sockaddr_dl *)ifa->ifa_addr; 299178354Ssam sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ 300178354Ssam sdl->sdl_alen = IEEE80211_ADDR_LEN; 301190526Ssam IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr); 302116742Ssam} 303116742Ssam 304178354Ssam/* 305178354Ssam * Detach net80211 state on device detach. Tear down 306178354Ssam * all vap's and reclaim all common state prior to the 307178354Ssam * device state going away. Note we may call back into 308178354Ssam * driver; it must be prepared for this. 309178354Ssam */ 310116742Ssamvoid 311138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 312116742Ssam{ 313138568Ssam struct ifnet *ifp = ic->ic_ifp; 314178354Ssam struct ieee80211vap *vap; 315116742Ssam 316178354Ssam while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) 317178354Ssam ieee80211_vap_destroy(vap); 318188533Sthompsa ieee80211_waitfor_parent(ic); 319138568Ssam 320138568Ssam ieee80211_sysctl_detach(ic); 321178354Ssam ieee80211_regdomain_detach(ic); 322170530Ssam ieee80211_scan_detach(ic); 323190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 324190391Ssam ieee80211_superg_detach(ic); 325190391Ssam#endif 326170530Ssam ieee80211_ht_detach(ic); 327166012Ssam /* NB: must be called before ieee80211_node_detach */ 328138568Ssam ieee80211_proto_detach(ic); 329138568Ssam ieee80211_crypto_detach(ic); 330170530Ssam ieee80211_power_detach(ic); 331138568Ssam ieee80211_node_detach(ic); 332116742Ssam ifmedia_removeall(&ic->ic_media); 333138568Ssam 334191746Sthompsa taskqueue_free(ic->ic_tq); 335170530Ssam IEEE80211_LOCK_DESTROY(ic); 336178354Ssam if_detach(ifp); 337178354Ssam} 338138568Ssam 339178354Ssam/* 340178354Ssam * Default reset method for use with the ioctl support. This 341178354Ssam * method is invoked after any state change in the 802.11 342178354Ssam * layer that should be propagated to the hardware but not 343178354Ssam * require re-initialization of the 802.11 state machine (e.g 344178354Ssam * rescanning for an ap). We always return ENETRESET which 345178354Ssam * should cause the driver to re-initialize the device. Drivers 346178354Ssam * can override this method to implement more optimized support. 347178354Ssam */ 348178354Ssamstatic int 349178354Ssamdefault_reset(struct ieee80211vap *vap, u_long cmd) 350178354Ssam{ 351178354Ssam return ENETRESET; 352178354Ssam} 353178354Ssam 354178354Ssam/* 355178354Ssam * Prepare a vap for use. Drivers use this call to 356178354Ssam * setup net80211 state in new vap's prior attaching 357178354Ssam * them with ieee80211_vap_attach (below). 358178354Ssam */ 359178354Ssamint 360178354Ssamieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, 361178354Ssam const char name[IFNAMSIZ], int unit, int opmode, int flags, 362178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 363178354Ssam const uint8_t macaddr[IEEE80211_ADDR_LEN]) 364178354Ssam{ 365178354Ssam struct ifnet *ifp; 366178354Ssam 367178354Ssam ifp = if_alloc(IFT_ETHER); 368178354Ssam if (ifp == NULL) { 369178354Ssam if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n", 370178354Ssam __func__); 371178354Ssam return ENOMEM; 372178354Ssam } 373178354Ssam if_initname(ifp, name, unit); 374178354Ssam ifp->if_softc = vap; /* back pointer */ 375178354Ssam ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 376178354Ssam ifp->if_start = ieee80211_start; 377178354Ssam ifp->if_ioctl = ieee80211_ioctl; 378178354Ssam ifp->if_watchdog = NULL; /* NB: no watchdog routine */ 379178354Ssam ifp->if_init = ieee80211_init; 380178354Ssam /* NB: input+output filled in by ether_ifattach */ 381178354Ssam IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 382178354Ssam ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 383178354Ssam IFQ_SET_READY(&ifp->if_snd); 384178354Ssam 385178354Ssam vap->iv_ifp = ifp; 386178354Ssam vap->iv_ic = ic; 387178354Ssam vap->iv_flags = ic->ic_flags; /* propagate common flags */ 388178354Ssam vap->iv_flags_ext = ic->ic_flags_ext; 389178354Ssam vap->iv_flags_ven = ic->ic_flags_ven; 390178354Ssam vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; 391178354Ssam vap->iv_htcaps = ic->ic_htcaps; 392178354Ssam vap->iv_opmode = opmode; 393178957Ssam vap->iv_caps |= ieee80211_opcap[opmode]; 394178354Ssam switch (opmode) { 395178354Ssam case IEEE80211_M_WDS: 396178354Ssam /* 397178354Ssam * WDS links must specify the bssid of the far end. 398178354Ssam * For legacy operation this is a static relationship. 399178354Ssam * For non-legacy operation the station must associate 400178354Ssam * and be authorized to pass traffic. Plumbing the 401178354Ssam * vap to the proper node happens when the vap 402178354Ssam * transitions to RUN state. 403178354Ssam */ 404178354Ssam IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); 405178354Ssam vap->iv_flags |= IEEE80211_F_DESBSSID; 406178354Ssam if (flags & IEEE80211_CLONE_WDSLEGACY) 407178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; 408178354Ssam break; 409186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 410186904Ssam case IEEE80211_M_AHDEMO: 411186904Ssam if (flags & IEEE80211_CLONE_TDMA) { 412186904Ssam /* NB: checked before clone operation allowed */ 413186904Ssam KASSERT(ic->ic_caps & IEEE80211_C_TDMA, 414186904Ssam ("not TDMA capable, ic_caps 0x%x", ic->ic_caps)); 415186904Ssam /* 416186904Ssam * Propagate TDMA capability to mark vap; this 417186904Ssam * cannot be removed and is used to distinguish 418186904Ssam * regular ahdemo operation from ahdemo+tdma. 419186904Ssam */ 420186904Ssam vap->iv_caps |= IEEE80211_C_TDMA; 421186904Ssam } 422186904Ssam break; 423186904Ssam#endif 424178354Ssam } 425184278Ssam /* auto-enable s/w beacon miss support */ 426184278Ssam if (flags & IEEE80211_CLONE_NOBEACONS) 427184278Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; 428178354Ssam /* 429178354Ssam * Enable various functionality by default if we're 430178354Ssam * capable; the driver can override us if it knows better. 431178354Ssam */ 432178354Ssam if (vap->iv_caps & IEEE80211_C_WME) 433178354Ssam vap->iv_flags |= IEEE80211_F_WME; 434178354Ssam if (vap->iv_caps & IEEE80211_C_BURST) 435178354Ssam vap->iv_flags |= IEEE80211_F_BURST; 436178354Ssam /* NB: bg scanning only makes sense for station mode right now */ 437178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 438178354Ssam (vap->iv_caps & IEEE80211_C_BGSCAN)) 439178354Ssam vap->iv_flags |= IEEE80211_F_BGSCAN; 440178957Ssam vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ 441178954Ssam /* NB: DFS support only makes sense for ap mode right now */ 442178954Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 443178954Ssam (vap->iv_caps & IEEE80211_C_DFS)) 444178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_DFS; 445178354Ssam 446178354Ssam vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 447178354Ssam vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 448178354Ssam vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; 449178354Ssam /* 450178354Ssam * Install a default reset method for the ioctl support; 451178354Ssam * the driver can override this. 452178354Ssam */ 453178354Ssam vap->iv_reset = default_reset; 454178354Ssam 455178354Ssam IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); 456178354Ssam 457178354Ssam ieee80211_sysctl_vattach(vap); 458178354Ssam ieee80211_crypto_vattach(vap); 459178354Ssam ieee80211_node_vattach(vap); 460178354Ssam ieee80211_power_vattach(vap); 461178354Ssam ieee80211_proto_vattach(vap); 462190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 463190391Ssam ieee80211_superg_vattach(vap); 464190391Ssam#endif 465178354Ssam ieee80211_ht_vattach(vap); 466178354Ssam ieee80211_scan_vattach(vap); 467178354Ssam ieee80211_regdomain_vattach(vap); 468192468Ssam ieee80211_radiotap_vattach(vap); 469178354Ssam 470178354Ssam return 0; 471178354Ssam} 472178354Ssam 473178354Ssam/* 474178354Ssam * Activate a vap. State should have been prepared with a 475178354Ssam * call to ieee80211_vap_setup and by the driver. On return 476178354Ssam * from this call the vap is ready for use. 477178354Ssam */ 478178354Ssamint 479178354Ssamieee80211_vap_attach(struct ieee80211vap *vap, 480178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 481178354Ssam{ 482178354Ssam struct ifnet *ifp = vap->iv_ifp; 483178354Ssam struct ieee80211com *ic = vap->iv_ic; 484178354Ssam struct ifmediareq imr; 485178354Ssam int maxrate; 486178354Ssam 487178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 488178354Ssam "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", 489178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 490178354Ssam ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); 491178354Ssam 492178354Ssam /* 493178354Ssam * Do late attach work that cannot happen until after 494178354Ssam * the driver has had a chance to override defaults. 495178354Ssam */ 496178354Ssam ieee80211_node_latevattach(vap); 497178354Ssam ieee80211_power_latevattach(vap); 498178354Ssam 499178354Ssam maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, 500178354Ssam vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); 501178354Ssam ieee80211_media_status(ifp, &imr); 502178354Ssam /* NB: strip explicit mode; we're actually in autoselect */ 503188106Ssam ifmedia_set(&vap->iv_media, 504188106Ssam imr.ifm_active &~ (IFM_MMASK | IFM_IEEE80211_TURBO)); 505178354Ssam if (maxrate) 506178354Ssam ifp->if_baudrate = IF_Mbps(maxrate); 507178354Ssam 508178354Ssam ether_ifattach(ifp, vap->iv_myaddr); 509178354Ssam /* hook output method setup by ether_ifattach */ 510178354Ssam vap->iv_output = ifp->if_output; 511178354Ssam ifp->if_output = ieee80211_output; 512178354Ssam /* NB: if_mtu set by ether_ifattach to ETHERMTU */ 513178354Ssam 514178354Ssam IEEE80211_LOCK(ic); 515178354Ssam TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); 516192468Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR) 517192468Ssam ic->ic_monvaps++; 518178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 519190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 520178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 521190391Ssam#endif 522178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 523178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 524178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); 525178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); 526178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 527178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 528178354Ssam IEEE80211_UNLOCK(ic); 529178354Ssam 530178354Ssam return 1; 531178354Ssam} 532178354Ssam 533178354Ssam/* 534178354Ssam * Tear down vap state and reclaim the ifnet. 535178354Ssam * The driver is assumed to have prepared for 536178354Ssam * this; e.g. by turning off interrupts for the 537178354Ssam * underlying device. 538178354Ssam */ 539178354Ssamvoid 540178354Ssamieee80211_vap_detach(struct ieee80211vap *vap) 541178354Ssam{ 542178354Ssam struct ieee80211com *ic = vap->iv_ic; 543178354Ssam struct ifnet *ifp = vap->iv_ifp; 544178354Ssam 545178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", 546178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 547178354Ssam ic->ic_ifp->if_xname); 548178354Ssam 549178354Ssam IEEE80211_LOCK(ic); 550178354Ssam /* block traffic from above */ 551178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 552178354Ssam /* 553178354Ssam * Evil hack. Clear the backpointer from the ifnet to the 554178354Ssam * vap so any requests from above will return an error or 555178354Ssam * be ignored. In particular this short-circuits requests 556178354Ssam * by the bridge to turn off promiscuous mode as a result 557178354Ssam * of calling ether_ifdetach. 558178354Ssam */ 559178354Ssam ifp->if_softc = NULL; 560178354Ssam /* 561178354Ssam * Stop the vap before detaching the ifnet. Ideally we'd 562178354Ssam * do this in the other order so the ifnet is inaccessible 563178354Ssam * while we cleanup internal state but that is hard. 564178354Ssam */ 565178354Ssam ieee80211_stop_locked(vap); 566191746Sthompsa IEEE80211_UNLOCK(ic); 567178354Ssam 568191746Sthompsa /* 569191746Sthompsa * Flush any deferred vap tasks. 570191746Sthompsa * NB: must be before ether_ifdetach() and removal from ic_vaps list 571191746Sthompsa */ 572191746Sthompsa ieee80211_draintask(ic, &vap->iv_nstate_task); 573191746Sthompsa ieee80211_draintask(ic, &vap->iv_swbmiss_task); 574191746Sthompsa 575191746Sthompsa IEEE80211_LOCK(ic); 576191746Sthompsa KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running")); 577178354Ssam TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); 578192468Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR) 579192468Ssam ic->ic_monvaps--; 580178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 581190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 582178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 583190391Ssam#endif 584178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 585178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 586178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); 587178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); 588192468Ssam /* NB: this handles the bpfdetach done below */ 589192468Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); 590178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 591178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 592178354Ssam IEEE80211_UNLOCK(ic); 593178354Ssam 594178354Ssam /* XXX can't hold com lock */ 595192468Ssam /* NB: bpfdetach is called by ether_ifdetach and claims all taps */ 596116742Ssam ether_ifdetach(ifp); 597178354Ssam 598178354Ssam ifmedia_removeall(&vap->iv_media); 599178354Ssam 600192468Ssam ieee80211_radiotap_vdetach(vap); 601178354Ssam ieee80211_regdomain_vdetach(vap); 602178354Ssam ieee80211_scan_vdetach(vap); 603190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 604190391Ssam ieee80211_superg_vdetach(vap); 605190391Ssam#endif 606178354Ssam ieee80211_ht_vdetach(vap); 607178354Ssam /* NB: must be before ieee80211_node_vdetach */ 608178354Ssam ieee80211_proto_vdetach(vap); 609178354Ssam ieee80211_crypto_vdetach(vap); 610178354Ssam ieee80211_power_vdetach(vap); 611178354Ssam ieee80211_node_vdetach(vap); 612178354Ssam ieee80211_sysctl_vdetach(vap); 613182674Sweongyo 614182674Sweongyo if_free(ifp); 615116742Ssam} 616116742Ssam 617178354Ssam/* 618178354Ssam * Synchronize flag bit state in the parent ifnet structure 619178354Ssam * according to the state of all vap ifnet's. This is used, 620178354Ssam * for example, to handle IFF_PROMISC and IFF_ALLMULTI. 621178354Ssam */ 622178354Ssamvoid 623178354Ssamieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) 624178354Ssam{ 625178354Ssam struct ifnet *ifp = ic->ic_ifp; 626178354Ssam struct ieee80211vap *vap; 627178354Ssam int bit, oflags; 628178354Ssam 629178354Ssam IEEE80211_LOCK_ASSERT(ic); 630178354Ssam 631178354Ssam bit = 0; 632178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 633178354Ssam if (vap->iv_ifp->if_flags & flag) { 634178354Ssam /* 635178354Ssam * XXX the bridge sets PROMISC but we don't want to 636178354Ssam * enable it on the device, discard here so all the 637178354Ssam * drivers don't need to special-case it 638178354Ssam */ 639178354Ssam if (flag == IFF_PROMISC && 640178354Ssam vap->iv_opmode == IEEE80211_M_HOSTAP) 641178354Ssam continue; 642178354Ssam bit = 1; 643178354Ssam break; 644178354Ssam } 645178354Ssam oflags = ifp->if_flags; 646178354Ssam if (bit) 647178354Ssam ifp->if_flags |= flag; 648178354Ssam else 649178354Ssam ifp->if_flags &= ~flag; 650178354Ssam if ((ifp->if_flags ^ oflags) & flag) { 651178354Ssam /* XXX should we return 1/0 and let caller do this? */ 652178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 653178354Ssam if (flag == IFF_PROMISC) 654191746Sthompsa ieee80211_runtask(ic, &ic->ic_promisc_task); 655178354Ssam else if (flag == IFF_ALLMULTI) 656191746Sthompsa ieee80211_runtask(ic, &ic->ic_mcast_task); 657178354Ssam } 658178354Ssam } 659178354Ssam} 660178354Ssam 661178354Ssam/* 662178354Ssam * Synchronize flag bit state in the com structure 663178354Ssam * according to the state of all vap's. This is used, 664178354Ssam * for example, to handle state changes via ioctls. 665178354Ssam */ 666178354Ssamstatic void 667178354Ssamieee80211_syncflag_locked(struct ieee80211com *ic, int flag) 668178354Ssam{ 669178354Ssam struct ieee80211vap *vap; 670178354Ssam int bit; 671178354Ssam 672178354Ssam IEEE80211_LOCK_ASSERT(ic); 673178354Ssam 674178354Ssam bit = 0; 675178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 676178354Ssam if (vap->iv_flags & flag) { 677178354Ssam bit = 1; 678178354Ssam break; 679178354Ssam } 680178354Ssam if (bit) 681178354Ssam ic->ic_flags |= flag; 682178354Ssam else 683178354Ssam ic->ic_flags &= ~flag; 684178354Ssam} 685178354Ssam 686178354Ssamvoid 687178354Ssamieee80211_syncflag(struct ieee80211vap *vap, int flag) 688178354Ssam{ 689178354Ssam struct ieee80211com *ic = vap->iv_ic; 690178354Ssam 691178354Ssam IEEE80211_LOCK(ic); 692178354Ssam if (flag < 0) { 693178354Ssam flag = -flag; 694178354Ssam vap->iv_flags &= ~flag; 695178354Ssam } else 696178354Ssam vap->iv_flags |= flag; 697178354Ssam ieee80211_syncflag_locked(ic, flag); 698178354Ssam IEEE80211_UNLOCK(ic); 699178354Ssam} 700178354Ssam 701178354Ssam/* 702178354Ssam * Synchronize flag bit state in the com structure 703178354Ssam * according to the state of all vap's. This is used, 704178354Ssam * for example, to handle state changes via ioctls. 705178354Ssam */ 706178354Ssamstatic void 707178354Ssamieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) 708178354Ssam{ 709178354Ssam struct ieee80211vap *vap; 710178354Ssam int bit; 711178354Ssam 712178354Ssam IEEE80211_LOCK_ASSERT(ic); 713178354Ssam 714178354Ssam bit = 0; 715178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 716178354Ssam if (vap->iv_flags_ext & flag) { 717178354Ssam bit = 1; 718178354Ssam break; 719178354Ssam } 720178354Ssam if (bit) 721178354Ssam ic->ic_flags_ext |= flag; 722178354Ssam else 723178354Ssam ic->ic_flags_ext &= ~flag; 724178354Ssam} 725178354Ssam 726178354Ssamvoid 727178354Ssamieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) 728178354Ssam{ 729178354Ssam struct ieee80211com *ic = vap->iv_ic; 730178354Ssam 731178354Ssam IEEE80211_LOCK(ic); 732178354Ssam if (flag < 0) { 733178354Ssam flag = -flag; 734178354Ssam vap->iv_flags_ext &= ~flag; 735178354Ssam } else 736178354Ssam vap->iv_flags_ext |= flag; 737178354Ssam ieee80211_syncflag_ext_locked(ic, flag); 738178354Ssam IEEE80211_UNLOCK(ic); 739178354Ssam} 740178354Ssam 741166012Ssamstatic __inline int 742166012Ssammapgsm(u_int freq, u_int flags) 743166012Ssam{ 744166012Ssam freq *= 10; 745166012Ssam if (flags & IEEE80211_CHAN_QUARTER) 746166012Ssam freq += 5; 747166012Ssam else if (flags & IEEE80211_CHAN_HALF) 748166012Ssam freq += 10; 749166012Ssam else 750166012Ssam freq += 20; 751166012Ssam /* NB: there is no 907/20 wide but leave room */ 752166012Ssam return (freq - 906*10) / 5; 753166012Ssam} 754166012Ssam 755166012Ssamstatic __inline int 756166012Ssammappsb(u_int freq, u_int flags) 757166012Ssam{ 758166012Ssam return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 759166012Ssam} 760166012Ssam 761116742Ssam/* 762116742Ssam * Convert MHz frequency to IEEE channel number. 763116742Ssam */ 764152450Ssamint 765116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 766116742Ssam{ 767167430Ssam#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) 768166012Ssam if (flags & IEEE80211_CHAN_GSM) 769166012Ssam return mapgsm(freq, flags); 770116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 771116742Ssam if (freq == 2484) 772116742Ssam return 14; 773116742Ssam if (freq < 2484) 774152450Ssam return ((int) freq - 2407) / 5; 775116742Ssam else 776116742Ssam return 15 + ((freq - 2512) / 20); 777116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 778165569Ssam if (freq <= 5000) { 779170530Ssam /* XXX check regdomain? */ 780167430Ssam if (IS_FREQ_IN_PSB(freq)) 781166012Ssam return mappsb(freq, flags); 782152450Ssam return (freq - 4000) / 5; 783165569Ssam } else 784152450Ssam return (freq - 5000) / 5; 785116742Ssam } else { /* either, guess */ 786116742Ssam if (freq == 2484) 787116742Ssam return 14; 788166012Ssam if (freq < 2484) { 789166012Ssam if (907 <= freq && freq <= 922) 790166012Ssam return mapgsm(freq, flags); 791152450Ssam return ((int) freq - 2407) / 5; 792166012Ssam } 793152450Ssam if (freq < 5000) { 794167430Ssam if (IS_FREQ_IN_PSB(freq)) 795166012Ssam return mappsb(freq, flags); 796165569Ssam else if (freq > 4900) 797152450Ssam return (freq - 4000) / 5; 798152450Ssam else 799152450Ssam return 15 + ((freq - 2512) / 20); 800152450Ssam } 801116742Ssam return (freq - 5000) / 5; 802116742Ssam } 803167430Ssam#undef IS_FREQ_IN_PSB 804116742Ssam} 805116742Ssam 806116742Ssam/* 807116742Ssam * Convert channel to IEEE channel number. 808116742Ssam */ 809152450Ssamint 810165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) 811116742Ssam{ 812170530Ssam if (c == NULL) { 813138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 814117039Ssam return 0; /* XXX */ 815116742Ssam } 816170530Ssam return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); 817116742Ssam} 818116742Ssam 819116742Ssam/* 820116742Ssam * Convert IEEE channel number to MHz frequency. 821116742Ssam */ 822116742Ssamu_int 823116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 824116742Ssam{ 825166012Ssam if (flags & IEEE80211_CHAN_GSM) 826166012Ssam return 907 + 5 * (chan / 10); 827116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 828116742Ssam if (chan == 14) 829116742Ssam return 2484; 830116742Ssam if (chan < 14) 831116742Ssam return 2407 + chan*5; 832116742Ssam else 833116742Ssam return 2512 + ((chan-15)*20); 834116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 835165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 836165569Ssam chan -= 37; 837165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 838165569Ssam } 839116742Ssam return 5000 + (chan*5); 840116742Ssam } else { /* either, guess */ 841166012Ssam /* XXX can't distinguish PSB+GSM channels */ 842116742Ssam if (chan == 14) 843116742Ssam return 2484; 844116742Ssam if (chan < 14) /* 0-13 */ 845116742Ssam return 2407 + chan*5; 846116742Ssam if (chan < 27) /* 15-26 */ 847116742Ssam return 2512 + ((chan-15)*20); 848116742Ssam return 5000 + (chan*5); 849116742Ssam } 850116742Ssam} 851116742Ssam 852116742Ssam/* 853170530Ssam * Locate a channel given a frequency+flags. We cache 854178354Ssam * the previous lookup to optimize switching between two 855170530Ssam * channels--as happens with dynamic turbo. 856170530Ssam */ 857170530Ssamstruct ieee80211_channel * 858170530Ssamieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) 859170530Ssam{ 860170530Ssam struct ieee80211_channel *c; 861170530Ssam int i; 862170530Ssam 863170530Ssam flags &= IEEE80211_CHAN_ALLTURBO; 864170530Ssam c = ic->ic_prevchan; 865170530Ssam if (c != NULL && c->ic_freq == freq && 866170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 867170530Ssam return c; 868170530Ssam /* brute force search */ 869170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 870170530Ssam c = &ic->ic_channels[i]; 871170530Ssam if (c->ic_freq == freq && 872170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 873170530Ssam return c; 874170530Ssam } 875170530Ssam return NULL; 876170530Ssam} 877170530Ssam 878173861Ssam/* 879173861Ssam * Locate a channel given a channel number+flags. We cache 880173861Ssam * the previous lookup to optimize switching between two 881173861Ssam * channels--as happens with dynamic turbo. 882173861Ssam */ 883173861Ssamstruct ieee80211_channel * 884173861Ssamieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) 885173861Ssam{ 886173861Ssam struct ieee80211_channel *c; 887173861Ssam int i; 888173861Ssam 889173861Ssam flags &= IEEE80211_CHAN_ALLTURBO; 890173861Ssam c = ic->ic_prevchan; 891173861Ssam if (c != NULL && c->ic_ieee == ieee && 892173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 893173861Ssam return c; 894173861Ssam /* brute force search */ 895173861Ssam for (i = 0; i < ic->ic_nchans; i++) { 896173861Ssam c = &ic->ic_channels[i]; 897173861Ssam if (c->ic_ieee == ieee && 898173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 899173861Ssam return c; 900173861Ssam } 901173861Ssam return NULL; 902173861Ssam} 903173861Ssam 904170530Ssamstatic void 905178354Ssamaddmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) 906170530Ssam{ 907170530Ssam#define ADD(_ic, _s, _o) \ 908178354Ssam ifmedia_add(media, \ 909170530Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 910170530Ssam static const u_int mopts[IEEE80211_MODE_MAX] = { 911188106Ssam [IEEE80211_MODE_AUTO] = IFM_AUTO, 912188106Ssam [IEEE80211_MODE_11A] = IFM_IEEE80211_11A, 913188106Ssam [IEEE80211_MODE_11B] = IFM_IEEE80211_11B, 914188106Ssam [IEEE80211_MODE_11G] = IFM_IEEE80211_11G, 915188106Ssam [IEEE80211_MODE_FH] = IFM_IEEE80211_FH, 916188106Ssam [IEEE80211_MODE_TURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 917188106Ssam [IEEE80211_MODE_TURBO_G] = IFM_IEEE80211_11G|IFM_IEEE80211_TURBO, 918188106Ssam [IEEE80211_MODE_STURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 919188782Ssam [IEEE80211_MODE_HALF] = IFM_IEEE80211_11A, /* XXX */ 920188782Ssam [IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */ 921188106Ssam [IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA, 922188106Ssam [IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG, 923170530Ssam }; 924170530Ssam u_int mopt; 925170530Ssam 926170530Ssam mopt = mopts[mode]; 927178354Ssam if (addsta) 928178354Ssam ADD(ic, mword, mopt); /* STA mode has no cap */ 929178354Ssam if (caps & IEEE80211_C_IBSS) 930178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); 931178354Ssam if (caps & IEEE80211_C_HOSTAP) 932178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); 933178354Ssam if (caps & IEEE80211_C_AHDEMO) 934178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 935178354Ssam if (caps & IEEE80211_C_MONITOR) 936178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); 937178354Ssam if (caps & IEEE80211_C_WDS) 938178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_WDS); 939170530Ssam#undef ADD 940170530Ssam} 941170530Ssam 942170530Ssam/* 943116742Ssam * Setup the media data structures according to the channel and 944178354Ssam * rate tables. 945116742Ssam */ 946178354Ssamstatic int 947178354Ssamieee80211_media_setup(struct ieee80211com *ic, 948178354Ssam struct ifmedia *media, int caps, int addsta, 949116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 950116742Ssam{ 951170530Ssam int i, j, mode, rate, maxrate, mword, r; 952170530Ssam const struct ieee80211_rateset *rs; 953116742Ssam struct ieee80211_rateset allrates; 954116742Ssam 955118887Ssam /* 956116742Ssam * Fill in media characteristics. 957116742Ssam */ 958178354Ssam ifmedia_init(media, 0, media_change, media_stat); 959116742Ssam maxrate = 0; 960170530Ssam /* 961170530Ssam * Add media for legacy operating modes. 962170530Ssam */ 963116742Ssam memset(&allrates, 0, sizeof(allrates)); 964170530Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { 965167468Ssam if (isclr(ic->ic_modecaps, mode)) 966116742Ssam continue; 967178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 968116742Ssam if (mode == IEEE80211_MODE_AUTO) 969116742Ssam continue; 970116742Ssam rs = &ic->ic_sup_rates[mode]; 971116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 972116742Ssam rate = rs->rs_rates[i]; 973116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 974116742Ssam if (mword == 0) 975116742Ssam continue; 976178354Ssam addmedia(media, caps, addsta, mode, mword); 977116742Ssam /* 978170530Ssam * Add legacy rate to the collection of all rates. 979116742Ssam */ 980116742Ssam r = rate & IEEE80211_RATE_VAL; 981116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 982116742Ssam if (allrates.rs_rates[j] == r) 983116742Ssam break; 984116742Ssam if (j == allrates.rs_nrates) { 985116742Ssam /* unique, add to the set */ 986116742Ssam allrates.rs_rates[j] = r; 987116742Ssam allrates.rs_nrates++; 988116742Ssam } 989116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 990116742Ssam if (rate > maxrate) 991116742Ssam maxrate = rate; 992116742Ssam } 993116742Ssam } 994116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 995116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 996116742Ssam IEEE80211_MODE_AUTO); 997116742Ssam if (mword == 0) 998116742Ssam continue; 999170530Ssam /* NB: remove media options from mword */ 1000178354Ssam addmedia(media, caps, addsta, 1001178354Ssam IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); 1002116742Ssam } 1003170530Ssam /* 1004170530Ssam * Add HT/11n media. Note that we do not have enough 1005170530Ssam * bits in the media subtype to express the MCS so we 1006170530Ssam * use a "placeholder" media subtype and any fixed MCS 1007170530Ssam * must be specified with a different mechanism. 1008170530Ssam */ 1009188782Ssam for (; mode <= IEEE80211_MODE_11NG; mode++) { 1010170530Ssam if (isclr(ic->ic_modecaps, mode)) 1011170530Ssam continue; 1012178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 1013178354Ssam addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); 1014170530Ssam } 1015170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 1016170530Ssam isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 1017178354Ssam addmedia(media, caps, addsta, 1018178354Ssam IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); 1019170530Ssam /* XXX could walk htrates */ 1020170530Ssam /* XXX known array size */ 1021178354Ssam if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) 1022178354Ssam maxrate = ieee80211_htrates[15].ht40_rate_400ns; 1023170530Ssam } 1024178354Ssam return maxrate; 1025178354Ssam} 1026116742Ssam 1027178354Ssamvoid 1028178354Ssamieee80211_media_init(struct ieee80211com *ic) 1029178354Ssam{ 1030178354Ssam struct ifnet *ifp = ic->ic_ifp; 1031178354Ssam int maxrate; 1032178354Ssam 1033178354Ssam /* NB: this works because the structure is initialized to zero */ 1034178354Ssam if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { 1035178354Ssam /* 1036178354Ssam * We are re-initializing the channel list; clear 1037178354Ssam * the existing media state as the media routines 1038178354Ssam * don't suppress duplicates. 1039178354Ssam */ 1040178354Ssam ifmedia_removeall(&ic->ic_media); 1041178354Ssam } 1042178354Ssam ieee80211_chan_init(ic); 1043178354Ssam 1044178354Ssam /* 1045178354Ssam * Recalculate media settings in case new channel list changes 1046178354Ssam * the set of available modes. 1047178354Ssam */ 1048178354Ssam maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, 1049178354Ssam ieee80211com_media_change, ieee80211com_media_status); 1050170530Ssam /* NB: strip explicit mode; we're actually in autoselect */ 1051170530Ssam ifmedia_set(&ic->ic_media, 1052188106Ssam media_status(ic->ic_opmode, ic->ic_curchan) &~ 1053188106Ssam (IFM_MMASK | IFM_IEEE80211_TURBO)); 1054116742Ssam if (maxrate) 1055116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 1056178354Ssam 1057178354Ssam /* XXX need to propagate new media settings to vap's */ 1058116742Ssam} 1059116742Ssam 1060188782Ssam/* XXX inline or eliminate? */ 1061165569Ssamconst struct ieee80211_rateset * 1062165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 1063165569Ssam{ 1064188774Ssam /* XXX does this work for 11ng basic rates? */ 1065170530Ssam return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; 1066165569Ssam} 1067165569Ssam 1068138568Ssamvoid 1069138568Ssamieee80211_announce(struct ieee80211com *ic) 1070138568Ssam{ 1071138568Ssam struct ifnet *ifp = ic->ic_ifp; 1072138568Ssam int i, mode, rate, mword; 1073170530Ssam const struct ieee80211_rateset *rs; 1074138568Ssam 1075172227Ssam /* NB: skip AUTO since it has no rates */ 1076172227Ssam for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { 1077167468Ssam if (isclr(ic->ic_modecaps, mode)) 1078138568Ssam continue; 1079138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 1080138568Ssam rs = &ic->ic_sup_rates[mode]; 1081138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 1082170530Ssam mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 1083138568Ssam if (mword == 0) 1084138568Ssam continue; 1085170530Ssam rate = ieee80211_media2rate(mword); 1086138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 1087170530Ssam rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 1088138568Ssam } 1089138568Ssam printf("\n"); 1090138568Ssam } 1091170530Ssam ieee80211_ht_announce(ic); 1092138568Ssam} 1093138568Ssam 1094170530Ssamvoid 1095170530Ssamieee80211_announce_channels(struct ieee80211com *ic) 1096116742Ssam{ 1097170530Ssam const struct ieee80211_channel *c; 1098170530Ssam char type; 1099170530Ssam int i, cw; 1100170530Ssam 1101170530Ssam printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); 1102170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 1103170530Ssam c = &ic->ic_channels[i]; 1104170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 1105170530Ssam type = 'S'; 1106170530Ssam else if (IEEE80211_IS_CHAN_108A(c)) 1107170530Ssam type = 'T'; 1108170530Ssam else if (IEEE80211_IS_CHAN_108G(c)) 1109170530Ssam type = 'G'; 1110170530Ssam else if (IEEE80211_IS_CHAN_HT(c)) 1111170530Ssam type = 'n'; 1112170530Ssam else if (IEEE80211_IS_CHAN_A(c)) 1113170530Ssam type = 'a'; 1114170530Ssam else if (IEEE80211_IS_CHAN_ANYG(c)) 1115170530Ssam type = 'g'; 1116170530Ssam else if (IEEE80211_IS_CHAN_B(c)) 1117170530Ssam type = 'b'; 1118170530Ssam else 1119170530Ssam type = 'f'; 1120170530Ssam if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) 1121170530Ssam cw = 40; 1122170530Ssam else if (IEEE80211_IS_CHAN_HALF(c)) 1123170530Ssam cw = 10; 1124170530Ssam else if (IEEE80211_IS_CHAN_QUARTER(c)) 1125170530Ssam cw = 5; 1126170530Ssam else 1127170530Ssam cw = 20; 1128170530Ssam printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" 1129170530Ssam , c->ic_ieee, c->ic_freq, type 1130170530Ssam , cw 1131170530Ssam , IEEE80211_IS_CHAN_HT40U(c) ? '+' : 1132170530Ssam IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' 1133170530Ssam , c->ic_maxregpower 1134170530Ssam , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 1135170530Ssam , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 1136170530Ssam ); 1137170530Ssam } 1138116742Ssam} 1139116742Ssam 1140170530Ssamstatic int 1141184273Ssammedia2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) 1142170530Ssam{ 1143116742Ssam switch (IFM_MODE(ime->ifm_media)) { 1144116742Ssam case IFM_IEEE80211_11A: 1145178354Ssam *mode = IEEE80211_MODE_11A; 1146116742Ssam break; 1147116742Ssam case IFM_IEEE80211_11B: 1148178354Ssam *mode = IEEE80211_MODE_11B; 1149116742Ssam break; 1150116742Ssam case IFM_IEEE80211_11G: 1151178354Ssam *mode = IEEE80211_MODE_11G; 1152116742Ssam break; 1153124543Sonoe case IFM_IEEE80211_FH: 1154178354Ssam *mode = IEEE80211_MODE_FH; 1155124543Sonoe break; 1156170530Ssam case IFM_IEEE80211_11NA: 1157178354Ssam *mode = IEEE80211_MODE_11NA; 1158170530Ssam break; 1159170530Ssam case IFM_IEEE80211_11NG: 1160178354Ssam *mode = IEEE80211_MODE_11NG; 1161170530Ssam break; 1162116742Ssam case IFM_AUTO: 1163178354Ssam *mode = IEEE80211_MODE_AUTO; 1164116742Ssam break; 1165116742Ssam default: 1166178354Ssam return 0; 1167116742Ssam } 1168116742Ssam /* 1169138568Ssam * Turbo mode is an ``option''. 1170138568Ssam * XXX does not apply to AUTO 1171116742Ssam */ 1172116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 1173178354Ssam if (*mode == IEEE80211_MODE_11A) { 1174184273Ssam if (flags & IEEE80211_F_TURBOP) 1175178354Ssam *mode = IEEE80211_MODE_TURBO_A; 1176170530Ssam else 1177178354Ssam *mode = IEEE80211_MODE_STURBO_A; 1178178354Ssam } else if (*mode == IEEE80211_MODE_11G) 1179178354Ssam *mode = IEEE80211_MODE_TURBO_G; 1180138568Ssam else 1181178354Ssam return 0; 1182116742Ssam } 1183170530Ssam /* XXX HT40 +/- */ 1184178354Ssam return 1; 1185178354Ssam} 1186116742Ssam 1187178354Ssam/* 1188184273Ssam * Handle a media change request on the underlying interface. 1189178354Ssam */ 1190178354Ssamint 1191178354Ssamieee80211com_media_change(struct ifnet *ifp) 1192178354Ssam{ 1193184273Ssam return EINVAL; 1194178354Ssam} 1195116742Ssam 1196178354Ssam/* 1197178354Ssam * Handle a media change request on the vap interface. 1198178354Ssam */ 1199178354Ssamint 1200178354Ssamieee80211_media_change(struct ifnet *ifp) 1201178354Ssam{ 1202178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1203178354Ssam struct ifmedia_entry *ime = vap->iv_media.ifm_cur; 1204184273Ssam uint16_t newmode; 1205178354Ssam 1206184273Ssam if (!media2mode(ime, vap->iv_flags, &newmode)) 1207178354Ssam return EINVAL; 1208184273Ssam if (vap->iv_des_mode != newmode) { 1209184273Ssam vap->iv_des_mode = newmode; 1210178354Ssam return ENETRESET; 1211178354Ssam } 1212178354Ssam return 0; 1213116742Ssam} 1214116742Ssam 1215170530Ssam/* 1216170530Ssam * Common code to calculate the media status word 1217170530Ssam * from the operating mode and channel state. 1218170530Ssam */ 1219170530Ssamstatic int 1220170530Ssammedia_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) 1221170530Ssam{ 1222170530Ssam int status; 1223170530Ssam 1224170530Ssam status = IFM_IEEE80211; 1225170530Ssam switch (opmode) { 1226170530Ssam case IEEE80211_M_STA: 1227170530Ssam break; 1228170530Ssam case IEEE80211_M_IBSS: 1229170530Ssam status |= IFM_IEEE80211_ADHOC; 1230170530Ssam break; 1231170530Ssam case IEEE80211_M_HOSTAP: 1232170530Ssam status |= IFM_IEEE80211_HOSTAP; 1233170530Ssam break; 1234170530Ssam case IEEE80211_M_MONITOR: 1235170530Ssam status |= IFM_IEEE80211_MONITOR; 1236170530Ssam break; 1237170530Ssam case IEEE80211_M_AHDEMO: 1238170530Ssam status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; 1239170530Ssam break; 1240170530Ssam case IEEE80211_M_WDS: 1241178354Ssam status |= IFM_IEEE80211_WDS; 1242170530Ssam break; 1243170530Ssam } 1244170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) { 1245170530Ssam status |= IFM_IEEE80211_11NA; 1246170530Ssam } else if (IEEE80211_IS_CHAN_HTG(chan)) { 1247170530Ssam status |= IFM_IEEE80211_11NG; 1248170530Ssam } else if (IEEE80211_IS_CHAN_A(chan)) { 1249170530Ssam status |= IFM_IEEE80211_11A; 1250170530Ssam } else if (IEEE80211_IS_CHAN_B(chan)) { 1251170530Ssam status |= IFM_IEEE80211_11B; 1252170530Ssam } else if (IEEE80211_IS_CHAN_ANYG(chan)) { 1253170530Ssam status |= IFM_IEEE80211_11G; 1254170530Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) { 1255170530Ssam status |= IFM_IEEE80211_FH; 1256170530Ssam } 1257170530Ssam /* XXX else complain? */ 1258170530Ssam 1259170530Ssam if (IEEE80211_IS_CHAN_TURBO(chan)) 1260170530Ssam status |= IFM_IEEE80211_TURBO; 1261178354Ssam#if 0 1262178354Ssam if (IEEE80211_IS_CHAN_HT20(chan)) 1263178354Ssam status |= IFM_IEEE80211_HT20; 1264178354Ssam if (IEEE80211_IS_CHAN_HT40(chan)) 1265178354Ssam status |= IFM_IEEE80211_HT40; 1266178354Ssam#endif 1267170530Ssam return status; 1268170530Ssam} 1269170530Ssam 1270178354Ssamstatic void 1271178354Ssamieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1272178354Ssam{ 1273178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1274178354Ssam struct ieee80211vap *vap; 1275178354Ssam 1276178354Ssam imr->ifm_status = IFM_AVALID; 1277178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1278178354Ssam if (vap->iv_ifp->if_flags & IFF_UP) { 1279178354Ssam imr->ifm_status |= IFM_ACTIVE; 1280178354Ssam break; 1281178354Ssam } 1282178354Ssam imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); 1283178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1284178354Ssam imr->ifm_current = imr->ifm_active; 1285178354Ssam} 1286178354Ssam 1287116742Ssamvoid 1288116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1289116742Ssam{ 1290178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1291178354Ssam struct ieee80211com *ic = vap->iv_ic; 1292170530Ssam enum ieee80211_phymode mode; 1293116742Ssam 1294116742Ssam imr->ifm_status = IFM_AVALID; 1295170530Ssam /* 1296170530Ssam * NB: use the current channel's mode to lock down a xmit 1297170530Ssam * rate only when running; otherwise we may have a mismatch 1298170530Ssam * in which case the rate will not be convertible. 1299170530Ssam */ 1300178354Ssam if (vap->iv_state == IEEE80211_S_RUN) { 1301116742Ssam imr->ifm_status |= IFM_ACTIVE; 1302170530Ssam mode = ieee80211_chan2mode(ic->ic_curchan); 1303170530Ssam } else 1304170530Ssam mode = IEEE80211_MODE_AUTO; 1305178354Ssam imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); 1306138568Ssam /* 1307138568Ssam * Calculate a current rate if possible. 1308138568Ssam */ 1309178354Ssam if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { 1310138568Ssam /* 1311138568Ssam * A fixed rate is set, report that. 1312138568Ssam */ 1313138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1314178354Ssam vap->iv_txparms[mode].ucastrate, mode); 1315178354Ssam } else if (vap->iv_opmode == IEEE80211_M_STA) { 1316138568Ssam /* 1317138568Ssam * In station mode report the current transmit rate. 1318138568Ssam */ 1319138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1320178354Ssam vap->iv_bss->ni_txrate, mode); 1321128966Sandre } else 1322138568Ssam imr->ifm_active |= IFM_AUTO; 1323178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1324178354Ssam imr->ifm_current = imr->ifm_active; 1325116742Ssam} 1326116742Ssam 1327116742Ssam/* 1328116742Ssam * Set the current phy mode and recalculate the active channel 1329116742Ssam * set based on the available channels for this mode. Also 1330116742Ssam * select a new default/current channel if the current one is 1331116742Ssam * inappropriate for this mode. 1332116742Ssam */ 1333116742Ssamint 1334116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 1335116742Ssam{ 1336116742Ssam /* 1337166012Ssam * Adjust basic rates in 11b/11g supported rate set. 1338166012Ssam * Note that if operating on a hal/quarter rate channel 1339166012Ssam * this is a noop as those rates sets are different 1340166012Ssam * and used instead. 1341116742Ssam */ 1342166012Ssam if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 1343178354Ssam ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); 1344166012Ssam 1345116742Ssam ic->ic_curmode = mode; 1346138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 1347138568Ssam 1348116742Ssam return 0; 1349116742Ssam} 1350116742Ssam 1351116742Ssam/* 1352170530Ssam * Return the phy mode for with the specified channel. 1353116742Ssam */ 1354116742Ssamenum ieee80211_phymode 1355170530Ssamieee80211_chan2mode(const struct ieee80211_channel *chan) 1356116742Ssam{ 1357170530Ssam 1358170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) 1359170530Ssam return IEEE80211_MODE_11NA; 1360170530Ssam else if (IEEE80211_IS_CHAN_HTG(chan)) 1361170530Ssam return IEEE80211_MODE_11NG; 1362170530Ssam else if (IEEE80211_IS_CHAN_108G(chan)) 1363170530Ssam return IEEE80211_MODE_TURBO_G; 1364170530Ssam else if (IEEE80211_IS_CHAN_ST(chan)) 1365170530Ssam return IEEE80211_MODE_STURBO_A; 1366170530Ssam else if (IEEE80211_IS_CHAN_TURBO(chan)) 1367153350Ssam return IEEE80211_MODE_TURBO_A; 1368188782Ssam else if (IEEE80211_IS_CHAN_HALF(chan)) 1369188782Ssam return IEEE80211_MODE_HALF; 1370188782Ssam else if (IEEE80211_IS_CHAN_QUARTER(chan)) 1371188782Ssam return IEEE80211_MODE_QUARTER; 1372170530Ssam else if (IEEE80211_IS_CHAN_A(chan)) 1373116742Ssam return IEEE80211_MODE_11A; 1374170530Ssam else if (IEEE80211_IS_CHAN_ANYG(chan)) 1375116742Ssam return IEEE80211_MODE_11G; 1376170530Ssam else if (IEEE80211_IS_CHAN_B(chan)) 1377116742Ssam return IEEE80211_MODE_11B; 1378170530Ssam else if (IEEE80211_IS_CHAN_FHSS(chan)) 1379170530Ssam return IEEE80211_MODE_FH; 1380170530Ssam 1381170530Ssam /* NB: should not get here */ 1382170530Ssam printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", 1383170530Ssam __func__, chan->ic_freq, chan->ic_flags); 1384170530Ssam return IEEE80211_MODE_11B; 1385116742Ssam} 1386116742Ssam 1387170530Ssamstruct ratemedia { 1388170530Ssam u_int match; /* rate + mode */ 1389170530Ssam u_int media; /* if_media rate */ 1390170530Ssam}; 1391170530Ssam 1392170530Ssamstatic int 1393170530Ssamfindmedia(const struct ratemedia rates[], int n, u_int match) 1394170530Ssam{ 1395170530Ssam int i; 1396170530Ssam 1397170530Ssam for (i = 0; i < n; i++) 1398170530Ssam if (rates[i].match == match) 1399170530Ssam return rates[i].media; 1400170530Ssam return IFM_AUTO; 1401170530Ssam} 1402170530Ssam 1403116742Ssam/* 1404170530Ssam * Convert IEEE80211 rate value to ifmedia subtype. 1405170530Ssam * Rate is either a legacy rate in units of 0.5Mbps 1406170530Ssam * or an MCS index. 1407116742Ssam */ 1408116742Ssamint 1409116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1410116742Ssam{ 1411116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1412170530Ssam static const struct ratemedia rates[] = { 1413124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1414124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1415124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1416124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1417124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1418124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1419124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1420124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1421124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1422124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1423124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1424124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1425124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1426124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1427124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1428124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1429124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1430124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1431124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1432124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1433124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1434124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1435124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1436124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1437124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1438124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1439124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1440165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1441165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1442165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1443116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 1444116742Ssam }; 1445170530Ssam static const struct ratemedia htrates[] = { 1446170530Ssam { 0, IFM_IEEE80211_MCS }, 1447170530Ssam { 1, IFM_IEEE80211_MCS }, 1448170530Ssam { 2, IFM_IEEE80211_MCS }, 1449170530Ssam { 3, IFM_IEEE80211_MCS }, 1450170530Ssam { 4, IFM_IEEE80211_MCS }, 1451170530Ssam { 5, IFM_IEEE80211_MCS }, 1452170530Ssam { 6, IFM_IEEE80211_MCS }, 1453170530Ssam { 7, IFM_IEEE80211_MCS }, 1454170530Ssam { 8, IFM_IEEE80211_MCS }, 1455170530Ssam { 9, IFM_IEEE80211_MCS }, 1456170530Ssam { 10, IFM_IEEE80211_MCS }, 1457170530Ssam { 11, IFM_IEEE80211_MCS }, 1458170530Ssam { 12, IFM_IEEE80211_MCS }, 1459170530Ssam { 13, IFM_IEEE80211_MCS }, 1460170530Ssam { 14, IFM_IEEE80211_MCS }, 1461170530Ssam { 15, IFM_IEEE80211_MCS }, 1462170530Ssam }; 1463170530Ssam int m; 1464116742Ssam 1465170530Ssam /* 1466170530Ssam * Check 11n rates first for match as an MCS. 1467170530Ssam */ 1468170530Ssam if (mode == IEEE80211_MODE_11NA) { 1469172226Ssam if (rate & IEEE80211_RATE_MCS) { 1470172226Ssam rate &= ~IEEE80211_RATE_MCS; 1471170530Ssam m = findmedia(htrates, N(htrates), rate); 1472170530Ssam if (m != IFM_AUTO) 1473170530Ssam return m | IFM_IEEE80211_11NA; 1474170530Ssam } 1475170530Ssam } else if (mode == IEEE80211_MODE_11NG) { 1476170530Ssam /* NB: 12 is ambiguous, it will be treated as an MCS */ 1477172226Ssam if (rate & IEEE80211_RATE_MCS) { 1478172226Ssam rate &= ~IEEE80211_RATE_MCS; 1479170530Ssam m = findmedia(htrates, N(htrates), rate); 1480170530Ssam if (m != IFM_AUTO) 1481170530Ssam return m | IFM_IEEE80211_11NG; 1482170530Ssam } 1483170530Ssam } 1484170530Ssam rate &= IEEE80211_RATE_VAL; 1485116742Ssam switch (mode) { 1486116742Ssam case IEEE80211_MODE_11A: 1487188782Ssam case IEEE80211_MODE_HALF: /* XXX good 'nuf */ 1488188782Ssam case IEEE80211_MODE_QUARTER: 1489170530Ssam case IEEE80211_MODE_11NA: 1490138568Ssam case IEEE80211_MODE_TURBO_A: 1491170530Ssam case IEEE80211_MODE_STURBO_A: 1492170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); 1493116742Ssam case IEEE80211_MODE_11B: 1494170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); 1495124543Sonoe case IEEE80211_MODE_FH: 1496170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); 1497116742Ssam case IEEE80211_MODE_AUTO: 1498116742Ssam /* NB: ic may be NULL for some drivers */ 1499188775Ssam if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) 1500170530Ssam return findmedia(rates, N(rates), 1501170530Ssam rate | IFM_IEEE80211_FH); 1502116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1503116742Ssam /* fall thru... */ 1504116742Ssam case IEEE80211_MODE_11G: 1505170530Ssam case IEEE80211_MODE_11NG: 1506138568Ssam case IEEE80211_MODE_TURBO_G: 1507170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); 1508116742Ssam } 1509116742Ssam return IFM_AUTO; 1510116742Ssam#undef N 1511116742Ssam} 1512116742Ssam 1513116742Ssamint 1514116742Ssamieee80211_media2rate(int mword) 1515116742Ssam{ 1516116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1517116742Ssam static const int ieeerates[] = { 1518116742Ssam -1, /* IFM_AUTO */ 1519116742Ssam 0, /* IFM_MANUAL */ 1520116742Ssam 0, /* IFM_NONE */ 1521116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1522116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1523116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1524116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1525116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1526116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1527116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1528116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1529116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1530116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1531116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1532116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1533116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1534116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1535116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1536116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1537165569Ssam 0, /* IFM_IEEE80211_DS354k */ 1538165569Ssam 0, /* IFM_IEEE80211_DS512k */ 1539165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 1540165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 1541165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 1542170530Ssam -1, /* IFM_IEEE80211_MCS */ 1543116742Ssam }; 1544116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1545116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1546116742Ssam#undef N 1547116742Ssam} 1548