ieee80211.c revision 190532
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 190532 2009-03-29 21:17:08Z 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> 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, 224178521Ssam struct sockaddr *dst, struct rtentry *rt0) 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); 254165569Ssam /* 255165569Ssam * Fill in 802.11 available channel set, mark all 256165569Ssam * available channels as active, and pick a default 257165569Ssam * channel if not already specified. 258165569Ssam */ 259178354Ssam ieee80211_media_init(ic); 260170530Ssam 261178354Ssam ic->ic_update_mcast = null_update_mcast; 262178354Ssam ic->ic_update_promisc = null_update_promisc; 263116742Ssam 264155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 265155688Ssam ic->ic_lintval = ic->ic_bintval; 266138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 267138568Ssam 268170530Ssam ieee80211_crypto_attach(ic); 269138568Ssam ieee80211_node_attach(ic); 270170530Ssam ieee80211_power_attach(ic); 271138568Ssam ieee80211_proto_attach(ic); 272190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 273190391Ssam ieee80211_superg_attach(ic); 274190391Ssam#endif 275170530Ssam ieee80211_ht_attach(ic); 276170530Ssam ieee80211_scan_attach(ic); 277178354Ssam ieee80211_regdomain_attach(ic); 278138568Ssam 279178354Ssam ieee80211_sysctl_attach(ic); 280138568Ssam 281178354Ssam ifp->if_addrlen = IEEE80211_ADDR_LEN; 282178354Ssam ifp->if_hdrlen = 0; 283178354Ssam if_attach(ifp); 284178354Ssam ifp->if_mtu = IEEE80211_MTU_MAX; 285178354Ssam ifp->if_broadcastaddr = ieee80211broadcastaddr; 286178521Ssam ifp->if_output = null_output; 287178521Ssam ifp->if_input = null_input; /* just in case */ 288178521Ssam ifp->if_resolvemulti = NULL; /* NB: callers check */ 289140915Ssam 290178354Ssam ifa = ifaddr_byindex(ifp->if_index); 291178354Ssam KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); 292178354Ssam sdl = (struct sockaddr_dl *)ifa->ifa_addr; 293178354Ssam sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ 294178354Ssam sdl->sdl_alen = IEEE80211_ADDR_LEN; 295190526Ssam IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr); 296116742Ssam} 297116742Ssam 298178354Ssam/* 299178354Ssam * Detach net80211 state on device detach. Tear down 300178354Ssam * all vap's and reclaim all common state prior to the 301178354Ssam * device state going away. Note we may call back into 302178354Ssam * driver; it must be prepared for this. 303178354Ssam */ 304116742Ssamvoid 305138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 306116742Ssam{ 307138568Ssam struct ifnet *ifp = ic->ic_ifp; 308178354Ssam struct ieee80211vap *vap; 309116742Ssam 310178354Ssam while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) 311178354Ssam ieee80211_vap_destroy(vap); 312188533Sthompsa ieee80211_waitfor_parent(ic); 313138568Ssam 314138568Ssam ieee80211_sysctl_detach(ic); 315178354Ssam ieee80211_regdomain_detach(ic); 316170530Ssam ieee80211_scan_detach(ic); 317190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 318190391Ssam ieee80211_superg_detach(ic); 319190391Ssam#endif 320170530Ssam ieee80211_ht_detach(ic); 321166012Ssam /* NB: must be called before ieee80211_node_detach */ 322138568Ssam ieee80211_proto_detach(ic); 323138568Ssam ieee80211_crypto_detach(ic); 324170530Ssam ieee80211_power_detach(ic); 325138568Ssam ieee80211_node_detach(ic); 326116742Ssam ifmedia_removeall(&ic->ic_media); 327138568Ssam 328170530Ssam IEEE80211_LOCK_DESTROY(ic); 329178354Ssam if_detach(ifp); 330178354Ssam} 331138568Ssam 332178354Ssam/* 333178354Ssam * Default reset method for use with the ioctl support. This 334178354Ssam * method is invoked after any state change in the 802.11 335178354Ssam * layer that should be propagated to the hardware but not 336178354Ssam * require re-initialization of the 802.11 state machine (e.g 337178354Ssam * rescanning for an ap). We always return ENETRESET which 338178354Ssam * should cause the driver to re-initialize the device. Drivers 339178354Ssam * can override this method to implement more optimized support. 340178354Ssam */ 341178354Ssamstatic int 342178354Ssamdefault_reset(struct ieee80211vap *vap, u_long cmd) 343178354Ssam{ 344178354Ssam return ENETRESET; 345178354Ssam} 346178354Ssam 347178354Ssam/* 348178354Ssam * Prepare a vap for use. Drivers use this call to 349178354Ssam * setup net80211 state in new vap's prior attaching 350178354Ssam * them with ieee80211_vap_attach (below). 351178354Ssam */ 352178354Ssamint 353178354Ssamieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, 354178354Ssam const char name[IFNAMSIZ], int unit, int opmode, int flags, 355178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 356178354Ssam const uint8_t macaddr[IEEE80211_ADDR_LEN]) 357178354Ssam{ 358178354Ssam struct ifnet *ifp; 359178354Ssam 360178354Ssam ifp = if_alloc(IFT_ETHER); 361178354Ssam if (ifp == NULL) { 362178354Ssam if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n", 363178354Ssam __func__); 364178354Ssam return ENOMEM; 365178354Ssam } 366178354Ssam if_initname(ifp, name, unit); 367178354Ssam ifp->if_softc = vap; /* back pointer */ 368178354Ssam ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 369178354Ssam ifp->if_start = ieee80211_start; 370178354Ssam ifp->if_ioctl = ieee80211_ioctl; 371178354Ssam ifp->if_watchdog = NULL; /* NB: no watchdog routine */ 372178354Ssam ifp->if_init = ieee80211_init; 373178354Ssam /* NB: input+output filled in by ether_ifattach */ 374178354Ssam IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 375178354Ssam ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 376178354Ssam IFQ_SET_READY(&ifp->if_snd); 377178354Ssam 378178354Ssam vap->iv_ifp = ifp; 379178354Ssam vap->iv_ic = ic; 380178354Ssam vap->iv_flags = ic->ic_flags; /* propagate common flags */ 381178354Ssam vap->iv_flags_ext = ic->ic_flags_ext; 382178354Ssam vap->iv_flags_ven = ic->ic_flags_ven; 383178354Ssam vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; 384178354Ssam vap->iv_htcaps = ic->ic_htcaps; 385178354Ssam vap->iv_opmode = opmode; 386178957Ssam vap->iv_caps |= ieee80211_opcap[opmode]; 387178354Ssam switch (opmode) { 388178354Ssam case IEEE80211_M_WDS: 389178354Ssam /* 390178354Ssam * WDS links must specify the bssid of the far end. 391178354Ssam * For legacy operation this is a static relationship. 392178354Ssam * For non-legacy operation the station must associate 393178354Ssam * and be authorized to pass traffic. Plumbing the 394178354Ssam * vap to the proper node happens when the vap 395178354Ssam * transitions to RUN state. 396178354Ssam */ 397178354Ssam IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); 398178354Ssam vap->iv_flags |= IEEE80211_F_DESBSSID; 399178354Ssam if (flags & IEEE80211_CLONE_WDSLEGACY) 400178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; 401178354Ssam break; 402186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 403186904Ssam case IEEE80211_M_AHDEMO: 404186904Ssam if (flags & IEEE80211_CLONE_TDMA) { 405186904Ssam /* NB: checked before clone operation allowed */ 406186904Ssam KASSERT(ic->ic_caps & IEEE80211_C_TDMA, 407186904Ssam ("not TDMA capable, ic_caps 0x%x", ic->ic_caps)); 408186904Ssam /* 409186904Ssam * Propagate TDMA capability to mark vap; this 410186904Ssam * cannot be removed and is used to distinguish 411186904Ssam * regular ahdemo operation from ahdemo+tdma. 412186904Ssam */ 413186904Ssam vap->iv_caps |= IEEE80211_C_TDMA; 414186904Ssam } 415186904Ssam break; 416186904Ssam#endif 417178354Ssam } 418184278Ssam /* auto-enable s/w beacon miss support */ 419184278Ssam if (flags & IEEE80211_CLONE_NOBEACONS) 420184278Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; 421178354Ssam /* 422178354Ssam * Enable various functionality by default if we're 423178354Ssam * capable; the driver can override us if it knows better. 424178354Ssam */ 425178354Ssam if (vap->iv_caps & IEEE80211_C_WME) 426178354Ssam vap->iv_flags |= IEEE80211_F_WME; 427178354Ssam if (vap->iv_caps & IEEE80211_C_BURST) 428178354Ssam vap->iv_flags |= IEEE80211_F_BURST; 429178354Ssam /* NB: bg scanning only makes sense for station mode right now */ 430178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 431178354Ssam (vap->iv_caps & IEEE80211_C_BGSCAN)) 432178354Ssam vap->iv_flags |= IEEE80211_F_BGSCAN; 433178957Ssam vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ 434178954Ssam /* NB: DFS support only makes sense for ap mode right now */ 435178954Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 436178954Ssam (vap->iv_caps & IEEE80211_C_DFS)) 437178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_DFS; 438178354Ssam 439178354Ssam vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 440178354Ssam vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 441178354Ssam vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; 442178354Ssam /* 443178354Ssam * Install a default reset method for the ioctl support; 444178354Ssam * the driver can override this. 445178354Ssam */ 446178354Ssam vap->iv_reset = default_reset; 447178354Ssam 448178354Ssam IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); 449178354Ssam 450178354Ssam ieee80211_sysctl_vattach(vap); 451178354Ssam ieee80211_crypto_vattach(vap); 452178354Ssam ieee80211_node_vattach(vap); 453178354Ssam ieee80211_power_vattach(vap); 454178354Ssam ieee80211_proto_vattach(vap); 455190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 456190391Ssam ieee80211_superg_vattach(vap); 457190391Ssam#endif 458178354Ssam ieee80211_ht_vattach(vap); 459178354Ssam ieee80211_scan_vattach(vap); 460178354Ssam ieee80211_regdomain_vattach(vap); 461178354Ssam 462178354Ssam return 0; 463178354Ssam} 464178354Ssam 465178354Ssam/* 466178354Ssam * Activate a vap. State should have been prepared with a 467178354Ssam * call to ieee80211_vap_setup and by the driver. On return 468178354Ssam * from this call the vap is ready for use. 469178354Ssam */ 470178354Ssamint 471178354Ssamieee80211_vap_attach(struct ieee80211vap *vap, 472178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 473178354Ssam{ 474178354Ssam struct ifnet *ifp = vap->iv_ifp; 475178354Ssam struct ieee80211com *ic = vap->iv_ic; 476178354Ssam struct ifmediareq imr; 477178354Ssam int maxrate; 478178354Ssam 479178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 480178354Ssam "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", 481178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 482178354Ssam ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); 483178354Ssam 484178354Ssam /* 485178354Ssam * Do late attach work that cannot happen until after 486178354Ssam * the driver has had a chance to override defaults. 487178354Ssam */ 488178354Ssam ieee80211_node_latevattach(vap); 489178354Ssam ieee80211_power_latevattach(vap); 490178354Ssam 491178354Ssam maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, 492178354Ssam vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); 493178354Ssam ieee80211_media_status(ifp, &imr); 494178354Ssam /* NB: strip explicit mode; we're actually in autoselect */ 495188106Ssam ifmedia_set(&vap->iv_media, 496188106Ssam imr.ifm_active &~ (IFM_MMASK | IFM_IEEE80211_TURBO)); 497178354Ssam if (maxrate) 498178354Ssam ifp->if_baudrate = IF_Mbps(maxrate); 499178354Ssam 500178354Ssam ether_ifattach(ifp, vap->iv_myaddr); 501178354Ssam /* hook output method setup by ether_ifattach */ 502178354Ssam vap->iv_output = ifp->if_output; 503178354Ssam ifp->if_output = ieee80211_output; 504178354Ssam /* NB: if_mtu set by ether_ifattach to ETHERMTU */ 505178354Ssam bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf); 506178354Ssam 507178354Ssam IEEE80211_LOCK(ic); 508178354Ssam TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); 509178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 510190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 511178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 512190391Ssam#endif 513178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 514178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 515178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); 516178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); 517178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 518178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 519178354Ssam IEEE80211_UNLOCK(ic); 520178354Ssam 521178354Ssam return 1; 522178354Ssam} 523178354Ssam 524178354Ssam/* 525178354Ssam * Tear down vap state and reclaim the ifnet. 526178354Ssam * The driver is assumed to have prepared for 527178354Ssam * this; e.g. by turning off interrupts for the 528178354Ssam * underlying device. 529178354Ssam */ 530178354Ssamvoid 531178354Ssamieee80211_vap_detach(struct ieee80211vap *vap) 532178354Ssam{ 533178354Ssam struct ieee80211com *ic = vap->iv_ic; 534178354Ssam struct ifnet *ifp = vap->iv_ifp; 535178354Ssam 536178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", 537178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 538178354Ssam ic->ic_ifp->if_xname); 539178354Ssam 540178354Ssam IEEE80211_LOCK(ic); 541178354Ssam /* block traffic from above */ 542178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 543178354Ssam /* 544178354Ssam * Evil hack. Clear the backpointer from the ifnet to the 545178354Ssam * vap so any requests from above will return an error or 546178354Ssam * be ignored. In particular this short-circuits requests 547178354Ssam * by the bridge to turn off promiscuous mode as a result 548178354Ssam * of calling ether_ifdetach. 549178354Ssam */ 550178354Ssam ifp->if_softc = NULL; 551178354Ssam /* 552178354Ssam * Stop the vap before detaching the ifnet. Ideally we'd 553178354Ssam * do this in the other order so the ifnet is inaccessible 554178354Ssam * while we cleanup internal state but that is hard. 555178354Ssam */ 556178354Ssam ieee80211_stop_locked(vap); 557178354Ssam 558178354Ssam TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); 559178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 560190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 561178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 562190391Ssam#endif 563178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 564178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 565178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); 566178354Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); 567178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 568178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 569178354Ssam IEEE80211_UNLOCK(ic); 570178354Ssam 571178354Ssam /* XXX can't hold com lock */ 572178354Ssam /* NB: bpfattach is called by ether_ifdetach and claims all taps */ 573116742Ssam ether_ifdetach(ifp); 574178354Ssam 575178354Ssam ifmedia_removeall(&vap->iv_media); 576178354Ssam 577178354Ssam ieee80211_regdomain_vdetach(vap); 578178354Ssam ieee80211_scan_vdetach(vap); 579190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 580190391Ssam ieee80211_superg_vdetach(vap); 581190391Ssam#endif 582178354Ssam ieee80211_ht_vdetach(vap); 583178354Ssam /* NB: must be before ieee80211_node_vdetach */ 584178354Ssam ieee80211_proto_vdetach(vap); 585178354Ssam ieee80211_crypto_vdetach(vap); 586178354Ssam ieee80211_power_vdetach(vap); 587178354Ssam ieee80211_node_vdetach(vap); 588178354Ssam ieee80211_sysctl_vdetach(vap); 589182674Sweongyo 590182674Sweongyo if_free(ifp); 591116742Ssam} 592116742Ssam 593178354Ssam/* 594178354Ssam * Synchronize flag bit state in the parent ifnet structure 595178354Ssam * according to the state of all vap ifnet's. This is used, 596178354Ssam * for example, to handle IFF_PROMISC and IFF_ALLMULTI. 597178354Ssam */ 598178354Ssamvoid 599178354Ssamieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) 600178354Ssam{ 601178354Ssam struct ifnet *ifp = ic->ic_ifp; 602178354Ssam struct ieee80211vap *vap; 603178354Ssam int bit, oflags; 604178354Ssam 605178354Ssam IEEE80211_LOCK_ASSERT(ic); 606178354Ssam 607178354Ssam bit = 0; 608178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 609178354Ssam if (vap->iv_ifp->if_flags & flag) { 610178354Ssam /* 611178354Ssam * XXX the bridge sets PROMISC but we don't want to 612178354Ssam * enable it on the device, discard here so all the 613178354Ssam * drivers don't need to special-case it 614178354Ssam */ 615178354Ssam if (flag == IFF_PROMISC && 616178354Ssam vap->iv_opmode == IEEE80211_M_HOSTAP) 617178354Ssam continue; 618178354Ssam bit = 1; 619178354Ssam break; 620178354Ssam } 621178354Ssam oflags = ifp->if_flags; 622178354Ssam if (bit) 623178354Ssam ifp->if_flags |= flag; 624178354Ssam else 625178354Ssam ifp->if_flags &= ~flag; 626178354Ssam if ((ifp->if_flags ^ oflags) & flag) { 627178354Ssam /* XXX should we return 1/0 and let caller do this? */ 628178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 629178354Ssam if (flag == IFF_PROMISC) 630178354Ssam ic->ic_update_promisc(ifp); 631178354Ssam else if (flag == IFF_ALLMULTI) 632178354Ssam ic->ic_update_mcast(ifp); 633178354Ssam } 634178354Ssam } 635178354Ssam} 636178354Ssam 637178354Ssam/* 638178354Ssam * Synchronize flag bit state in the com structure 639178354Ssam * according to the state of all vap's. This is used, 640178354Ssam * for example, to handle state changes via ioctls. 641178354Ssam */ 642178354Ssamstatic void 643178354Ssamieee80211_syncflag_locked(struct ieee80211com *ic, int flag) 644178354Ssam{ 645178354Ssam struct ieee80211vap *vap; 646178354Ssam int bit; 647178354Ssam 648178354Ssam IEEE80211_LOCK_ASSERT(ic); 649178354Ssam 650178354Ssam bit = 0; 651178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 652178354Ssam if (vap->iv_flags & flag) { 653178354Ssam bit = 1; 654178354Ssam break; 655178354Ssam } 656178354Ssam if (bit) 657178354Ssam ic->ic_flags |= flag; 658178354Ssam else 659178354Ssam ic->ic_flags &= ~flag; 660178354Ssam} 661178354Ssam 662178354Ssamvoid 663178354Ssamieee80211_syncflag(struct ieee80211vap *vap, int flag) 664178354Ssam{ 665178354Ssam struct ieee80211com *ic = vap->iv_ic; 666178354Ssam 667178354Ssam IEEE80211_LOCK(ic); 668178354Ssam if (flag < 0) { 669178354Ssam flag = -flag; 670178354Ssam vap->iv_flags &= ~flag; 671178354Ssam } else 672178354Ssam vap->iv_flags |= flag; 673178354Ssam ieee80211_syncflag_locked(ic, flag); 674178354Ssam IEEE80211_UNLOCK(ic); 675178354Ssam} 676178354Ssam 677178354Ssam/* 678178354Ssam * Synchronize flag bit state in the com structure 679178354Ssam * according to the state of all vap's. This is used, 680178354Ssam * for example, to handle state changes via ioctls. 681178354Ssam */ 682178354Ssamstatic void 683178354Ssamieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) 684178354Ssam{ 685178354Ssam struct ieee80211vap *vap; 686178354Ssam int bit; 687178354Ssam 688178354Ssam IEEE80211_LOCK_ASSERT(ic); 689178354Ssam 690178354Ssam bit = 0; 691178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 692178354Ssam if (vap->iv_flags_ext & flag) { 693178354Ssam bit = 1; 694178354Ssam break; 695178354Ssam } 696178354Ssam if (bit) 697178354Ssam ic->ic_flags_ext |= flag; 698178354Ssam else 699178354Ssam ic->ic_flags_ext &= ~flag; 700178354Ssam} 701178354Ssam 702178354Ssamvoid 703178354Ssamieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) 704178354Ssam{ 705178354Ssam struct ieee80211com *ic = vap->iv_ic; 706178354Ssam 707178354Ssam IEEE80211_LOCK(ic); 708178354Ssam if (flag < 0) { 709178354Ssam flag = -flag; 710178354Ssam vap->iv_flags_ext &= ~flag; 711178354Ssam } else 712178354Ssam vap->iv_flags_ext |= flag; 713178354Ssam ieee80211_syncflag_ext_locked(ic, flag); 714178354Ssam IEEE80211_UNLOCK(ic); 715178354Ssam} 716178354Ssam 717166012Ssamstatic __inline int 718166012Ssammapgsm(u_int freq, u_int flags) 719166012Ssam{ 720166012Ssam freq *= 10; 721166012Ssam if (flags & IEEE80211_CHAN_QUARTER) 722166012Ssam freq += 5; 723166012Ssam else if (flags & IEEE80211_CHAN_HALF) 724166012Ssam freq += 10; 725166012Ssam else 726166012Ssam freq += 20; 727166012Ssam /* NB: there is no 907/20 wide but leave room */ 728166012Ssam return (freq - 906*10) / 5; 729166012Ssam} 730166012Ssam 731166012Ssamstatic __inline int 732166012Ssammappsb(u_int freq, u_int flags) 733166012Ssam{ 734166012Ssam return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 735166012Ssam} 736166012Ssam 737116742Ssam/* 738116742Ssam * Convert MHz frequency to IEEE channel number. 739116742Ssam */ 740152450Ssamint 741116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 742116742Ssam{ 743167430Ssam#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) 744166012Ssam if (flags & IEEE80211_CHAN_GSM) 745166012Ssam return mapgsm(freq, flags); 746116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 747116742Ssam if (freq == 2484) 748116742Ssam return 14; 749116742Ssam if (freq < 2484) 750152450Ssam return ((int) freq - 2407) / 5; 751116742Ssam else 752116742Ssam return 15 + ((freq - 2512) / 20); 753116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 754165569Ssam if (freq <= 5000) { 755170530Ssam /* XXX check regdomain? */ 756167430Ssam if (IS_FREQ_IN_PSB(freq)) 757166012Ssam return mappsb(freq, flags); 758152450Ssam return (freq - 4000) / 5; 759165569Ssam } else 760152450Ssam return (freq - 5000) / 5; 761116742Ssam } else { /* either, guess */ 762116742Ssam if (freq == 2484) 763116742Ssam return 14; 764166012Ssam if (freq < 2484) { 765166012Ssam if (907 <= freq && freq <= 922) 766166012Ssam return mapgsm(freq, flags); 767152450Ssam return ((int) freq - 2407) / 5; 768166012Ssam } 769152450Ssam if (freq < 5000) { 770167430Ssam if (IS_FREQ_IN_PSB(freq)) 771166012Ssam return mappsb(freq, flags); 772165569Ssam else if (freq > 4900) 773152450Ssam return (freq - 4000) / 5; 774152450Ssam else 775152450Ssam return 15 + ((freq - 2512) / 20); 776152450Ssam } 777116742Ssam return (freq - 5000) / 5; 778116742Ssam } 779167430Ssam#undef IS_FREQ_IN_PSB 780116742Ssam} 781116742Ssam 782116742Ssam/* 783116742Ssam * Convert channel to IEEE channel number. 784116742Ssam */ 785152450Ssamint 786165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) 787116742Ssam{ 788170530Ssam if (c == NULL) { 789138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 790117039Ssam return 0; /* XXX */ 791116742Ssam } 792170530Ssam return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); 793116742Ssam} 794116742Ssam 795116742Ssam/* 796116742Ssam * Convert IEEE channel number to MHz frequency. 797116742Ssam */ 798116742Ssamu_int 799116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 800116742Ssam{ 801166012Ssam if (flags & IEEE80211_CHAN_GSM) 802166012Ssam return 907 + 5 * (chan / 10); 803116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 804116742Ssam if (chan == 14) 805116742Ssam return 2484; 806116742Ssam if (chan < 14) 807116742Ssam return 2407 + chan*5; 808116742Ssam else 809116742Ssam return 2512 + ((chan-15)*20); 810116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 811165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 812165569Ssam chan -= 37; 813165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 814165569Ssam } 815116742Ssam return 5000 + (chan*5); 816116742Ssam } else { /* either, guess */ 817166012Ssam /* XXX can't distinguish PSB+GSM channels */ 818116742Ssam if (chan == 14) 819116742Ssam return 2484; 820116742Ssam if (chan < 14) /* 0-13 */ 821116742Ssam return 2407 + chan*5; 822116742Ssam if (chan < 27) /* 15-26 */ 823116742Ssam return 2512 + ((chan-15)*20); 824116742Ssam return 5000 + (chan*5); 825116742Ssam } 826116742Ssam} 827116742Ssam 828116742Ssam/* 829170530Ssam * Locate a channel given a frequency+flags. We cache 830178354Ssam * the previous lookup to optimize switching between two 831170530Ssam * channels--as happens with dynamic turbo. 832170530Ssam */ 833170530Ssamstruct ieee80211_channel * 834170530Ssamieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) 835170530Ssam{ 836170530Ssam struct ieee80211_channel *c; 837170530Ssam int i; 838170530Ssam 839170530Ssam flags &= IEEE80211_CHAN_ALLTURBO; 840170530Ssam c = ic->ic_prevchan; 841170530Ssam if (c != NULL && c->ic_freq == freq && 842170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 843170530Ssam return c; 844170530Ssam /* brute force search */ 845170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 846170530Ssam c = &ic->ic_channels[i]; 847170530Ssam if (c->ic_freq == freq && 848170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 849170530Ssam return c; 850170530Ssam } 851170530Ssam return NULL; 852170530Ssam} 853170530Ssam 854173861Ssam/* 855173861Ssam * Locate a channel given a channel number+flags. We cache 856173861Ssam * the previous lookup to optimize switching between two 857173861Ssam * channels--as happens with dynamic turbo. 858173861Ssam */ 859173861Ssamstruct ieee80211_channel * 860173861Ssamieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) 861173861Ssam{ 862173861Ssam struct ieee80211_channel *c; 863173861Ssam int i; 864173861Ssam 865173861Ssam flags &= IEEE80211_CHAN_ALLTURBO; 866173861Ssam c = ic->ic_prevchan; 867173861Ssam if (c != NULL && c->ic_ieee == ieee && 868173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 869173861Ssam return c; 870173861Ssam /* brute force search */ 871173861Ssam for (i = 0; i < ic->ic_nchans; i++) { 872173861Ssam c = &ic->ic_channels[i]; 873173861Ssam if (c->ic_ieee == ieee && 874173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 875173861Ssam return c; 876173861Ssam } 877173861Ssam return NULL; 878173861Ssam} 879173861Ssam 880170530Ssamstatic void 881178354Ssamaddmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) 882170530Ssam{ 883170530Ssam#define ADD(_ic, _s, _o) \ 884178354Ssam ifmedia_add(media, \ 885170530Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 886170530Ssam static const u_int mopts[IEEE80211_MODE_MAX] = { 887188106Ssam [IEEE80211_MODE_AUTO] = IFM_AUTO, 888188106Ssam [IEEE80211_MODE_11A] = IFM_IEEE80211_11A, 889188106Ssam [IEEE80211_MODE_11B] = IFM_IEEE80211_11B, 890188106Ssam [IEEE80211_MODE_11G] = IFM_IEEE80211_11G, 891188106Ssam [IEEE80211_MODE_FH] = IFM_IEEE80211_FH, 892188106Ssam [IEEE80211_MODE_TURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 893188106Ssam [IEEE80211_MODE_TURBO_G] = IFM_IEEE80211_11G|IFM_IEEE80211_TURBO, 894188106Ssam [IEEE80211_MODE_STURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 895188782Ssam [IEEE80211_MODE_HALF] = IFM_IEEE80211_11A, /* XXX */ 896188782Ssam [IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */ 897188106Ssam [IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA, 898188106Ssam [IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG, 899170530Ssam }; 900170530Ssam u_int mopt; 901170530Ssam 902170530Ssam mopt = mopts[mode]; 903178354Ssam if (addsta) 904178354Ssam ADD(ic, mword, mopt); /* STA mode has no cap */ 905178354Ssam if (caps & IEEE80211_C_IBSS) 906178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); 907178354Ssam if (caps & IEEE80211_C_HOSTAP) 908178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); 909178354Ssam if (caps & IEEE80211_C_AHDEMO) 910178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 911178354Ssam if (caps & IEEE80211_C_MONITOR) 912178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); 913178354Ssam if (caps & IEEE80211_C_WDS) 914178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_WDS); 915170530Ssam#undef ADD 916170530Ssam} 917170530Ssam 918170530Ssam/* 919116742Ssam * Setup the media data structures according to the channel and 920178354Ssam * rate tables. 921116742Ssam */ 922178354Ssamstatic int 923178354Ssamieee80211_media_setup(struct ieee80211com *ic, 924178354Ssam struct ifmedia *media, int caps, int addsta, 925116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 926116742Ssam{ 927170530Ssam int i, j, mode, rate, maxrate, mword, r; 928170530Ssam const struct ieee80211_rateset *rs; 929116742Ssam struct ieee80211_rateset allrates; 930116742Ssam 931118887Ssam /* 932116742Ssam * Fill in media characteristics. 933116742Ssam */ 934178354Ssam ifmedia_init(media, 0, media_change, media_stat); 935116742Ssam maxrate = 0; 936170530Ssam /* 937170530Ssam * Add media for legacy operating modes. 938170530Ssam */ 939116742Ssam memset(&allrates, 0, sizeof(allrates)); 940170530Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { 941167468Ssam if (isclr(ic->ic_modecaps, mode)) 942116742Ssam continue; 943178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 944116742Ssam if (mode == IEEE80211_MODE_AUTO) 945116742Ssam continue; 946116742Ssam rs = &ic->ic_sup_rates[mode]; 947116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 948116742Ssam rate = rs->rs_rates[i]; 949116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 950116742Ssam if (mword == 0) 951116742Ssam continue; 952178354Ssam addmedia(media, caps, addsta, mode, mword); 953116742Ssam /* 954170530Ssam * Add legacy rate to the collection of all rates. 955116742Ssam */ 956116742Ssam r = rate & IEEE80211_RATE_VAL; 957116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 958116742Ssam if (allrates.rs_rates[j] == r) 959116742Ssam break; 960116742Ssam if (j == allrates.rs_nrates) { 961116742Ssam /* unique, add to the set */ 962116742Ssam allrates.rs_rates[j] = r; 963116742Ssam allrates.rs_nrates++; 964116742Ssam } 965116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 966116742Ssam if (rate > maxrate) 967116742Ssam maxrate = rate; 968116742Ssam } 969116742Ssam } 970116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 971116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 972116742Ssam IEEE80211_MODE_AUTO); 973116742Ssam if (mword == 0) 974116742Ssam continue; 975170530Ssam /* NB: remove media options from mword */ 976178354Ssam addmedia(media, caps, addsta, 977178354Ssam IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); 978116742Ssam } 979170530Ssam /* 980170530Ssam * Add HT/11n media. Note that we do not have enough 981170530Ssam * bits in the media subtype to express the MCS so we 982170530Ssam * use a "placeholder" media subtype and any fixed MCS 983170530Ssam * must be specified with a different mechanism. 984170530Ssam */ 985188782Ssam for (; mode <= IEEE80211_MODE_11NG; mode++) { 986170530Ssam if (isclr(ic->ic_modecaps, mode)) 987170530Ssam continue; 988178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 989178354Ssam addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); 990170530Ssam } 991170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 992170530Ssam isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 993178354Ssam addmedia(media, caps, addsta, 994178354Ssam IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); 995170530Ssam /* XXX could walk htrates */ 996170530Ssam /* XXX known array size */ 997178354Ssam if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) 998178354Ssam maxrate = ieee80211_htrates[15].ht40_rate_400ns; 999170530Ssam } 1000178354Ssam return maxrate; 1001178354Ssam} 1002116742Ssam 1003178354Ssamvoid 1004178354Ssamieee80211_media_init(struct ieee80211com *ic) 1005178354Ssam{ 1006178354Ssam struct ifnet *ifp = ic->ic_ifp; 1007178354Ssam int maxrate; 1008178354Ssam 1009178354Ssam /* NB: this works because the structure is initialized to zero */ 1010178354Ssam if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { 1011178354Ssam /* 1012178354Ssam * We are re-initializing the channel list; clear 1013178354Ssam * the existing media state as the media routines 1014178354Ssam * don't suppress duplicates. 1015178354Ssam */ 1016178354Ssam ifmedia_removeall(&ic->ic_media); 1017178354Ssam } 1018178354Ssam ieee80211_chan_init(ic); 1019178354Ssam 1020178354Ssam /* 1021178354Ssam * Recalculate media settings in case new channel list changes 1022178354Ssam * the set of available modes. 1023178354Ssam */ 1024178354Ssam maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, 1025178354Ssam ieee80211com_media_change, ieee80211com_media_status); 1026170530Ssam /* NB: strip explicit mode; we're actually in autoselect */ 1027170530Ssam ifmedia_set(&ic->ic_media, 1028188106Ssam media_status(ic->ic_opmode, ic->ic_curchan) &~ 1029188106Ssam (IFM_MMASK | IFM_IEEE80211_TURBO)); 1030116742Ssam if (maxrate) 1031116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 1032178354Ssam 1033178354Ssam /* XXX need to propagate new media settings to vap's */ 1034116742Ssam} 1035116742Ssam 1036188782Ssam/* XXX inline or eliminate? */ 1037165569Ssamconst struct ieee80211_rateset * 1038165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 1039165569Ssam{ 1040188774Ssam /* XXX does this work for 11ng basic rates? */ 1041170530Ssam return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; 1042165569Ssam} 1043165569Ssam 1044138568Ssamvoid 1045138568Ssamieee80211_announce(struct ieee80211com *ic) 1046138568Ssam{ 1047138568Ssam struct ifnet *ifp = ic->ic_ifp; 1048138568Ssam int i, mode, rate, mword; 1049170530Ssam const struct ieee80211_rateset *rs; 1050138568Ssam 1051172227Ssam /* NB: skip AUTO since it has no rates */ 1052172227Ssam for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { 1053167468Ssam if (isclr(ic->ic_modecaps, mode)) 1054138568Ssam continue; 1055138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 1056138568Ssam rs = &ic->ic_sup_rates[mode]; 1057138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 1058170530Ssam mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 1059138568Ssam if (mword == 0) 1060138568Ssam continue; 1061170530Ssam rate = ieee80211_media2rate(mword); 1062138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 1063170530Ssam rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 1064138568Ssam } 1065138568Ssam printf("\n"); 1066138568Ssam } 1067170530Ssam ieee80211_ht_announce(ic); 1068138568Ssam} 1069138568Ssam 1070170530Ssamvoid 1071170530Ssamieee80211_announce_channels(struct ieee80211com *ic) 1072116742Ssam{ 1073170530Ssam const struct ieee80211_channel *c; 1074170530Ssam char type; 1075170530Ssam int i, cw; 1076170530Ssam 1077170530Ssam printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); 1078170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 1079170530Ssam c = &ic->ic_channels[i]; 1080170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 1081170530Ssam type = 'S'; 1082170530Ssam else if (IEEE80211_IS_CHAN_108A(c)) 1083170530Ssam type = 'T'; 1084170530Ssam else if (IEEE80211_IS_CHAN_108G(c)) 1085170530Ssam type = 'G'; 1086170530Ssam else if (IEEE80211_IS_CHAN_HT(c)) 1087170530Ssam type = 'n'; 1088170530Ssam else if (IEEE80211_IS_CHAN_A(c)) 1089170530Ssam type = 'a'; 1090170530Ssam else if (IEEE80211_IS_CHAN_ANYG(c)) 1091170530Ssam type = 'g'; 1092170530Ssam else if (IEEE80211_IS_CHAN_B(c)) 1093170530Ssam type = 'b'; 1094170530Ssam else 1095170530Ssam type = 'f'; 1096170530Ssam if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) 1097170530Ssam cw = 40; 1098170530Ssam else if (IEEE80211_IS_CHAN_HALF(c)) 1099170530Ssam cw = 10; 1100170530Ssam else if (IEEE80211_IS_CHAN_QUARTER(c)) 1101170530Ssam cw = 5; 1102170530Ssam else 1103170530Ssam cw = 20; 1104170530Ssam printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" 1105170530Ssam , c->ic_ieee, c->ic_freq, type 1106170530Ssam , cw 1107170530Ssam , IEEE80211_IS_CHAN_HT40U(c) ? '+' : 1108170530Ssam IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' 1109170530Ssam , c->ic_maxregpower 1110170530Ssam , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 1111170530Ssam , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 1112170530Ssam ); 1113170530Ssam } 1114116742Ssam} 1115116742Ssam 1116170530Ssamstatic int 1117184273Ssammedia2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) 1118170530Ssam{ 1119116742Ssam switch (IFM_MODE(ime->ifm_media)) { 1120116742Ssam case IFM_IEEE80211_11A: 1121178354Ssam *mode = IEEE80211_MODE_11A; 1122116742Ssam break; 1123116742Ssam case IFM_IEEE80211_11B: 1124178354Ssam *mode = IEEE80211_MODE_11B; 1125116742Ssam break; 1126116742Ssam case IFM_IEEE80211_11G: 1127178354Ssam *mode = IEEE80211_MODE_11G; 1128116742Ssam break; 1129124543Sonoe case IFM_IEEE80211_FH: 1130178354Ssam *mode = IEEE80211_MODE_FH; 1131124543Sonoe break; 1132170530Ssam case IFM_IEEE80211_11NA: 1133178354Ssam *mode = IEEE80211_MODE_11NA; 1134170530Ssam break; 1135170530Ssam case IFM_IEEE80211_11NG: 1136178354Ssam *mode = IEEE80211_MODE_11NG; 1137170530Ssam break; 1138116742Ssam case IFM_AUTO: 1139178354Ssam *mode = IEEE80211_MODE_AUTO; 1140116742Ssam break; 1141116742Ssam default: 1142178354Ssam return 0; 1143116742Ssam } 1144116742Ssam /* 1145138568Ssam * Turbo mode is an ``option''. 1146138568Ssam * XXX does not apply to AUTO 1147116742Ssam */ 1148116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 1149178354Ssam if (*mode == IEEE80211_MODE_11A) { 1150184273Ssam if (flags & IEEE80211_F_TURBOP) 1151178354Ssam *mode = IEEE80211_MODE_TURBO_A; 1152170530Ssam else 1153178354Ssam *mode = IEEE80211_MODE_STURBO_A; 1154178354Ssam } else if (*mode == IEEE80211_MODE_11G) 1155178354Ssam *mode = IEEE80211_MODE_TURBO_G; 1156138568Ssam else 1157178354Ssam return 0; 1158116742Ssam } 1159170530Ssam /* XXX HT40 +/- */ 1160178354Ssam return 1; 1161178354Ssam} 1162116742Ssam 1163178354Ssam/* 1164184273Ssam * Handle a media change request on the underlying interface. 1165178354Ssam */ 1166178354Ssamint 1167178354Ssamieee80211com_media_change(struct ifnet *ifp) 1168178354Ssam{ 1169184273Ssam return EINVAL; 1170178354Ssam} 1171116742Ssam 1172178354Ssam/* 1173178354Ssam * Handle a media change request on the vap interface. 1174178354Ssam */ 1175178354Ssamint 1176178354Ssamieee80211_media_change(struct ifnet *ifp) 1177178354Ssam{ 1178178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1179178354Ssam struct ifmedia_entry *ime = vap->iv_media.ifm_cur; 1180184273Ssam uint16_t newmode; 1181178354Ssam 1182184273Ssam if (!media2mode(ime, vap->iv_flags, &newmode)) 1183178354Ssam return EINVAL; 1184184273Ssam if (vap->iv_des_mode != newmode) { 1185184273Ssam vap->iv_des_mode = newmode; 1186178354Ssam return ENETRESET; 1187178354Ssam } 1188178354Ssam return 0; 1189116742Ssam} 1190116742Ssam 1191170530Ssam/* 1192170530Ssam * Common code to calculate the media status word 1193170530Ssam * from the operating mode and channel state. 1194170530Ssam */ 1195170530Ssamstatic int 1196170530Ssammedia_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) 1197170530Ssam{ 1198170530Ssam int status; 1199170530Ssam 1200170530Ssam status = IFM_IEEE80211; 1201170530Ssam switch (opmode) { 1202170530Ssam case IEEE80211_M_STA: 1203170530Ssam break; 1204170530Ssam case IEEE80211_M_IBSS: 1205170530Ssam status |= IFM_IEEE80211_ADHOC; 1206170530Ssam break; 1207170530Ssam case IEEE80211_M_HOSTAP: 1208170530Ssam status |= IFM_IEEE80211_HOSTAP; 1209170530Ssam break; 1210170530Ssam case IEEE80211_M_MONITOR: 1211170530Ssam status |= IFM_IEEE80211_MONITOR; 1212170530Ssam break; 1213170530Ssam case IEEE80211_M_AHDEMO: 1214170530Ssam status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; 1215170530Ssam break; 1216170530Ssam case IEEE80211_M_WDS: 1217178354Ssam status |= IFM_IEEE80211_WDS; 1218170530Ssam break; 1219170530Ssam } 1220170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) { 1221170530Ssam status |= IFM_IEEE80211_11NA; 1222170530Ssam } else if (IEEE80211_IS_CHAN_HTG(chan)) { 1223170530Ssam status |= IFM_IEEE80211_11NG; 1224170530Ssam } else if (IEEE80211_IS_CHAN_A(chan)) { 1225170530Ssam status |= IFM_IEEE80211_11A; 1226170530Ssam } else if (IEEE80211_IS_CHAN_B(chan)) { 1227170530Ssam status |= IFM_IEEE80211_11B; 1228170530Ssam } else if (IEEE80211_IS_CHAN_ANYG(chan)) { 1229170530Ssam status |= IFM_IEEE80211_11G; 1230170530Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) { 1231170530Ssam status |= IFM_IEEE80211_FH; 1232170530Ssam } 1233170530Ssam /* XXX else complain? */ 1234170530Ssam 1235170530Ssam if (IEEE80211_IS_CHAN_TURBO(chan)) 1236170530Ssam status |= IFM_IEEE80211_TURBO; 1237178354Ssam#if 0 1238178354Ssam if (IEEE80211_IS_CHAN_HT20(chan)) 1239178354Ssam status |= IFM_IEEE80211_HT20; 1240178354Ssam if (IEEE80211_IS_CHAN_HT40(chan)) 1241178354Ssam status |= IFM_IEEE80211_HT40; 1242178354Ssam#endif 1243170530Ssam return status; 1244170530Ssam} 1245170530Ssam 1246178354Ssamstatic void 1247178354Ssamieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1248178354Ssam{ 1249178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1250178354Ssam struct ieee80211vap *vap; 1251178354Ssam 1252178354Ssam imr->ifm_status = IFM_AVALID; 1253178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1254178354Ssam if (vap->iv_ifp->if_flags & IFF_UP) { 1255178354Ssam imr->ifm_status |= IFM_ACTIVE; 1256178354Ssam break; 1257178354Ssam } 1258178354Ssam imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); 1259178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1260178354Ssam imr->ifm_current = imr->ifm_active; 1261178354Ssam} 1262178354Ssam 1263116742Ssamvoid 1264116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1265116742Ssam{ 1266178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1267178354Ssam struct ieee80211com *ic = vap->iv_ic; 1268170530Ssam enum ieee80211_phymode mode; 1269116742Ssam 1270116742Ssam imr->ifm_status = IFM_AVALID; 1271170530Ssam /* 1272170530Ssam * NB: use the current channel's mode to lock down a xmit 1273170530Ssam * rate only when running; otherwise we may have a mismatch 1274170530Ssam * in which case the rate will not be convertible. 1275170530Ssam */ 1276178354Ssam if (vap->iv_state == IEEE80211_S_RUN) { 1277116742Ssam imr->ifm_status |= IFM_ACTIVE; 1278170530Ssam mode = ieee80211_chan2mode(ic->ic_curchan); 1279170530Ssam } else 1280170530Ssam mode = IEEE80211_MODE_AUTO; 1281178354Ssam imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); 1282138568Ssam /* 1283138568Ssam * Calculate a current rate if possible. 1284138568Ssam */ 1285178354Ssam if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { 1286138568Ssam /* 1287138568Ssam * A fixed rate is set, report that. 1288138568Ssam */ 1289138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1290178354Ssam vap->iv_txparms[mode].ucastrate, mode); 1291178354Ssam } else if (vap->iv_opmode == IEEE80211_M_STA) { 1292138568Ssam /* 1293138568Ssam * In station mode report the current transmit rate. 1294138568Ssam */ 1295138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1296178354Ssam vap->iv_bss->ni_txrate, mode); 1297128966Sandre } else 1298138568Ssam imr->ifm_active |= IFM_AUTO; 1299178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1300178354Ssam imr->ifm_current = imr->ifm_active; 1301116742Ssam} 1302116742Ssam 1303116742Ssam/* 1304116742Ssam * Set the current phy mode and recalculate the active channel 1305116742Ssam * set based on the available channels for this mode. Also 1306116742Ssam * select a new default/current channel if the current one is 1307116742Ssam * inappropriate for this mode. 1308116742Ssam */ 1309116742Ssamint 1310116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 1311116742Ssam{ 1312116742Ssam /* 1313166012Ssam * Adjust basic rates in 11b/11g supported rate set. 1314166012Ssam * Note that if operating on a hal/quarter rate channel 1315166012Ssam * this is a noop as those rates sets are different 1316166012Ssam * and used instead. 1317116742Ssam */ 1318166012Ssam if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 1319178354Ssam ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); 1320166012Ssam 1321116742Ssam ic->ic_curmode = mode; 1322138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 1323138568Ssam 1324116742Ssam return 0; 1325116742Ssam} 1326116742Ssam 1327116742Ssam/* 1328170530Ssam * Return the phy mode for with the specified channel. 1329116742Ssam */ 1330116742Ssamenum ieee80211_phymode 1331170530Ssamieee80211_chan2mode(const struct ieee80211_channel *chan) 1332116742Ssam{ 1333170530Ssam 1334170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) 1335170530Ssam return IEEE80211_MODE_11NA; 1336170530Ssam else if (IEEE80211_IS_CHAN_HTG(chan)) 1337170530Ssam return IEEE80211_MODE_11NG; 1338170530Ssam else if (IEEE80211_IS_CHAN_108G(chan)) 1339170530Ssam return IEEE80211_MODE_TURBO_G; 1340170530Ssam else if (IEEE80211_IS_CHAN_ST(chan)) 1341170530Ssam return IEEE80211_MODE_STURBO_A; 1342170530Ssam else if (IEEE80211_IS_CHAN_TURBO(chan)) 1343153350Ssam return IEEE80211_MODE_TURBO_A; 1344188782Ssam else if (IEEE80211_IS_CHAN_HALF(chan)) 1345188782Ssam return IEEE80211_MODE_HALF; 1346188782Ssam else if (IEEE80211_IS_CHAN_QUARTER(chan)) 1347188782Ssam return IEEE80211_MODE_QUARTER; 1348170530Ssam else if (IEEE80211_IS_CHAN_A(chan)) 1349116742Ssam return IEEE80211_MODE_11A; 1350170530Ssam else if (IEEE80211_IS_CHAN_ANYG(chan)) 1351116742Ssam return IEEE80211_MODE_11G; 1352170530Ssam else if (IEEE80211_IS_CHAN_B(chan)) 1353116742Ssam return IEEE80211_MODE_11B; 1354170530Ssam else if (IEEE80211_IS_CHAN_FHSS(chan)) 1355170530Ssam return IEEE80211_MODE_FH; 1356170530Ssam 1357170530Ssam /* NB: should not get here */ 1358170530Ssam printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", 1359170530Ssam __func__, chan->ic_freq, chan->ic_flags); 1360170530Ssam return IEEE80211_MODE_11B; 1361116742Ssam} 1362116742Ssam 1363170530Ssamstruct ratemedia { 1364170530Ssam u_int match; /* rate + mode */ 1365170530Ssam u_int media; /* if_media rate */ 1366170530Ssam}; 1367170530Ssam 1368170530Ssamstatic int 1369170530Ssamfindmedia(const struct ratemedia rates[], int n, u_int match) 1370170530Ssam{ 1371170530Ssam int i; 1372170530Ssam 1373170530Ssam for (i = 0; i < n; i++) 1374170530Ssam if (rates[i].match == match) 1375170530Ssam return rates[i].media; 1376170530Ssam return IFM_AUTO; 1377170530Ssam} 1378170530Ssam 1379116742Ssam/* 1380170530Ssam * Convert IEEE80211 rate value to ifmedia subtype. 1381170530Ssam * Rate is either a legacy rate in units of 0.5Mbps 1382170530Ssam * or an MCS index. 1383116742Ssam */ 1384116742Ssamint 1385116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1386116742Ssam{ 1387116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1388170530Ssam static const struct ratemedia rates[] = { 1389124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1390124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1391124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1392124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1393124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1394124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1395124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1396124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1397124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1398124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1399124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1400124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1401124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1402124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1403124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1404124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1405124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1406124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1407124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1408124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1409124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1410124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1411124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1412124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1413124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1414124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1415124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1416165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1417165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1418165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1419116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 1420116742Ssam }; 1421170530Ssam static const struct ratemedia htrates[] = { 1422170530Ssam { 0, IFM_IEEE80211_MCS }, 1423170530Ssam { 1, IFM_IEEE80211_MCS }, 1424170530Ssam { 2, IFM_IEEE80211_MCS }, 1425170530Ssam { 3, IFM_IEEE80211_MCS }, 1426170530Ssam { 4, IFM_IEEE80211_MCS }, 1427170530Ssam { 5, IFM_IEEE80211_MCS }, 1428170530Ssam { 6, IFM_IEEE80211_MCS }, 1429170530Ssam { 7, IFM_IEEE80211_MCS }, 1430170530Ssam { 8, IFM_IEEE80211_MCS }, 1431170530Ssam { 9, IFM_IEEE80211_MCS }, 1432170530Ssam { 10, IFM_IEEE80211_MCS }, 1433170530Ssam { 11, IFM_IEEE80211_MCS }, 1434170530Ssam { 12, IFM_IEEE80211_MCS }, 1435170530Ssam { 13, IFM_IEEE80211_MCS }, 1436170530Ssam { 14, IFM_IEEE80211_MCS }, 1437170530Ssam { 15, IFM_IEEE80211_MCS }, 1438170530Ssam }; 1439170530Ssam int m; 1440116742Ssam 1441170530Ssam /* 1442170530Ssam * Check 11n rates first for match as an MCS. 1443170530Ssam */ 1444170530Ssam if (mode == IEEE80211_MODE_11NA) { 1445172226Ssam if (rate & IEEE80211_RATE_MCS) { 1446172226Ssam rate &= ~IEEE80211_RATE_MCS; 1447170530Ssam m = findmedia(htrates, N(htrates), rate); 1448170530Ssam if (m != IFM_AUTO) 1449170530Ssam return m | IFM_IEEE80211_11NA; 1450170530Ssam } 1451170530Ssam } else if (mode == IEEE80211_MODE_11NG) { 1452170530Ssam /* NB: 12 is ambiguous, it will be treated as an MCS */ 1453172226Ssam if (rate & IEEE80211_RATE_MCS) { 1454172226Ssam rate &= ~IEEE80211_RATE_MCS; 1455170530Ssam m = findmedia(htrates, N(htrates), rate); 1456170530Ssam if (m != IFM_AUTO) 1457170530Ssam return m | IFM_IEEE80211_11NG; 1458170530Ssam } 1459170530Ssam } 1460170530Ssam rate &= IEEE80211_RATE_VAL; 1461116742Ssam switch (mode) { 1462116742Ssam case IEEE80211_MODE_11A: 1463188782Ssam case IEEE80211_MODE_HALF: /* XXX good 'nuf */ 1464188782Ssam case IEEE80211_MODE_QUARTER: 1465170530Ssam case IEEE80211_MODE_11NA: 1466138568Ssam case IEEE80211_MODE_TURBO_A: 1467170530Ssam case IEEE80211_MODE_STURBO_A: 1468170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); 1469116742Ssam case IEEE80211_MODE_11B: 1470170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); 1471124543Sonoe case IEEE80211_MODE_FH: 1472170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); 1473116742Ssam case IEEE80211_MODE_AUTO: 1474116742Ssam /* NB: ic may be NULL for some drivers */ 1475188775Ssam if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) 1476170530Ssam return findmedia(rates, N(rates), 1477170530Ssam rate | IFM_IEEE80211_FH); 1478116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1479116742Ssam /* fall thru... */ 1480116742Ssam case IEEE80211_MODE_11G: 1481170530Ssam case IEEE80211_MODE_11NG: 1482138568Ssam case IEEE80211_MODE_TURBO_G: 1483170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); 1484116742Ssam } 1485116742Ssam return IFM_AUTO; 1486116742Ssam#undef N 1487116742Ssam} 1488116742Ssam 1489116742Ssamint 1490116742Ssamieee80211_media2rate(int mword) 1491116742Ssam{ 1492116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1493116742Ssam static const int ieeerates[] = { 1494116742Ssam -1, /* IFM_AUTO */ 1495116742Ssam 0, /* IFM_MANUAL */ 1496116742Ssam 0, /* IFM_NONE */ 1497116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1498116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1499116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1500116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1501116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1502116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1503116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1504116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1505116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1506116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1507116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1508116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1509116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1510116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1511116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1512116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1513165569Ssam 0, /* IFM_IEEE80211_DS354k */ 1514165569Ssam 0, /* IFM_IEEE80211_DS512k */ 1515165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 1516165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 1517165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 1518170530Ssam -1, /* IFM_IEEE80211_MCS */ 1519116742Ssam }; 1520116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1521116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1522116742Ssam#undef N 1523116742Ssam} 1524