ieee80211.c revision 196004
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 196004 2009-07-31 19:12:19Z 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, 77195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 78195618Srpaulo [IEEE80211_M_MBSS] = IEEE80211_C_MBSS, 79195618Srpaulo#endif 80178957Ssam}; 81178957Ssam 82178354Ssamstatic const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = 83178354Ssam { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 84116742Ssam 85178354Ssamstatic void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); 86193655Ssamstatic void ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag); 87178354Ssamstatic void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); 88178354Ssamstatic int ieee80211_media_setup(struct ieee80211com *ic, 89178354Ssam struct ifmedia *media, int caps, int addsta, 90178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); 91178354Ssamstatic void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); 92178354Ssamstatic int ieee80211com_media_change(struct ifnet *); 93178354Ssamstatic int media_status(enum ieee80211_opmode, 94178354Ssam const struct ieee80211_channel *); 95178354Ssam 96178354SsamMALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); 97178354Ssam 98164645Ssam/* 99164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units). 100164645Ssam */ 101164645Ssam#define B(r) ((r) | IEEE80211_RATE_BASIC) 102164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a = 103164645Ssam { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; 104165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half = 105165569Ssam { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; 106165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter = 107165569Ssam { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; 108164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b = 109164645Ssam { 4, { B(2), B(4), B(11), B(22) } }; 110164645Ssam/* NB: OFDM rates are handled specially based on mode */ 111164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g = 112164645Ssam { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; 113164645Ssam#undef B 114164645Ssam 115140915Ssam/* 116165569Ssam * Fill in 802.11 available channel set, mark 117165569Ssam * all available channels as active, and pick 118165569Ssam * a default channel if not already specified. 119165569Ssam */ 120165569Ssamstatic void 121165569Ssamieee80211_chan_init(struct ieee80211com *ic) 122116742Ssam{ 123165569Ssam#define DEFAULTRATES(m, def) do { \ 124188782Ssam if (ic->ic_sup_rates[m].rs_nrates == 0) \ 125165574Ssam ic->ic_sup_rates[m] = def; \ 126165569Ssam} while (0) 127116742Ssam struct ieee80211_channel *c; 128116742Ssam int i; 129116742Ssam 130186107Ssam KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, 131170530Ssam ("invalid number of channels specified: %u", ic->ic_nchans)); 132116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 133178354Ssam memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); 134167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); 135170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 136116742Ssam c = &ic->ic_channels[i]; 137170530Ssam KASSERT(c->ic_flags != 0, ("channel with no flags")); 138187796Ssam /* 139187796Ssam * Help drivers that work only with frequencies by filling 140187796Ssam * in IEEE channel #'s if not already calculated. Note this 141187796Ssam * mimics similar work done in ieee80211_setregdomain when 142187796Ssam * changing regulatory state. 143187796Ssam */ 144187796Ssam if (c->ic_ieee == 0) 145187796Ssam c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); 146187796Ssam if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) 147187796Ssam c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + 148187796Ssam (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), 149187796Ssam c->ic_flags); 150187796Ssam /* default max tx power to max regulatory */ 151187796Ssam if (c->ic_maxpower == 0) 152187796Ssam c->ic_maxpower = 2*c->ic_maxregpower; 153170530Ssam setbit(ic->ic_chan_avail, c->ic_ieee); 154170530Ssam /* 155170530Ssam * Identify mode capabilities. 156170530Ssam */ 157170530Ssam if (IEEE80211_IS_CHAN_A(c)) 158170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11A); 159170530Ssam if (IEEE80211_IS_CHAN_B(c)) 160170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11B); 161170530Ssam if (IEEE80211_IS_CHAN_ANYG(c)) 162170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11G); 163170530Ssam if (IEEE80211_IS_CHAN_FHSS(c)) 164170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_FH); 165170530Ssam if (IEEE80211_IS_CHAN_108A(c)) 166170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); 167170530Ssam if (IEEE80211_IS_CHAN_108G(c)) 168170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); 169170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 170170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); 171188782Ssam if (IEEE80211_IS_CHAN_HALF(c)) 172188782Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_HALF); 173188782Ssam if (IEEE80211_IS_CHAN_QUARTER(c)) 174188782Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_QUARTER); 175170530Ssam if (IEEE80211_IS_CHAN_HTA(c)) 176170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); 177170530Ssam if (IEEE80211_IS_CHAN_HTG(c)) 178170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); 179116742Ssam } 180170530Ssam /* initialize candidate channels to all available */ 181170530Ssam memcpy(ic->ic_chan_active, ic->ic_chan_avail, 182170530Ssam sizeof(ic->ic_chan_avail)); 183164645Ssam 184178354Ssam /* sort channel table to allow lookup optimizations */ 185178354Ssam ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); 186178354Ssam 187178354Ssam /* invalidate any previous state */ 188170530Ssam ic->ic_bsschan = IEEE80211_CHAN_ANYC; 189172233Ssam ic->ic_prevchan = NULL; 190178354Ssam ic->ic_csa_newchan = NULL; 191170530Ssam /* arbitrarily pick the first channel */ 192170530Ssam ic->ic_curchan = &ic->ic_channels[0]; 193190532Ssam ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); 194170530Ssam 195164645Ssam /* fillin well-known rate sets if driver has not specified */ 196165569Ssam DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); 197165569Ssam DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); 198165569Ssam DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); 199165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); 200165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); 201187897Ssam DEFAULTRATES(IEEE80211_MODE_STURBO_A, ieee80211_rateset_11a); 202188782Ssam DEFAULTRATES(IEEE80211_MODE_HALF, ieee80211_rateset_half); 203188782Ssam DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter); 204188774Ssam DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); 205188774Ssam DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); 206165569Ssam 207165569Ssam /* 208165569Ssam * Set auto mode to reset active channel state and any desired channel. 209165569Ssam */ 210165569Ssam (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); 211165569Ssam#undef DEFAULTRATES 212165569Ssam} 213165569Ssam 214178354Ssamstatic void 215178354Ssamnull_update_mcast(struct ifnet *ifp) 216178354Ssam{ 217178354Ssam if_printf(ifp, "need multicast update callback\n"); 218178354Ssam} 219178354Ssam 220178354Ssamstatic void 221178354Ssamnull_update_promisc(struct ifnet *ifp) 222178354Ssam{ 223178354Ssam if_printf(ifp, "need promiscuous mode update callback\n"); 224178354Ssam} 225178354Ssam 226178521Ssamstatic int 227195846Ssamnull_transmit(struct ifnet *ifp, struct mbuf *m) 228195846Ssam{ 229195846Ssam m_freem(m); 230195846Ssam ifp->if_oerrors++; 231195846Ssam return EACCES; /* XXX EIO/EPERM? */ 232195846Ssam} 233195846Ssam 234195846Ssamstatic int 235178521Ssamnull_output(struct ifnet *ifp, struct mbuf *m, 236191148Skmacy struct sockaddr *dst, struct route *ro) 237178521Ssam{ 238178521Ssam if_printf(ifp, "discard raw packet\n"); 239195846Ssam return null_transmit(ifp, m); 240178521Ssam} 241178521Ssam 242178521Ssamstatic void 243178521Ssamnull_input(struct ifnet *ifp, struct mbuf *m) 244178521Ssam{ 245178521Ssam if_printf(ifp, "if_input should not be called\n"); 246178521Ssam m_freem(m); 247178521Ssam} 248178521Ssam 249178354Ssam/* 250178354Ssam * Attach/setup the common net80211 state. Called by 251178354Ssam * the driver on attach to prior to creating any vap's. 252178354Ssam */ 253165569Ssamvoid 254190526Ssamieee80211_ifattach(struct ieee80211com *ic, 255190526Ssam const uint8_t macaddr[IEEE80211_ADDR_LEN]) 256165569Ssam{ 257165569Ssam struct ifnet *ifp = ic->ic_ifp; 258178354Ssam struct sockaddr_dl *sdl; 259178354Ssam struct ifaddr *ifa; 260165569Ssam 261178354Ssam KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type)); 262165569Ssam 263179388Ssam IEEE80211_LOCK_INIT(ic, ifp->if_xname); 264178354Ssam TAILQ_INIT(&ic->ic_vaps); 265191746Sthompsa 266191746Sthompsa /* Create a taskqueue for all state changes */ 267191746Sthompsa ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO, 268191746Sthompsa taskqueue_thread_enqueue, &ic->ic_tq); 269191746Sthompsa taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s taskq", 270191746Sthompsa ifp->if_xname); 271165569Ssam /* 272165569Ssam * Fill in 802.11 available channel set, mark all 273165569Ssam * available channels as active, and pick a default 274165569Ssam * channel if not already specified. 275165569Ssam */ 276178354Ssam ieee80211_media_init(ic); 277170530Ssam 278178354Ssam ic->ic_update_mcast = null_update_mcast; 279178354Ssam ic->ic_update_promisc = null_update_promisc; 280116742Ssam 281195379Ssam ic->ic_hash_key = arc4random(); 282155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 283155688Ssam ic->ic_lintval = ic->ic_bintval; 284138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 285138568Ssam 286170530Ssam ieee80211_crypto_attach(ic); 287138568Ssam ieee80211_node_attach(ic); 288170530Ssam ieee80211_power_attach(ic); 289138568Ssam ieee80211_proto_attach(ic); 290190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 291190391Ssam ieee80211_superg_attach(ic); 292190391Ssam#endif 293170530Ssam ieee80211_ht_attach(ic); 294170530Ssam ieee80211_scan_attach(ic); 295178354Ssam ieee80211_regdomain_attach(ic); 296193843Ssam ieee80211_dfs_attach(ic); 297138568Ssam 298178354Ssam ieee80211_sysctl_attach(ic); 299138568Ssam 300178354Ssam ifp->if_addrlen = IEEE80211_ADDR_LEN; 301178354Ssam ifp->if_hdrlen = 0; 302178354Ssam if_attach(ifp); 303178354Ssam ifp->if_mtu = IEEE80211_MTU_MAX; 304178354Ssam ifp->if_broadcastaddr = ieee80211broadcastaddr; 305178521Ssam ifp->if_output = null_output; 306178521Ssam ifp->if_input = null_input; /* just in case */ 307178521Ssam ifp->if_resolvemulti = NULL; /* NB: callers check */ 308140915Ssam 309178354Ssam ifa = ifaddr_byindex(ifp->if_index); 310178354Ssam KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); 311178354Ssam sdl = (struct sockaddr_dl *)ifa->ifa_addr; 312178354Ssam sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ 313178354Ssam sdl->sdl_alen = IEEE80211_ADDR_LEN; 314190526Ssam IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr); 315194760Srwatson ifa_free(ifa); 316116742Ssam} 317116742Ssam 318178354Ssam/* 319178354Ssam * Detach net80211 state on device detach. Tear down 320178354Ssam * all vap's and reclaim all common state prior to the 321178354Ssam * device state going away. Note we may call back into 322178354Ssam * driver; it must be prepared for this. 323178354Ssam */ 324116742Ssamvoid 325138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 326116742Ssam{ 327138568Ssam struct ifnet *ifp = ic->ic_ifp; 328178354Ssam struct ieee80211vap *vap; 329116742Ssam 330193337Ssam if_detach(ifp); 331193337Ssam 332178354Ssam while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) 333178354Ssam ieee80211_vap_destroy(vap); 334188533Sthompsa ieee80211_waitfor_parent(ic); 335138568Ssam 336138568Ssam ieee80211_sysctl_detach(ic); 337193843Ssam ieee80211_dfs_detach(ic); 338178354Ssam ieee80211_regdomain_detach(ic); 339170530Ssam ieee80211_scan_detach(ic); 340190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 341190391Ssam ieee80211_superg_detach(ic); 342190391Ssam#endif 343170530Ssam ieee80211_ht_detach(ic); 344166012Ssam /* NB: must be called before ieee80211_node_detach */ 345138568Ssam ieee80211_proto_detach(ic); 346138568Ssam ieee80211_crypto_detach(ic); 347170530Ssam ieee80211_power_detach(ic); 348138568Ssam ieee80211_node_detach(ic); 349193337Ssam 350116742Ssam ifmedia_removeall(&ic->ic_media); 351191746Sthompsa taskqueue_free(ic->ic_tq); 352170530Ssam IEEE80211_LOCK_DESTROY(ic); 353178354Ssam} 354138568Ssam 355178354Ssam/* 356178354Ssam * Default reset method for use with the ioctl support. This 357178354Ssam * method is invoked after any state change in the 802.11 358178354Ssam * layer that should be propagated to the hardware but not 359178354Ssam * require re-initialization of the 802.11 state machine (e.g 360178354Ssam * rescanning for an ap). We always return ENETRESET which 361178354Ssam * should cause the driver to re-initialize the device. Drivers 362178354Ssam * can override this method to implement more optimized support. 363178354Ssam */ 364178354Ssamstatic int 365178354Ssamdefault_reset(struct ieee80211vap *vap, u_long cmd) 366178354Ssam{ 367178354Ssam return ENETRESET; 368178354Ssam} 369178354Ssam 370178354Ssam/* 371178354Ssam * Prepare a vap for use. Drivers use this call to 372178354Ssam * setup net80211 state in new vap's prior attaching 373178354Ssam * them with ieee80211_vap_attach (below). 374178354Ssam */ 375178354Ssamint 376178354Ssamieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, 377178354Ssam const char name[IFNAMSIZ], int unit, int opmode, int flags, 378178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 379178354Ssam const uint8_t macaddr[IEEE80211_ADDR_LEN]) 380178354Ssam{ 381178354Ssam struct ifnet *ifp; 382178354Ssam 383178354Ssam ifp = if_alloc(IFT_ETHER); 384178354Ssam if (ifp == NULL) { 385178354Ssam if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n", 386178354Ssam __func__); 387178354Ssam return ENOMEM; 388178354Ssam } 389178354Ssam if_initname(ifp, name, unit); 390178354Ssam ifp->if_softc = vap; /* back pointer */ 391178354Ssam ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 392178354Ssam ifp->if_start = ieee80211_start; 393178354Ssam ifp->if_ioctl = ieee80211_ioctl; 394178354Ssam ifp->if_watchdog = NULL; /* NB: no watchdog routine */ 395178354Ssam ifp->if_init = ieee80211_init; 396178354Ssam /* NB: input+output filled in by ether_ifattach */ 397178354Ssam IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 398178354Ssam ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 399178354Ssam IFQ_SET_READY(&ifp->if_snd); 400178354Ssam 401178354Ssam vap->iv_ifp = ifp; 402178354Ssam vap->iv_ic = ic; 403178354Ssam vap->iv_flags = ic->ic_flags; /* propagate common flags */ 404178354Ssam vap->iv_flags_ext = ic->ic_flags_ext; 405178354Ssam vap->iv_flags_ven = ic->ic_flags_ven; 406178354Ssam vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; 407178354Ssam vap->iv_htcaps = ic->ic_htcaps; 408178354Ssam vap->iv_opmode = opmode; 409178957Ssam vap->iv_caps |= ieee80211_opcap[opmode]; 410178354Ssam switch (opmode) { 411178354Ssam case IEEE80211_M_WDS: 412178354Ssam /* 413178354Ssam * WDS links must specify the bssid of the far end. 414178354Ssam * For legacy operation this is a static relationship. 415178354Ssam * For non-legacy operation the station must associate 416178354Ssam * and be authorized to pass traffic. Plumbing the 417178354Ssam * vap to the proper node happens when the vap 418178354Ssam * transitions to RUN state. 419178354Ssam */ 420178354Ssam IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); 421178354Ssam vap->iv_flags |= IEEE80211_F_DESBSSID; 422178354Ssam if (flags & IEEE80211_CLONE_WDSLEGACY) 423178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; 424178354Ssam break; 425186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 426186904Ssam case IEEE80211_M_AHDEMO: 427186904Ssam if (flags & IEEE80211_CLONE_TDMA) { 428186904Ssam /* NB: checked before clone operation allowed */ 429186904Ssam KASSERT(ic->ic_caps & IEEE80211_C_TDMA, 430186904Ssam ("not TDMA capable, ic_caps 0x%x", ic->ic_caps)); 431186904Ssam /* 432186904Ssam * Propagate TDMA capability to mark vap; this 433186904Ssam * cannot be removed and is used to distinguish 434186904Ssam * regular ahdemo operation from ahdemo+tdma. 435186904Ssam */ 436186904Ssam vap->iv_caps |= IEEE80211_C_TDMA; 437186904Ssam } 438186904Ssam break; 439186904Ssam#endif 440178354Ssam } 441184278Ssam /* auto-enable s/w beacon miss support */ 442184278Ssam if (flags & IEEE80211_CLONE_NOBEACONS) 443184278Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; 444178354Ssam /* 445178354Ssam * Enable various functionality by default if we're 446178354Ssam * capable; the driver can override us if it knows better. 447178354Ssam */ 448178354Ssam if (vap->iv_caps & IEEE80211_C_WME) 449178354Ssam vap->iv_flags |= IEEE80211_F_WME; 450178354Ssam if (vap->iv_caps & IEEE80211_C_BURST) 451178354Ssam vap->iv_flags |= IEEE80211_F_BURST; 452178354Ssam /* NB: bg scanning only makes sense for station mode right now */ 453178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 454178354Ssam (vap->iv_caps & IEEE80211_C_BGSCAN)) 455178354Ssam vap->iv_flags |= IEEE80211_F_BGSCAN; 456178957Ssam vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ 457178954Ssam /* NB: DFS support only makes sense for ap mode right now */ 458178954Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 459178954Ssam (vap->iv_caps & IEEE80211_C_DFS)) 460178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_DFS; 461178354Ssam 462178354Ssam vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 463178354Ssam vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 464178354Ssam vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; 465178354Ssam /* 466178354Ssam * Install a default reset method for the ioctl support; 467178354Ssam * the driver can override this. 468178354Ssam */ 469178354Ssam vap->iv_reset = default_reset; 470178354Ssam 471178354Ssam IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); 472178354Ssam 473178354Ssam ieee80211_sysctl_vattach(vap); 474178354Ssam ieee80211_crypto_vattach(vap); 475178354Ssam ieee80211_node_vattach(vap); 476178354Ssam ieee80211_power_vattach(vap); 477178354Ssam ieee80211_proto_vattach(vap); 478190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 479190391Ssam ieee80211_superg_vattach(vap); 480190391Ssam#endif 481178354Ssam ieee80211_ht_vattach(vap); 482178354Ssam ieee80211_scan_vattach(vap); 483178354Ssam ieee80211_regdomain_vattach(vap); 484192468Ssam ieee80211_radiotap_vattach(vap); 485178354Ssam 486178354Ssam return 0; 487178354Ssam} 488178354Ssam 489178354Ssam/* 490178354Ssam * Activate a vap. State should have been prepared with a 491178354Ssam * call to ieee80211_vap_setup and by the driver. On return 492178354Ssam * from this call the vap is ready for use. 493178354Ssam */ 494178354Ssamint 495178354Ssamieee80211_vap_attach(struct ieee80211vap *vap, 496178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 497178354Ssam{ 498178354Ssam struct ifnet *ifp = vap->iv_ifp; 499178354Ssam struct ieee80211com *ic = vap->iv_ic; 500178354Ssam struct ifmediareq imr; 501178354Ssam int maxrate; 502178354Ssam 503178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 504178354Ssam "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", 505178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 506178354Ssam ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); 507178354Ssam 508178354Ssam /* 509178354Ssam * Do late attach work that cannot happen until after 510178354Ssam * the driver has had a chance to override defaults. 511178354Ssam */ 512178354Ssam ieee80211_node_latevattach(vap); 513178354Ssam ieee80211_power_latevattach(vap); 514178354Ssam 515178354Ssam maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, 516178354Ssam vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); 517178354Ssam ieee80211_media_status(ifp, &imr); 518178354Ssam /* NB: strip explicit mode; we're actually in autoselect */ 519188106Ssam ifmedia_set(&vap->iv_media, 520188106Ssam imr.ifm_active &~ (IFM_MMASK | IFM_IEEE80211_TURBO)); 521178354Ssam if (maxrate) 522178354Ssam ifp->if_baudrate = IF_Mbps(maxrate); 523178354Ssam 524178354Ssam ether_ifattach(ifp, vap->iv_myaddr); 525195846Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR) { 526195846Ssam /* NB: disallow transmit */ 527195846Ssam ifp->if_transmit = null_transmit; 528195846Ssam ifp->if_output = null_output; 529195846Ssam } else { 530195846Ssam /* hook output method setup by ether_ifattach */ 531195846Ssam vap->iv_output = ifp->if_output; 532195846Ssam ifp->if_output = ieee80211_output; 533195846Ssam } 534178354Ssam /* NB: if_mtu set by ether_ifattach to ETHERMTU */ 535178354Ssam 536178354Ssam IEEE80211_LOCK(ic); 537178354Ssam TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); 538178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 539190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 540178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 541190391Ssam#endif 542178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 543178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 544193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); 545193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); 546178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 547178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 548178354Ssam IEEE80211_UNLOCK(ic); 549178354Ssam 550178354Ssam return 1; 551178354Ssam} 552178354Ssam 553178354Ssam/* 554178354Ssam * Tear down vap state and reclaim the ifnet. 555178354Ssam * The driver is assumed to have prepared for 556178354Ssam * this; e.g. by turning off interrupts for the 557178354Ssam * underlying device. 558178354Ssam */ 559178354Ssamvoid 560178354Ssamieee80211_vap_detach(struct ieee80211vap *vap) 561178354Ssam{ 562178354Ssam struct ieee80211com *ic = vap->iv_ic; 563178354Ssam struct ifnet *ifp = vap->iv_ifp; 564178354Ssam 565178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", 566178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 567178354Ssam ic->ic_ifp->if_xname); 568178354Ssam 569193312Ssam /* NB: bpfdetach is called by ether_ifdetach and claims all taps */ 570193312Ssam ether_ifdetach(ifp); 571178354Ssam 572193312Ssam ieee80211_stop(vap); 573193312Ssam 574191746Sthompsa /* 575191746Sthompsa * Flush any deferred vap tasks. 576191746Sthompsa * NB: must be before ether_ifdetach() and removal from ic_vaps list 577191746Sthompsa */ 578191746Sthompsa ieee80211_draintask(ic, &vap->iv_nstate_task); 579191746Sthompsa ieee80211_draintask(ic, &vap->iv_swbmiss_task); 580191746Sthompsa 581191746Sthompsa IEEE80211_LOCK(ic); 582191746Sthompsa KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running")); 583178354Ssam TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); 584178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 585190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 586178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 587190391Ssam#endif 588178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 589178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 590193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); 591193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); 592192468Ssam /* NB: this handles the bpfdetach done below */ 593192468Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); 594178354Ssam ieee80211_syncifflag_locked(ic, IFF_PROMISC); 595178354Ssam ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); 596178354Ssam IEEE80211_UNLOCK(ic); 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 && 640195847Ssam !(vap->iv_opmode == IEEE80211_M_MONITOR || 641196004Ssam (vap->iv_opmode == IEEE80211_M_AHDEMO && 642196004Ssam (vap->iv_caps & IEEE80211_C_TDMA) == 0))) 643178354Ssam continue; 644178354Ssam bit = 1; 645178354Ssam break; 646178354Ssam } 647178354Ssam oflags = ifp->if_flags; 648178354Ssam if (bit) 649178354Ssam ifp->if_flags |= flag; 650178354Ssam else 651178354Ssam ifp->if_flags &= ~flag; 652178354Ssam if ((ifp->if_flags ^ oflags) & flag) { 653178354Ssam /* XXX should we return 1/0 and let caller do this? */ 654178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 655178354Ssam if (flag == IFF_PROMISC) 656191746Sthompsa ieee80211_runtask(ic, &ic->ic_promisc_task); 657178354Ssam else if (flag == IFF_ALLMULTI) 658191746Sthompsa ieee80211_runtask(ic, &ic->ic_mcast_task); 659178354Ssam } 660178354Ssam } 661178354Ssam} 662178354Ssam 663178354Ssam/* 664178354Ssam * Synchronize flag bit state in the com structure 665178354Ssam * according to the state of all vap's. This is used, 666178354Ssam * for example, to handle state changes via ioctls. 667178354Ssam */ 668178354Ssamstatic void 669178354Ssamieee80211_syncflag_locked(struct ieee80211com *ic, int flag) 670178354Ssam{ 671178354Ssam struct ieee80211vap *vap; 672178354Ssam int bit; 673178354Ssam 674178354Ssam IEEE80211_LOCK_ASSERT(ic); 675178354Ssam 676178354Ssam bit = 0; 677178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 678178354Ssam if (vap->iv_flags & flag) { 679178354Ssam bit = 1; 680178354Ssam break; 681178354Ssam } 682178354Ssam if (bit) 683178354Ssam ic->ic_flags |= flag; 684178354Ssam else 685178354Ssam ic->ic_flags &= ~flag; 686178354Ssam} 687178354Ssam 688178354Ssamvoid 689178354Ssamieee80211_syncflag(struct ieee80211vap *vap, int flag) 690178354Ssam{ 691178354Ssam struct ieee80211com *ic = vap->iv_ic; 692178354Ssam 693178354Ssam IEEE80211_LOCK(ic); 694178354Ssam if (flag < 0) { 695178354Ssam flag = -flag; 696178354Ssam vap->iv_flags &= ~flag; 697178354Ssam } else 698178354Ssam vap->iv_flags |= flag; 699178354Ssam ieee80211_syncflag_locked(ic, flag); 700178354Ssam IEEE80211_UNLOCK(ic); 701178354Ssam} 702178354Ssam 703178354Ssam/* 704193655Ssam * Synchronize flags_ht bit state in the com structure 705178354Ssam * according to the state of all vap's. This is used, 706178354Ssam * for example, to handle state changes via ioctls. 707178354Ssam */ 708178354Ssamstatic void 709193655Ssamieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag) 710193655Ssam{ 711193655Ssam struct ieee80211vap *vap; 712193655Ssam int bit; 713193655Ssam 714193655Ssam IEEE80211_LOCK_ASSERT(ic); 715193655Ssam 716193655Ssam bit = 0; 717193655Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 718193655Ssam if (vap->iv_flags_ht & flag) { 719193655Ssam bit = 1; 720193655Ssam break; 721193655Ssam } 722193655Ssam if (bit) 723193655Ssam ic->ic_flags_ht |= flag; 724193655Ssam else 725193655Ssam ic->ic_flags_ht &= ~flag; 726193655Ssam} 727193655Ssam 728193655Ssamvoid 729193655Ssamieee80211_syncflag_ht(struct ieee80211vap *vap, int flag) 730193655Ssam{ 731193655Ssam struct ieee80211com *ic = vap->iv_ic; 732193655Ssam 733193655Ssam IEEE80211_LOCK(ic); 734193655Ssam if (flag < 0) { 735193655Ssam flag = -flag; 736193655Ssam vap->iv_flags_ht &= ~flag; 737193655Ssam } else 738193655Ssam vap->iv_flags_ht |= flag; 739193655Ssam ieee80211_syncflag_ht_locked(ic, flag); 740193655Ssam IEEE80211_UNLOCK(ic); 741193655Ssam} 742193655Ssam 743193655Ssam/* 744193655Ssam * Synchronize flags_ext bit state in the com structure 745193655Ssam * according to the state of all vap's. This is used, 746193655Ssam * for example, to handle state changes via ioctls. 747193655Ssam */ 748193655Ssamstatic void 749178354Ssamieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) 750178354Ssam{ 751178354Ssam struct ieee80211vap *vap; 752178354Ssam int bit; 753178354Ssam 754178354Ssam IEEE80211_LOCK_ASSERT(ic); 755178354Ssam 756178354Ssam bit = 0; 757178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 758178354Ssam if (vap->iv_flags_ext & flag) { 759178354Ssam bit = 1; 760178354Ssam break; 761178354Ssam } 762178354Ssam if (bit) 763178354Ssam ic->ic_flags_ext |= flag; 764178354Ssam else 765178354Ssam ic->ic_flags_ext &= ~flag; 766178354Ssam} 767178354Ssam 768178354Ssamvoid 769178354Ssamieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) 770178354Ssam{ 771178354Ssam struct ieee80211com *ic = vap->iv_ic; 772178354Ssam 773178354Ssam IEEE80211_LOCK(ic); 774178354Ssam if (flag < 0) { 775178354Ssam flag = -flag; 776178354Ssam vap->iv_flags_ext &= ~flag; 777178354Ssam } else 778178354Ssam vap->iv_flags_ext |= flag; 779178354Ssam ieee80211_syncflag_ext_locked(ic, flag); 780178354Ssam IEEE80211_UNLOCK(ic); 781178354Ssam} 782178354Ssam 783166012Ssamstatic __inline int 784166012Ssammapgsm(u_int freq, u_int flags) 785166012Ssam{ 786166012Ssam freq *= 10; 787166012Ssam if (flags & IEEE80211_CHAN_QUARTER) 788166012Ssam freq += 5; 789166012Ssam else if (flags & IEEE80211_CHAN_HALF) 790166012Ssam freq += 10; 791166012Ssam else 792166012Ssam freq += 20; 793166012Ssam /* NB: there is no 907/20 wide but leave room */ 794166012Ssam return (freq - 906*10) / 5; 795166012Ssam} 796166012Ssam 797166012Ssamstatic __inline int 798166012Ssammappsb(u_int freq, u_int flags) 799166012Ssam{ 800166012Ssam return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 801166012Ssam} 802166012Ssam 803116742Ssam/* 804116742Ssam * Convert MHz frequency to IEEE channel number. 805116742Ssam */ 806152450Ssamint 807116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 808116742Ssam{ 809167430Ssam#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) 810166012Ssam if (flags & IEEE80211_CHAN_GSM) 811166012Ssam return mapgsm(freq, flags); 812116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 813116742Ssam if (freq == 2484) 814116742Ssam return 14; 815116742Ssam if (freq < 2484) 816152450Ssam return ((int) freq - 2407) / 5; 817116742Ssam else 818116742Ssam return 15 + ((freq - 2512) / 20); 819116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 820165569Ssam if (freq <= 5000) { 821170530Ssam /* XXX check regdomain? */ 822167430Ssam if (IS_FREQ_IN_PSB(freq)) 823166012Ssam return mappsb(freq, flags); 824152450Ssam return (freq - 4000) / 5; 825165569Ssam } else 826152450Ssam return (freq - 5000) / 5; 827116742Ssam } else { /* either, guess */ 828116742Ssam if (freq == 2484) 829116742Ssam return 14; 830166012Ssam if (freq < 2484) { 831166012Ssam if (907 <= freq && freq <= 922) 832166012Ssam return mapgsm(freq, flags); 833152450Ssam return ((int) freq - 2407) / 5; 834166012Ssam } 835152450Ssam if (freq < 5000) { 836167430Ssam if (IS_FREQ_IN_PSB(freq)) 837166012Ssam return mappsb(freq, flags); 838165569Ssam else if (freq > 4900) 839152450Ssam return (freq - 4000) / 5; 840152450Ssam else 841152450Ssam return 15 + ((freq - 2512) / 20); 842152450Ssam } 843116742Ssam return (freq - 5000) / 5; 844116742Ssam } 845167430Ssam#undef IS_FREQ_IN_PSB 846116742Ssam} 847116742Ssam 848116742Ssam/* 849116742Ssam * Convert channel to IEEE channel number. 850116742Ssam */ 851152450Ssamint 852165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) 853116742Ssam{ 854170530Ssam if (c == NULL) { 855138568Ssam if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); 856117039Ssam return 0; /* XXX */ 857116742Ssam } 858170530Ssam return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); 859116742Ssam} 860116742Ssam 861116742Ssam/* 862116742Ssam * Convert IEEE channel number to MHz frequency. 863116742Ssam */ 864116742Ssamu_int 865116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 866116742Ssam{ 867166012Ssam if (flags & IEEE80211_CHAN_GSM) 868166012Ssam return 907 + 5 * (chan / 10); 869116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 870116742Ssam if (chan == 14) 871116742Ssam return 2484; 872116742Ssam if (chan < 14) 873116742Ssam return 2407 + chan*5; 874116742Ssam else 875116742Ssam return 2512 + ((chan-15)*20); 876116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 877165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 878165569Ssam chan -= 37; 879165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 880165569Ssam } 881116742Ssam return 5000 + (chan*5); 882116742Ssam } else { /* either, guess */ 883166012Ssam /* XXX can't distinguish PSB+GSM channels */ 884116742Ssam if (chan == 14) 885116742Ssam return 2484; 886116742Ssam if (chan < 14) /* 0-13 */ 887116742Ssam return 2407 + chan*5; 888116742Ssam if (chan < 27) /* 15-26 */ 889116742Ssam return 2512 + ((chan-15)*20); 890116742Ssam return 5000 + (chan*5); 891116742Ssam } 892116742Ssam} 893116742Ssam 894116742Ssam/* 895170530Ssam * Locate a channel given a frequency+flags. We cache 896178354Ssam * the previous lookup to optimize switching between two 897170530Ssam * channels--as happens with dynamic turbo. 898170530Ssam */ 899170530Ssamstruct ieee80211_channel * 900170530Ssamieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) 901170530Ssam{ 902170530Ssam struct ieee80211_channel *c; 903170530Ssam int i; 904170530Ssam 905170530Ssam flags &= IEEE80211_CHAN_ALLTURBO; 906170530Ssam c = ic->ic_prevchan; 907170530Ssam if (c != NULL && c->ic_freq == freq && 908170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 909170530Ssam return c; 910170530Ssam /* brute force search */ 911170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 912170530Ssam c = &ic->ic_channels[i]; 913170530Ssam if (c->ic_freq == freq && 914170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 915170530Ssam return c; 916170530Ssam } 917170530Ssam return NULL; 918170530Ssam} 919170530Ssam 920173861Ssam/* 921173861Ssam * Locate a channel given a channel number+flags. We cache 922173861Ssam * the previous lookup to optimize switching between two 923173861Ssam * channels--as happens with dynamic turbo. 924173861Ssam */ 925173861Ssamstruct ieee80211_channel * 926173861Ssamieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) 927173861Ssam{ 928173861Ssam struct ieee80211_channel *c; 929173861Ssam int i; 930173861Ssam 931173861Ssam flags &= IEEE80211_CHAN_ALLTURBO; 932173861Ssam c = ic->ic_prevchan; 933173861Ssam if (c != NULL && c->ic_ieee == ieee && 934173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 935173861Ssam return c; 936173861Ssam /* brute force search */ 937173861Ssam for (i = 0; i < ic->ic_nchans; i++) { 938173861Ssam c = &ic->ic_channels[i]; 939173861Ssam if (c->ic_ieee == ieee && 940173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 941173861Ssam return c; 942173861Ssam } 943173861Ssam return NULL; 944173861Ssam} 945173861Ssam 946170530Ssamstatic void 947178354Ssamaddmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) 948170530Ssam{ 949170530Ssam#define ADD(_ic, _s, _o) \ 950178354Ssam ifmedia_add(media, \ 951170530Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 952170530Ssam static const u_int mopts[IEEE80211_MODE_MAX] = { 953188106Ssam [IEEE80211_MODE_AUTO] = IFM_AUTO, 954188106Ssam [IEEE80211_MODE_11A] = IFM_IEEE80211_11A, 955188106Ssam [IEEE80211_MODE_11B] = IFM_IEEE80211_11B, 956188106Ssam [IEEE80211_MODE_11G] = IFM_IEEE80211_11G, 957188106Ssam [IEEE80211_MODE_FH] = IFM_IEEE80211_FH, 958188106Ssam [IEEE80211_MODE_TURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 959188106Ssam [IEEE80211_MODE_TURBO_G] = IFM_IEEE80211_11G|IFM_IEEE80211_TURBO, 960188106Ssam [IEEE80211_MODE_STURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 961188782Ssam [IEEE80211_MODE_HALF] = IFM_IEEE80211_11A, /* XXX */ 962188782Ssam [IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */ 963188106Ssam [IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA, 964188106Ssam [IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG, 965170530Ssam }; 966170530Ssam u_int mopt; 967170530Ssam 968170530Ssam mopt = mopts[mode]; 969178354Ssam if (addsta) 970178354Ssam ADD(ic, mword, mopt); /* STA mode has no cap */ 971178354Ssam if (caps & IEEE80211_C_IBSS) 972178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); 973178354Ssam if (caps & IEEE80211_C_HOSTAP) 974178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); 975178354Ssam if (caps & IEEE80211_C_AHDEMO) 976178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 977178354Ssam if (caps & IEEE80211_C_MONITOR) 978178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); 979178354Ssam if (caps & IEEE80211_C_WDS) 980178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_WDS); 981195618Srpaulo if (caps & IEEE80211_C_MBSS) 982195618Srpaulo ADD(media, mword, mopt | IFM_IEEE80211_MBSS); 983170530Ssam#undef ADD 984170530Ssam} 985170530Ssam 986170530Ssam/* 987116742Ssam * Setup the media data structures according to the channel and 988178354Ssam * rate tables. 989116742Ssam */ 990178354Ssamstatic int 991178354Ssamieee80211_media_setup(struct ieee80211com *ic, 992178354Ssam struct ifmedia *media, int caps, int addsta, 993116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 994116742Ssam{ 995170530Ssam int i, j, mode, rate, maxrate, mword, r; 996170530Ssam const struct ieee80211_rateset *rs; 997116742Ssam struct ieee80211_rateset allrates; 998116742Ssam 999118887Ssam /* 1000116742Ssam * Fill in media characteristics. 1001116742Ssam */ 1002178354Ssam ifmedia_init(media, 0, media_change, media_stat); 1003116742Ssam maxrate = 0; 1004170530Ssam /* 1005170530Ssam * Add media for legacy operating modes. 1006170530Ssam */ 1007116742Ssam memset(&allrates, 0, sizeof(allrates)); 1008170530Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { 1009167468Ssam if (isclr(ic->ic_modecaps, mode)) 1010116742Ssam continue; 1011178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 1012116742Ssam if (mode == IEEE80211_MODE_AUTO) 1013116742Ssam continue; 1014116742Ssam rs = &ic->ic_sup_rates[mode]; 1015116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 1016116742Ssam rate = rs->rs_rates[i]; 1017116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 1018116742Ssam if (mword == 0) 1019116742Ssam continue; 1020178354Ssam addmedia(media, caps, addsta, mode, mword); 1021116742Ssam /* 1022170530Ssam * Add legacy rate to the collection of all rates. 1023116742Ssam */ 1024116742Ssam r = rate & IEEE80211_RATE_VAL; 1025116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 1026116742Ssam if (allrates.rs_rates[j] == r) 1027116742Ssam break; 1028116742Ssam if (j == allrates.rs_nrates) { 1029116742Ssam /* unique, add to the set */ 1030116742Ssam allrates.rs_rates[j] = r; 1031116742Ssam allrates.rs_nrates++; 1032116742Ssam } 1033116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 1034116742Ssam if (rate > maxrate) 1035116742Ssam maxrate = rate; 1036116742Ssam } 1037116742Ssam } 1038116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 1039116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 1040116742Ssam IEEE80211_MODE_AUTO); 1041116742Ssam if (mword == 0) 1042116742Ssam continue; 1043170530Ssam /* NB: remove media options from mword */ 1044178354Ssam addmedia(media, caps, addsta, 1045178354Ssam IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); 1046116742Ssam } 1047170530Ssam /* 1048170530Ssam * Add HT/11n media. Note that we do not have enough 1049170530Ssam * bits in the media subtype to express the MCS so we 1050170530Ssam * use a "placeholder" media subtype and any fixed MCS 1051170530Ssam * must be specified with a different mechanism. 1052170530Ssam */ 1053188782Ssam for (; mode <= IEEE80211_MODE_11NG; mode++) { 1054170530Ssam if (isclr(ic->ic_modecaps, mode)) 1055170530Ssam continue; 1056178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 1057178354Ssam addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); 1058170530Ssam } 1059170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 1060170530Ssam isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 1061178354Ssam addmedia(media, caps, addsta, 1062178354Ssam IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); 1063170530Ssam /* XXX could walk htrates */ 1064170530Ssam /* XXX known array size */ 1065178354Ssam if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) 1066178354Ssam maxrate = ieee80211_htrates[15].ht40_rate_400ns; 1067170530Ssam } 1068178354Ssam return maxrate; 1069178354Ssam} 1070116742Ssam 1071178354Ssamvoid 1072178354Ssamieee80211_media_init(struct ieee80211com *ic) 1073178354Ssam{ 1074178354Ssam struct ifnet *ifp = ic->ic_ifp; 1075178354Ssam int maxrate; 1076178354Ssam 1077178354Ssam /* NB: this works because the structure is initialized to zero */ 1078178354Ssam if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { 1079178354Ssam /* 1080178354Ssam * We are re-initializing the channel list; clear 1081178354Ssam * the existing media state as the media routines 1082178354Ssam * don't suppress duplicates. 1083178354Ssam */ 1084178354Ssam ifmedia_removeall(&ic->ic_media); 1085178354Ssam } 1086178354Ssam ieee80211_chan_init(ic); 1087178354Ssam 1088178354Ssam /* 1089178354Ssam * Recalculate media settings in case new channel list changes 1090178354Ssam * the set of available modes. 1091178354Ssam */ 1092178354Ssam maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, 1093178354Ssam ieee80211com_media_change, ieee80211com_media_status); 1094170530Ssam /* NB: strip explicit mode; we're actually in autoselect */ 1095170530Ssam ifmedia_set(&ic->ic_media, 1096188106Ssam media_status(ic->ic_opmode, ic->ic_curchan) &~ 1097188106Ssam (IFM_MMASK | IFM_IEEE80211_TURBO)); 1098116742Ssam if (maxrate) 1099116742Ssam ifp->if_baudrate = IF_Mbps(maxrate); 1100178354Ssam 1101178354Ssam /* XXX need to propagate new media settings to vap's */ 1102116742Ssam} 1103116742Ssam 1104188782Ssam/* XXX inline or eliminate? */ 1105165569Ssamconst struct ieee80211_rateset * 1106165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 1107165569Ssam{ 1108188774Ssam /* XXX does this work for 11ng basic rates? */ 1109170530Ssam return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; 1110165569Ssam} 1111165569Ssam 1112138568Ssamvoid 1113138568Ssamieee80211_announce(struct ieee80211com *ic) 1114138568Ssam{ 1115138568Ssam struct ifnet *ifp = ic->ic_ifp; 1116138568Ssam int i, mode, rate, mword; 1117170530Ssam const struct ieee80211_rateset *rs; 1118138568Ssam 1119172227Ssam /* NB: skip AUTO since it has no rates */ 1120172227Ssam for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { 1121167468Ssam if (isclr(ic->ic_modecaps, mode)) 1122138568Ssam continue; 1123138568Ssam if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 1124138568Ssam rs = &ic->ic_sup_rates[mode]; 1125138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 1126170530Ssam mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 1127138568Ssam if (mword == 0) 1128138568Ssam continue; 1129170530Ssam rate = ieee80211_media2rate(mword); 1130138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 1131170530Ssam rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 1132138568Ssam } 1133138568Ssam printf("\n"); 1134138568Ssam } 1135170530Ssam ieee80211_ht_announce(ic); 1136138568Ssam} 1137138568Ssam 1138170530Ssamvoid 1139170530Ssamieee80211_announce_channels(struct ieee80211com *ic) 1140116742Ssam{ 1141170530Ssam const struct ieee80211_channel *c; 1142170530Ssam char type; 1143170530Ssam int i, cw; 1144170530Ssam 1145170530Ssam printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); 1146170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 1147170530Ssam c = &ic->ic_channels[i]; 1148170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 1149170530Ssam type = 'S'; 1150170530Ssam else if (IEEE80211_IS_CHAN_108A(c)) 1151170530Ssam type = 'T'; 1152170530Ssam else if (IEEE80211_IS_CHAN_108G(c)) 1153170530Ssam type = 'G'; 1154170530Ssam else if (IEEE80211_IS_CHAN_HT(c)) 1155170530Ssam type = 'n'; 1156170530Ssam else if (IEEE80211_IS_CHAN_A(c)) 1157170530Ssam type = 'a'; 1158170530Ssam else if (IEEE80211_IS_CHAN_ANYG(c)) 1159170530Ssam type = 'g'; 1160170530Ssam else if (IEEE80211_IS_CHAN_B(c)) 1161170530Ssam type = 'b'; 1162170530Ssam else 1163170530Ssam type = 'f'; 1164170530Ssam if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) 1165170530Ssam cw = 40; 1166170530Ssam else if (IEEE80211_IS_CHAN_HALF(c)) 1167170530Ssam cw = 10; 1168170530Ssam else if (IEEE80211_IS_CHAN_QUARTER(c)) 1169170530Ssam cw = 5; 1170170530Ssam else 1171170530Ssam cw = 20; 1172170530Ssam printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" 1173170530Ssam , c->ic_ieee, c->ic_freq, type 1174170530Ssam , cw 1175170530Ssam , IEEE80211_IS_CHAN_HT40U(c) ? '+' : 1176170530Ssam IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' 1177170530Ssam , c->ic_maxregpower 1178170530Ssam , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 1179170530Ssam , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 1180170530Ssam ); 1181170530Ssam } 1182116742Ssam} 1183116742Ssam 1184170530Ssamstatic int 1185184273Ssammedia2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) 1186170530Ssam{ 1187116742Ssam switch (IFM_MODE(ime->ifm_media)) { 1188116742Ssam case IFM_IEEE80211_11A: 1189178354Ssam *mode = IEEE80211_MODE_11A; 1190116742Ssam break; 1191116742Ssam case IFM_IEEE80211_11B: 1192178354Ssam *mode = IEEE80211_MODE_11B; 1193116742Ssam break; 1194116742Ssam case IFM_IEEE80211_11G: 1195178354Ssam *mode = IEEE80211_MODE_11G; 1196116742Ssam break; 1197124543Sonoe case IFM_IEEE80211_FH: 1198178354Ssam *mode = IEEE80211_MODE_FH; 1199124543Sonoe break; 1200170530Ssam case IFM_IEEE80211_11NA: 1201178354Ssam *mode = IEEE80211_MODE_11NA; 1202170530Ssam break; 1203170530Ssam case IFM_IEEE80211_11NG: 1204178354Ssam *mode = IEEE80211_MODE_11NG; 1205170530Ssam break; 1206116742Ssam case IFM_AUTO: 1207178354Ssam *mode = IEEE80211_MODE_AUTO; 1208116742Ssam break; 1209116742Ssam default: 1210178354Ssam return 0; 1211116742Ssam } 1212116742Ssam /* 1213138568Ssam * Turbo mode is an ``option''. 1214138568Ssam * XXX does not apply to AUTO 1215116742Ssam */ 1216116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 1217178354Ssam if (*mode == IEEE80211_MODE_11A) { 1218184273Ssam if (flags & IEEE80211_F_TURBOP) 1219178354Ssam *mode = IEEE80211_MODE_TURBO_A; 1220170530Ssam else 1221178354Ssam *mode = IEEE80211_MODE_STURBO_A; 1222178354Ssam } else if (*mode == IEEE80211_MODE_11G) 1223178354Ssam *mode = IEEE80211_MODE_TURBO_G; 1224138568Ssam else 1225178354Ssam return 0; 1226116742Ssam } 1227170530Ssam /* XXX HT40 +/- */ 1228178354Ssam return 1; 1229178354Ssam} 1230116742Ssam 1231178354Ssam/* 1232184273Ssam * Handle a media change request on the underlying interface. 1233178354Ssam */ 1234178354Ssamint 1235178354Ssamieee80211com_media_change(struct ifnet *ifp) 1236178354Ssam{ 1237184273Ssam return EINVAL; 1238178354Ssam} 1239116742Ssam 1240178354Ssam/* 1241178354Ssam * Handle a media change request on the vap interface. 1242178354Ssam */ 1243178354Ssamint 1244178354Ssamieee80211_media_change(struct ifnet *ifp) 1245178354Ssam{ 1246178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1247178354Ssam struct ifmedia_entry *ime = vap->iv_media.ifm_cur; 1248184273Ssam uint16_t newmode; 1249178354Ssam 1250184273Ssam if (!media2mode(ime, vap->iv_flags, &newmode)) 1251178354Ssam return EINVAL; 1252184273Ssam if (vap->iv_des_mode != newmode) { 1253184273Ssam vap->iv_des_mode = newmode; 1254193340Ssam /* XXX kick state machine if up+running */ 1255178354Ssam } 1256178354Ssam return 0; 1257116742Ssam} 1258116742Ssam 1259170530Ssam/* 1260170530Ssam * Common code to calculate the media status word 1261170530Ssam * from the operating mode and channel state. 1262170530Ssam */ 1263170530Ssamstatic int 1264170530Ssammedia_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) 1265170530Ssam{ 1266170530Ssam int status; 1267170530Ssam 1268170530Ssam status = IFM_IEEE80211; 1269170530Ssam switch (opmode) { 1270170530Ssam case IEEE80211_M_STA: 1271170530Ssam break; 1272170530Ssam case IEEE80211_M_IBSS: 1273170530Ssam status |= IFM_IEEE80211_ADHOC; 1274170530Ssam break; 1275170530Ssam case IEEE80211_M_HOSTAP: 1276170530Ssam status |= IFM_IEEE80211_HOSTAP; 1277170530Ssam break; 1278170530Ssam case IEEE80211_M_MONITOR: 1279170530Ssam status |= IFM_IEEE80211_MONITOR; 1280170530Ssam break; 1281170530Ssam case IEEE80211_M_AHDEMO: 1282170530Ssam status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; 1283170530Ssam break; 1284170530Ssam case IEEE80211_M_WDS: 1285178354Ssam status |= IFM_IEEE80211_WDS; 1286170530Ssam break; 1287195618Srpaulo case IEEE80211_M_MBSS: 1288195618Srpaulo status |= IFM_IEEE80211_MBSS; 1289195618Srpaulo break; 1290170530Ssam } 1291170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) { 1292170530Ssam status |= IFM_IEEE80211_11NA; 1293170530Ssam } else if (IEEE80211_IS_CHAN_HTG(chan)) { 1294170530Ssam status |= IFM_IEEE80211_11NG; 1295170530Ssam } else if (IEEE80211_IS_CHAN_A(chan)) { 1296170530Ssam status |= IFM_IEEE80211_11A; 1297170530Ssam } else if (IEEE80211_IS_CHAN_B(chan)) { 1298170530Ssam status |= IFM_IEEE80211_11B; 1299170530Ssam } else if (IEEE80211_IS_CHAN_ANYG(chan)) { 1300170530Ssam status |= IFM_IEEE80211_11G; 1301170530Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) { 1302170530Ssam status |= IFM_IEEE80211_FH; 1303170530Ssam } 1304170530Ssam /* XXX else complain? */ 1305170530Ssam 1306170530Ssam if (IEEE80211_IS_CHAN_TURBO(chan)) 1307170530Ssam status |= IFM_IEEE80211_TURBO; 1308178354Ssam#if 0 1309178354Ssam if (IEEE80211_IS_CHAN_HT20(chan)) 1310178354Ssam status |= IFM_IEEE80211_HT20; 1311178354Ssam if (IEEE80211_IS_CHAN_HT40(chan)) 1312178354Ssam status |= IFM_IEEE80211_HT40; 1313178354Ssam#endif 1314170530Ssam return status; 1315170530Ssam} 1316170530Ssam 1317178354Ssamstatic void 1318178354Ssamieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1319178354Ssam{ 1320178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1321178354Ssam struct ieee80211vap *vap; 1322178354Ssam 1323178354Ssam imr->ifm_status = IFM_AVALID; 1324178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1325178354Ssam if (vap->iv_ifp->if_flags & IFF_UP) { 1326178354Ssam imr->ifm_status |= IFM_ACTIVE; 1327178354Ssam break; 1328178354Ssam } 1329178354Ssam imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); 1330178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1331178354Ssam imr->ifm_current = imr->ifm_active; 1332178354Ssam} 1333178354Ssam 1334116742Ssamvoid 1335116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1336116742Ssam{ 1337178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1338178354Ssam struct ieee80211com *ic = vap->iv_ic; 1339170530Ssam enum ieee80211_phymode mode; 1340116742Ssam 1341116742Ssam imr->ifm_status = IFM_AVALID; 1342170530Ssam /* 1343170530Ssam * NB: use the current channel's mode to lock down a xmit 1344170530Ssam * rate only when running; otherwise we may have a mismatch 1345170530Ssam * in which case the rate will not be convertible. 1346170530Ssam */ 1347178354Ssam if (vap->iv_state == IEEE80211_S_RUN) { 1348116742Ssam imr->ifm_status |= IFM_ACTIVE; 1349170530Ssam mode = ieee80211_chan2mode(ic->ic_curchan); 1350170530Ssam } else 1351170530Ssam mode = IEEE80211_MODE_AUTO; 1352178354Ssam imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); 1353138568Ssam /* 1354138568Ssam * Calculate a current rate if possible. 1355138568Ssam */ 1356178354Ssam if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { 1357138568Ssam /* 1358138568Ssam * A fixed rate is set, report that. 1359138568Ssam */ 1360138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1361178354Ssam vap->iv_txparms[mode].ucastrate, mode); 1362178354Ssam } else if (vap->iv_opmode == IEEE80211_M_STA) { 1363138568Ssam /* 1364138568Ssam * In station mode report the current transmit rate. 1365138568Ssam */ 1366138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1367178354Ssam vap->iv_bss->ni_txrate, mode); 1368128966Sandre } else 1369138568Ssam imr->ifm_active |= IFM_AUTO; 1370178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1371178354Ssam imr->ifm_current = imr->ifm_active; 1372116742Ssam} 1373116742Ssam 1374116742Ssam/* 1375116742Ssam * Set the current phy mode and recalculate the active channel 1376116742Ssam * set based on the available channels for this mode. Also 1377116742Ssam * select a new default/current channel if the current one is 1378116742Ssam * inappropriate for this mode. 1379116742Ssam */ 1380116742Ssamint 1381116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 1382116742Ssam{ 1383116742Ssam /* 1384166012Ssam * Adjust basic rates in 11b/11g supported rate set. 1385166012Ssam * Note that if operating on a hal/quarter rate channel 1386166012Ssam * this is a noop as those rates sets are different 1387166012Ssam * and used instead. 1388116742Ssam */ 1389166012Ssam if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 1390178354Ssam ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); 1391166012Ssam 1392116742Ssam ic->ic_curmode = mode; 1393138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 1394138568Ssam 1395116742Ssam return 0; 1396116742Ssam} 1397116742Ssam 1398116742Ssam/* 1399170530Ssam * Return the phy mode for with the specified channel. 1400116742Ssam */ 1401116742Ssamenum ieee80211_phymode 1402170530Ssamieee80211_chan2mode(const struct ieee80211_channel *chan) 1403116742Ssam{ 1404170530Ssam 1405170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) 1406170530Ssam return IEEE80211_MODE_11NA; 1407170530Ssam else if (IEEE80211_IS_CHAN_HTG(chan)) 1408170530Ssam return IEEE80211_MODE_11NG; 1409170530Ssam else if (IEEE80211_IS_CHAN_108G(chan)) 1410170530Ssam return IEEE80211_MODE_TURBO_G; 1411170530Ssam else if (IEEE80211_IS_CHAN_ST(chan)) 1412170530Ssam return IEEE80211_MODE_STURBO_A; 1413170530Ssam else if (IEEE80211_IS_CHAN_TURBO(chan)) 1414153350Ssam return IEEE80211_MODE_TURBO_A; 1415188782Ssam else if (IEEE80211_IS_CHAN_HALF(chan)) 1416188782Ssam return IEEE80211_MODE_HALF; 1417188782Ssam else if (IEEE80211_IS_CHAN_QUARTER(chan)) 1418188782Ssam return IEEE80211_MODE_QUARTER; 1419170530Ssam else if (IEEE80211_IS_CHAN_A(chan)) 1420116742Ssam return IEEE80211_MODE_11A; 1421170530Ssam else if (IEEE80211_IS_CHAN_ANYG(chan)) 1422116742Ssam return IEEE80211_MODE_11G; 1423170530Ssam else if (IEEE80211_IS_CHAN_B(chan)) 1424116742Ssam return IEEE80211_MODE_11B; 1425170530Ssam else if (IEEE80211_IS_CHAN_FHSS(chan)) 1426170530Ssam return IEEE80211_MODE_FH; 1427170530Ssam 1428170530Ssam /* NB: should not get here */ 1429170530Ssam printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", 1430170530Ssam __func__, chan->ic_freq, chan->ic_flags); 1431170530Ssam return IEEE80211_MODE_11B; 1432116742Ssam} 1433116742Ssam 1434170530Ssamstruct ratemedia { 1435170530Ssam u_int match; /* rate + mode */ 1436170530Ssam u_int media; /* if_media rate */ 1437170530Ssam}; 1438170530Ssam 1439170530Ssamstatic int 1440170530Ssamfindmedia(const struct ratemedia rates[], int n, u_int match) 1441170530Ssam{ 1442170530Ssam int i; 1443170530Ssam 1444170530Ssam for (i = 0; i < n; i++) 1445170530Ssam if (rates[i].match == match) 1446170530Ssam return rates[i].media; 1447170530Ssam return IFM_AUTO; 1448170530Ssam} 1449170530Ssam 1450116742Ssam/* 1451170530Ssam * Convert IEEE80211 rate value to ifmedia subtype. 1452170530Ssam * Rate is either a legacy rate in units of 0.5Mbps 1453170530Ssam * or an MCS index. 1454116742Ssam */ 1455116742Ssamint 1456116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1457116742Ssam{ 1458116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1459170530Ssam static const struct ratemedia rates[] = { 1460124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1461124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1462124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1463124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1464124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1465124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1466124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1467124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1468124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1469124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1470124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1471124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1472124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1473124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1474124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1475124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1476124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1477124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1478124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1479124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1480124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1481124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1482124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1483124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1484124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1485124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1486124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1487165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1488165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1489165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1490116742Ssam /* NB: OFDM72 doesn't realy exist so we don't handle it */ 1491116742Ssam }; 1492170530Ssam static const struct ratemedia htrates[] = { 1493170530Ssam { 0, IFM_IEEE80211_MCS }, 1494170530Ssam { 1, IFM_IEEE80211_MCS }, 1495170530Ssam { 2, IFM_IEEE80211_MCS }, 1496170530Ssam { 3, IFM_IEEE80211_MCS }, 1497170530Ssam { 4, IFM_IEEE80211_MCS }, 1498170530Ssam { 5, IFM_IEEE80211_MCS }, 1499170530Ssam { 6, IFM_IEEE80211_MCS }, 1500170530Ssam { 7, IFM_IEEE80211_MCS }, 1501170530Ssam { 8, IFM_IEEE80211_MCS }, 1502170530Ssam { 9, IFM_IEEE80211_MCS }, 1503170530Ssam { 10, IFM_IEEE80211_MCS }, 1504170530Ssam { 11, IFM_IEEE80211_MCS }, 1505170530Ssam { 12, IFM_IEEE80211_MCS }, 1506170530Ssam { 13, IFM_IEEE80211_MCS }, 1507170530Ssam { 14, IFM_IEEE80211_MCS }, 1508170530Ssam { 15, IFM_IEEE80211_MCS }, 1509170530Ssam }; 1510170530Ssam int m; 1511116742Ssam 1512170530Ssam /* 1513170530Ssam * Check 11n rates first for match as an MCS. 1514170530Ssam */ 1515170530Ssam if (mode == IEEE80211_MODE_11NA) { 1516172226Ssam if (rate & IEEE80211_RATE_MCS) { 1517172226Ssam rate &= ~IEEE80211_RATE_MCS; 1518170530Ssam m = findmedia(htrates, N(htrates), rate); 1519170530Ssam if (m != IFM_AUTO) 1520170530Ssam return m | IFM_IEEE80211_11NA; 1521170530Ssam } 1522170530Ssam } else if (mode == IEEE80211_MODE_11NG) { 1523170530Ssam /* NB: 12 is ambiguous, it will be treated as an MCS */ 1524172226Ssam if (rate & IEEE80211_RATE_MCS) { 1525172226Ssam rate &= ~IEEE80211_RATE_MCS; 1526170530Ssam m = findmedia(htrates, N(htrates), rate); 1527170530Ssam if (m != IFM_AUTO) 1528170530Ssam return m | IFM_IEEE80211_11NG; 1529170530Ssam } 1530170530Ssam } 1531170530Ssam rate &= IEEE80211_RATE_VAL; 1532116742Ssam switch (mode) { 1533116742Ssam case IEEE80211_MODE_11A: 1534188782Ssam case IEEE80211_MODE_HALF: /* XXX good 'nuf */ 1535188782Ssam case IEEE80211_MODE_QUARTER: 1536170530Ssam case IEEE80211_MODE_11NA: 1537138568Ssam case IEEE80211_MODE_TURBO_A: 1538170530Ssam case IEEE80211_MODE_STURBO_A: 1539170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); 1540116742Ssam case IEEE80211_MODE_11B: 1541170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); 1542124543Sonoe case IEEE80211_MODE_FH: 1543170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); 1544116742Ssam case IEEE80211_MODE_AUTO: 1545116742Ssam /* NB: ic may be NULL for some drivers */ 1546188775Ssam if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) 1547170530Ssam return findmedia(rates, N(rates), 1548170530Ssam rate | IFM_IEEE80211_FH); 1549116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1550116742Ssam /* fall thru... */ 1551116742Ssam case IEEE80211_MODE_11G: 1552170530Ssam case IEEE80211_MODE_11NG: 1553138568Ssam case IEEE80211_MODE_TURBO_G: 1554170530Ssam return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); 1555116742Ssam } 1556116742Ssam return IFM_AUTO; 1557116742Ssam#undef N 1558116742Ssam} 1559116742Ssam 1560116742Ssamint 1561116742Ssamieee80211_media2rate(int mword) 1562116742Ssam{ 1563116742Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1564116742Ssam static const int ieeerates[] = { 1565116742Ssam -1, /* IFM_AUTO */ 1566116742Ssam 0, /* IFM_MANUAL */ 1567116742Ssam 0, /* IFM_NONE */ 1568116742Ssam 2, /* IFM_IEEE80211_FH1 */ 1569116742Ssam 4, /* IFM_IEEE80211_FH2 */ 1570116742Ssam 2, /* IFM_IEEE80211_DS1 */ 1571116742Ssam 4, /* IFM_IEEE80211_DS2 */ 1572116742Ssam 11, /* IFM_IEEE80211_DS5 */ 1573116742Ssam 22, /* IFM_IEEE80211_DS11 */ 1574116742Ssam 44, /* IFM_IEEE80211_DS22 */ 1575116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 1576116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 1577116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 1578116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 1579116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 1580116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 1581116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 1582116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 1583116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 1584165569Ssam 0, /* IFM_IEEE80211_DS354k */ 1585165569Ssam 0, /* IFM_IEEE80211_DS512k */ 1586165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 1587165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 1588165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 1589170530Ssam -1, /* IFM_IEEE80211_MCS */ 1590116742Ssam }; 1591116742Ssam return IFM_SUBTYPE(mword) < N(ieeerates) ? 1592116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 1593116742Ssam#undef N 1594116742Ssam} 1595195379Ssam 1596195379Ssam/* 1597195379Ssam * The following hash function is adapted from "Hash Functions" by Bob Jenkins 1598195379Ssam * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). 1599195379Ssam */ 1600195379Ssam#define mix(a, b, c) \ 1601195379Ssamdo { \ 1602195379Ssam a -= b; a -= c; a ^= (c >> 13); \ 1603195379Ssam b -= c; b -= a; b ^= (a << 8); \ 1604195379Ssam c -= a; c -= b; c ^= (b >> 13); \ 1605195379Ssam a -= b; a -= c; a ^= (c >> 12); \ 1606195379Ssam b -= c; b -= a; b ^= (a << 16); \ 1607195379Ssam c -= a; c -= b; c ^= (b >> 5); \ 1608195379Ssam a -= b; a -= c; a ^= (c >> 3); \ 1609195379Ssam b -= c; b -= a; b ^= (a << 10); \ 1610195379Ssam c -= a; c -= b; c ^= (b >> 15); \ 1611195379Ssam} while (/*CONSTCOND*/0) 1612195379Ssam 1613195379Ssamuint32_t 1614195379Ssamieee80211_mac_hash(const struct ieee80211com *ic, 1615195379Ssam const uint8_t addr[IEEE80211_ADDR_LEN]) 1616195379Ssam{ 1617195379Ssam uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = ic->ic_hash_key; 1618195379Ssam 1619195379Ssam b += addr[5] << 8; 1620195379Ssam b += addr[4]; 1621195379Ssam a += addr[3] << 24; 1622195379Ssam a += addr[2] << 16; 1623195379Ssam a += addr[1] << 8; 1624195379Ssam a += addr[0]; 1625195379Ssam 1626195379Ssam mix(a, b, c); 1627195379Ssam 1628195379Ssam return c; 1629195379Ssam} 1630195379Ssam#undef mix 1631