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: stable/11/sys/net80211/ieee80211.c 343976 2019-02-10 21:00:02Z avos $"); 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> 38295126Sglebius#include <sys/malloc.h> 39116742Ssam#include <sys/socket.h> 40287197Sglebius#include <sys/sbuf.h> 41116742Ssam 42283529Sglebius#include <machine/stdarg.h> 43283529Sglebius 44116742Ssam#include <net/if.h> 45257176Sglebius#include <net/if_var.h> 46178354Ssam#include <net/if_dl.h> 47116742Ssam#include <net/if_media.h> 48178354Ssam#include <net/if_types.h> 49116742Ssam#include <net/ethernet.h> 50116742Ssam 51116742Ssam#include <net80211/ieee80211_var.h> 52178354Ssam#include <net80211/ieee80211_regdomain.h> 53190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 54190391Ssam#include <net80211/ieee80211_superg.h> 55190391Ssam#endif 56206358Srpaulo#include <net80211/ieee80211_ratectl.h> 57116742Ssam 58116742Ssam#include <net/bpf.h> 59116742Ssam 60178955Ssamconst char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = { 61178955Ssam [IEEE80211_MODE_AUTO] = "auto", 62178955Ssam [IEEE80211_MODE_11A] = "11a", 63178955Ssam [IEEE80211_MODE_11B] = "11b", 64178955Ssam [IEEE80211_MODE_11G] = "11g", 65178955Ssam [IEEE80211_MODE_FH] = "FH", 66178955Ssam [IEEE80211_MODE_TURBO_A] = "turboA", 67178955Ssam [IEEE80211_MODE_TURBO_G] = "turboG", 68178955Ssam [IEEE80211_MODE_STURBO_A] = "sturboA", 69188782Ssam [IEEE80211_MODE_HALF] = "half", 70188782Ssam [IEEE80211_MODE_QUARTER] = "quarter", 71178955Ssam [IEEE80211_MODE_11NA] = "11na", 72178955Ssam [IEEE80211_MODE_11NG] = "11ng", 73116742Ssam}; 74178957Ssam/* map ieee80211_opmode to the corresponding capability bit */ 75178957Ssamconst int ieee80211_opcap[IEEE80211_OPMODE_MAX] = { 76178957Ssam [IEEE80211_M_IBSS] = IEEE80211_C_IBSS, 77178957Ssam [IEEE80211_M_WDS] = IEEE80211_C_WDS, 78178957Ssam [IEEE80211_M_STA] = IEEE80211_C_STA, 79178957Ssam [IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO, 80178957Ssam [IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP, 81178957Ssam [IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR, 82195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 83195618Srpaulo [IEEE80211_M_MBSS] = IEEE80211_C_MBSS, 84195618Srpaulo#endif 85178957Ssam}; 86178957Ssam 87283566Sglebiusconst uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = 88178354Ssam { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 89116742Ssam 90178354Ssamstatic void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); 91193655Ssamstatic void ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag); 92178354Ssamstatic void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); 93178354Ssamstatic int ieee80211_media_setup(struct ieee80211com *ic, 94178354Ssam struct ifmedia *media, int caps, int addsta, 95178354Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); 96178354Ssamstatic int media_status(enum ieee80211_opmode, 97178354Ssam const struct ieee80211_channel *); 98283568Sglebiusstatic uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); 99178354Ssam 100178354SsamMALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); 101178354Ssam 102164645Ssam/* 103164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units). 104164645Ssam */ 105164645Ssam#define B(r) ((r) | IEEE80211_RATE_BASIC) 106164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a = 107164645Ssam { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; 108165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half = 109165569Ssam { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; 110165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter = 111165569Ssam { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; 112164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b = 113164645Ssam { 4, { B(2), B(4), B(11), B(22) } }; 114164645Ssam/* NB: OFDM rates are handled specially based on mode */ 115164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g = 116164645Ssam { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; 117164645Ssam#undef B 118164645Ssam 119140915Ssam/* 120165569Ssam * Fill in 802.11 available channel set, mark 121165569Ssam * all available channels as active, and pick 122165569Ssam * a default channel if not already specified. 123165569Ssam */ 124287197Sglebiusvoid 125165569Ssamieee80211_chan_init(struct ieee80211com *ic) 126116742Ssam{ 127165569Ssam#define DEFAULTRATES(m, def) do { \ 128188782Ssam if (ic->ic_sup_rates[m].rs_nrates == 0) \ 129165574Ssam ic->ic_sup_rates[m] = def; \ 130165569Ssam} while (0) 131116742Ssam struct ieee80211_channel *c; 132116742Ssam int i; 133116742Ssam 134186107Ssam KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, 135170530Ssam ("invalid number of channels specified: %u", ic->ic_nchans)); 136116742Ssam memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); 137178354Ssam memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); 138167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); 139170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 140116742Ssam c = &ic->ic_channels[i]; 141170530Ssam KASSERT(c->ic_flags != 0, ("channel with no flags")); 142187796Ssam /* 143187796Ssam * Help drivers that work only with frequencies by filling 144187796Ssam * in IEEE channel #'s if not already calculated. Note this 145187796Ssam * mimics similar work done in ieee80211_setregdomain when 146187796Ssam * changing regulatory state. 147187796Ssam */ 148187796Ssam if (c->ic_ieee == 0) 149187796Ssam c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); 150187796Ssam if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) 151187796Ssam c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + 152187796Ssam (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), 153187796Ssam c->ic_flags); 154187796Ssam /* default max tx power to max regulatory */ 155187796Ssam if (c->ic_maxpower == 0) 156187796Ssam c->ic_maxpower = 2*c->ic_maxregpower; 157170530Ssam setbit(ic->ic_chan_avail, c->ic_ieee); 158170530Ssam /* 159170530Ssam * Identify mode capabilities. 160170530Ssam */ 161170530Ssam if (IEEE80211_IS_CHAN_A(c)) 162170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11A); 163170530Ssam if (IEEE80211_IS_CHAN_B(c)) 164170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11B); 165170530Ssam if (IEEE80211_IS_CHAN_ANYG(c)) 166170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11G); 167170530Ssam if (IEEE80211_IS_CHAN_FHSS(c)) 168170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_FH); 169170530Ssam if (IEEE80211_IS_CHAN_108A(c)) 170170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); 171170530Ssam if (IEEE80211_IS_CHAN_108G(c)) 172170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); 173170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 174170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); 175188782Ssam if (IEEE80211_IS_CHAN_HALF(c)) 176188782Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_HALF); 177188782Ssam if (IEEE80211_IS_CHAN_QUARTER(c)) 178188782Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_QUARTER); 179170530Ssam if (IEEE80211_IS_CHAN_HTA(c)) 180170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); 181170530Ssam if (IEEE80211_IS_CHAN_HTG(c)) 182170530Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); 183116742Ssam } 184170530Ssam /* initialize candidate channels to all available */ 185170530Ssam memcpy(ic->ic_chan_active, ic->ic_chan_avail, 186170530Ssam sizeof(ic->ic_chan_avail)); 187164645Ssam 188178354Ssam /* sort channel table to allow lookup optimizations */ 189178354Ssam ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); 190178354Ssam 191178354Ssam /* invalidate any previous state */ 192170530Ssam ic->ic_bsschan = IEEE80211_CHAN_ANYC; 193172233Ssam ic->ic_prevchan = NULL; 194178354Ssam ic->ic_csa_newchan = NULL; 195170530Ssam /* arbitrarily pick the first channel */ 196170530Ssam ic->ic_curchan = &ic->ic_channels[0]; 197190532Ssam ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); 198170530Ssam 199164645Ssam /* fillin well-known rate sets if driver has not specified */ 200165569Ssam DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); 201165569Ssam DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); 202165569Ssam DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); 203165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); 204165569Ssam DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); 205187897Ssam DEFAULTRATES(IEEE80211_MODE_STURBO_A, ieee80211_rateset_11a); 206188782Ssam DEFAULTRATES(IEEE80211_MODE_HALF, ieee80211_rateset_half); 207188782Ssam DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter); 208188774Ssam DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); 209188774Ssam DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); 210165569Ssam 211165569Ssam /* 212219596Sbschmidt * Setup required information to fill the mcsset field, if driver did 213219596Sbschmidt * not. Assume a 2T2R setup for historic reasons. 214219596Sbschmidt */ 215219596Sbschmidt if (ic->ic_rxstream == 0) 216219596Sbschmidt ic->ic_rxstream = 2; 217219596Sbschmidt if (ic->ic_txstream == 0) 218219596Sbschmidt ic->ic_txstream = 2; 219219596Sbschmidt 220219596Sbschmidt /* 221165569Ssam * Set auto mode to reset active channel state and any desired channel. 222165569Ssam */ 223165569Ssam (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); 224165569Ssam#undef DEFAULTRATES 225165569Ssam} 226165569Ssam 227178354Ssamstatic void 228283540Sglebiusnull_update_mcast(struct ieee80211com *ic) 229178354Ssam{ 230283540Sglebius 231283540Sglebius ic_printf(ic, "need multicast update callback\n"); 232178354Ssam} 233178354Ssam 234178354Ssamstatic void 235283540Sglebiusnull_update_promisc(struct ieee80211com *ic) 236178354Ssam{ 237283540Sglebius 238283540Sglebius ic_printf(ic, "need promiscuous mode update callback\n"); 239178354Ssam} 240178354Ssam 241178521Ssamstatic void 242233452Sadriannull_update_chw(struct ieee80211com *ic) 243233452Sadrian{ 244233452Sadrian 245283529Sglebius ic_printf(ic, "%s: need callback\n", __func__); 246233452Sadrian} 247233452Sadrian 248283529Sglebiusint 249283529Sglebiusic_printf(struct ieee80211com *ic, const char * fmt, ...) 250337950Skevans{ 251283529Sglebius va_list ap; 252283529Sglebius int retval; 253283529Sglebius 254283529Sglebius retval = printf("%s: ", ic->ic_name); 255283529Sglebius va_start(ap, fmt); 256283529Sglebius retval += vprintf(fmt, ap); 257337950Skevans va_end(ap); 258283529Sglebius return (retval); 259283529Sglebius} 260283529Sglebius 261287197Sglebiusstatic LIST_HEAD(, ieee80211com) ic_head = LIST_HEAD_INITIALIZER(ic_head); 262287197Sglebiusstatic struct mtx ic_list_mtx; 263287197SglebiusMTX_SYSINIT(ic_list, &ic_list_mtx, "ieee80211com list", MTX_DEF); 264287197Sglebius 265287197Sglebiusstatic int 266287197Sglebiussysctl_ieee80211coms(SYSCTL_HANDLER_ARGS) 267287197Sglebius{ 268287197Sglebius struct ieee80211com *ic; 269296114Savos struct sbuf sb; 270287197Sglebius char *sp; 271287197Sglebius int error; 272287197Sglebius 273296114Savos error = sysctl_wire_old_buffer(req, 0); 274296114Savos if (error) 275296114Savos return (error); 276296114Savos sbuf_new_for_sysctl(&sb, NULL, 8, req); 277296114Savos sbuf_clear_flags(&sb, SBUF_INCLUDENUL); 278287197Sglebius sp = ""; 279287197Sglebius mtx_lock(&ic_list_mtx); 280287197Sglebius LIST_FOREACH(ic, &ic_head, ic_next) { 281296114Savos sbuf_printf(&sb, "%s%s", sp, ic->ic_name); 282287197Sglebius sp = " "; 283287197Sglebius } 284287197Sglebius mtx_unlock(&ic_list_mtx); 285296114Savos error = sbuf_finish(&sb); 286296114Savos sbuf_delete(&sb); 287287197Sglebius return (error); 288287197Sglebius} 289287197Sglebius 290287197SglebiusSYSCTL_PROC(_net_wlan, OID_AUTO, devices, 291287197Sglebius CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, 292287197Sglebius sysctl_ieee80211coms, "A", "names of available 802.11 devices"); 293287197Sglebius 294178354Ssam/* 295178354Ssam * Attach/setup the common net80211 state. Called by 296178354Ssam * the driver on attach to prior to creating any vap's. 297178354Ssam */ 298165569Ssamvoid 299287197Sglebiusieee80211_ifattach(struct ieee80211com *ic) 300165569Ssam{ 301165569Ssam 302283529Sglebius IEEE80211_LOCK_INIT(ic, ic->ic_name); 303283529Sglebius IEEE80211_TX_LOCK_INIT(ic, ic->ic_name); 304178354Ssam TAILQ_INIT(&ic->ic_vaps); 305191746Sthompsa 306191746Sthompsa /* Create a taskqueue for all state changes */ 307191746Sthompsa ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO, 308191746Sthompsa taskqueue_thread_enqueue, &ic->ic_tq); 309230447Sadrian taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s net80211 taskq", 310283565Sglebius ic->ic_name); 311283568Sglebius ic->ic_ierrors = counter_u64_alloc(M_WAITOK); 312283568Sglebius ic->ic_oerrors = counter_u64_alloc(M_WAITOK); 313165569Ssam /* 314165569Ssam * Fill in 802.11 available channel set, mark all 315165569Ssam * available channels as active, and pick a default 316165569Ssam * channel if not already specified. 317165569Ssam */ 318287197Sglebius ieee80211_chan_init(ic); 319170530Ssam 320178354Ssam ic->ic_update_mcast = null_update_mcast; 321178354Ssam ic->ic_update_promisc = null_update_promisc; 322233452Sadrian ic->ic_update_chw = null_update_chw; 323116742Ssam 324195379Ssam ic->ic_hash_key = arc4random(); 325155688Ssam ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 326155688Ssam ic->ic_lintval = ic->ic_bintval; 327138568Ssam ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 328138568Ssam 329170530Ssam ieee80211_crypto_attach(ic); 330138568Ssam ieee80211_node_attach(ic); 331170530Ssam ieee80211_power_attach(ic); 332138568Ssam ieee80211_proto_attach(ic); 333190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 334190391Ssam ieee80211_superg_attach(ic); 335190391Ssam#endif 336170530Ssam ieee80211_ht_attach(ic); 337170530Ssam ieee80211_scan_attach(ic); 338178354Ssam ieee80211_regdomain_attach(ic); 339193843Ssam ieee80211_dfs_attach(ic); 340138568Ssam 341178354Ssam ieee80211_sysctl_attach(ic); 342138568Ssam 343287197Sglebius mtx_lock(&ic_list_mtx); 344287197Sglebius LIST_INSERT_HEAD(&ic_head, ic, ic_next); 345287197Sglebius mtx_unlock(&ic_list_mtx); 346116742Ssam} 347116742Ssam 348178354Ssam/* 349178354Ssam * Detach net80211 state on device detach. Tear down 350178354Ssam * all vap's and reclaim all common state prior to the 351178354Ssam * device state going away. Note we may call back into 352178354Ssam * driver; it must be prepared for this. 353178354Ssam */ 354116742Ssamvoid 355138568Ssamieee80211_ifdetach(struct ieee80211com *ic) 356116742Ssam{ 357178354Ssam struct ieee80211vap *vap; 358116742Ssam 359337950Skevans /* 360337950Skevans * We use this as an indicator that ifattach never had a chance to be 361337950Skevans * called, e.g. early driver attach failed and ifdetach was called 362337950Skevans * during subsequent detach. Never fear, for we have nothing to do 363337950Skevans * here. 364337950Skevans */ 365337950Skevans if (ic->ic_tq == NULL) 366337950Skevans return; 367337950Skevans 368287197Sglebius mtx_lock(&ic_list_mtx); 369287197Sglebius LIST_REMOVE(ic, ic_next); 370287197Sglebius mtx_unlock(&ic_list_mtx); 371193337Ssam 372290058Savos taskqueue_drain(taskqueue_thread, &ic->ic_restart_task); 373290058Savos 374242149Sadrian /* 375242149Sadrian * The VAP is responsible for setting and clearing 376242149Sadrian * the VIMAGE context. 377242149Sadrian */ 378343489Savos while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) { 379343489Savos ieee80211_com_vdetach(vap); 380178354Ssam ieee80211_vap_destroy(vap); 381343489Savos } 382188533Sthompsa ieee80211_waitfor_parent(ic); 383138568Ssam 384138568Ssam ieee80211_sysctl_detach(ic); 385193843Ssam ieee80211_dfs_detach(ic); 386178354Ssam ieee80211_regdomain_detach(ic); 387170530Ssam ieee80211_scan_detach(ic); 388190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 389190391Ssam ieee80211_superg_detach(ic); 390190391Ssam#endif 391170530Ssam ieee80211_ht_detach(ic); 392166012Ssam /* NB: must be called before ieee80211_node_detach */ 393138568Ssam ieee80211_proto_detach(ic); 394138568Ssam ieee80211_crypto_detach(ic); 395170530Ssam ieee80211_power_detach(ic); 396138568Ssam ieee80211_node_detach(ic); 397193337Ssam 398283568Sglebius counter_u64_free(ic->ic_ierrors); 399283568Sglebius counter_u64_free(ic->ic_oerrors); 400242149Sadrian 401191746Sthompsa taskqueue_free(ic->ic_tq); 402248069Sadrian IEEE80211_TX_LOCK_DESTROY(ic); 403170530Ssam IEEE80211_LOCK_DESTROY(ic); 404178354Ssam} 405138568Ssam 406287197Sglebiusstruct ieee80211com * 407287197Sglebiusieee80211_find_com(const char *name) 408287197Sglebius{ 409287197Sglebius struct ieee80211com *ic; 410287197Sglebius 411287197Sglebius mtx_lock(&ic_list_mtx); 412287197Sglebius LIST_FOREACH(ic, &ic_head, ic_next) 413287197Sglebius if (strcmp(ic->ic_name, name) == 0) 414287197Sglebius break; 415287197Sglebius mtx_unlock(&ic_list_mtx); 416287197Sglebius 417287197Sglebius return (ic); 418287197Sglebius} 419287197Sglebius 420300063Savosvoid 421300063Savosieee80211_iterate_coms(ieee80211_com_iter_func *f, void *arg) 422300063Savos{ 423300063Savos struct ieee80211com *ic; 424300063Savos 425300063Savos mtx_lock(&ic_list_mtx); 426300063Savos LIST_FOREACH(ic, &ic_head, ic_next) 427300063Savos (*f)(arg, ic); 428300063Savos mtx_unlock(&ic_list_mtx); 429300063Savos} 430300063Savos 431178354Ssam/* 432178354Ssam * Default reset method for use with the ioctl support. This 433178354Ssam * method is invoked after any state change in the 802.11 434178354Ssam * layer that should be propagated to the hardware but not 435178354Ssam * require re-initialization of the 802.11 state machine (e.g 436178354Ssam * rescanning for an ap). We always return ENETRESET which 437178354Ssam * should cause the driver to re-initialize the device. Drivers 438178354Ssam * can override this method to implement more optimized support. 439178354Ssam */ 440178354Ssamstatic int 441178354Ssamdefault_reset(struct ieee80211vap *vap, u_long cmd) 442178354Ssam{ 443178354Ssam return ENETRESET; 444178354Ssam} 445178354Ssam 446178354Ssam/* 447283568Sglebius * Add underlying device errors to vap errors. 448283568Sglebius */ 449283568Sglebiusstatic uint64_t 450283568Sglebiusieee80211_get_counter(struct ifnet *ifp, ift_counter cnt) 451283568Sglebius{ 452283568Sglebius struct ieee80211vap *vap = ifp->if_softc; 453283568Sglebius struct ieee80211com *ic = vap->iv_ic; 454283568Sglebius uint64_t rv; 455283568Sglebius 456283568Sglebius rv = if_get_counter_default(ifp, cnt); 457283568Sglebius switch (cnt) { 458283568Sglebius case IFCOUNTER_OERRORS: 459283568Sglebius rv += counter_u64_fetch(ic->ic_oerrors); 460283568Sglebius break; 461283568Sglebius case IFCOUNTER_IERRORS: 462283568Sglebius rv += counter_u64_fetch(ic->ic_ierrors); 463283568Sglebius break; 464283568Sglebius default: 465283568Sglebius break; 466283568Sglebius } 467283568Sglebius 468283568Sglebius return (rv); 469283568Sglebius} 470283568Sglebius 471283568Sglebius/* 472178354Ssam * Prepare a vap for use. Drivers use this call to 473178354Ssam * setup net80211 state in new vap's prior attaching 474178354Ssam * them with ieee80211_vap_attach (below). 475178354Ssam */ 476178354Ssamint 477178354Ssamieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, 478228621Sbschmidt const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, 479287197Sglebius int flags, const uint8_t bssid[IEEE80211_ADDR_LEN]) 480178354Ssam{ 481178354Ssam struct ifnet *ifp; 482178354Ssam 483178354Ssam ifp = if_alloc(IFT_ETHER); 484178354Ssam if (ifp == NULL) { 485283529Sglebius ic_printf(ic, "%s: unable to allocate ifnet\n", 486178354Ssam __func__); 487178354Ssam return ENOMEM; 488178354Ssam } 489178354Ssam if_initname(ifp, name, unit); 490178354Ssam ifp->if_softc = vap; /* back pointer */ 491178354Ssam ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 492254082Sadrian ifp->if_transmit = ieee80211_vap_transmit; 493254082Sadrian ifp->if_qflush = ieee80211_vap_qflush; 494178354Ssam ifp->if_ioctl = ieee80211_ioctl; 495178354Ssam ifp->if_init = ieee80211_init; 496283568Sglebius ifp->if_get_counter = ieee80211_get_counter; 497178354Ssam 498178354Ssam vap->iv_ifp = ifp; 499178354Ssam vap->iv_ic = ic; 500178354Ssam vap->iv_flags = ic->ic_flags; /* propagate common flags */ 501178354Ssam vap->iv_flags_ext = ic->ic_flags_ext; 502178354Ssam vap->iv_flags_ven = ic->ic_flags_ven; 503178354Ssam vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; 504178354Ssam vap->iv_htcaps = ic->ic_htcaps; 505205513Srpaulo vap->iv_htextcaps = ic->ic_htextcaps; 506178354Ssam vap->iv_opmode = opmode; 507178957Ssam vap->iv_caps |= ieee80211_opcap[opmode]; 508297592Savos IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr); 509178354Ssam switch (opmode) { 510178354Ssam case IEEE80211_M_WDS: 511178354Ssam /* 512178354Ssam * WDS links must specify the bssid of the far end. 513178354Ssam * For legacy operation this is a static relationship. 514178354Ssam * For non-legacy operation the station must associate 515178354Ssam * and be authorized to pass traffic. Plumbing the 516178354Ssam * vap to the proper node happens when the vap 517178354Ssam * transitions to RUN state. 518178354Ssam */ 519178354Ssam IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); 520178354Ssam vap->iv_flags |= IEEE80211_F_DESBSSID; 521178354Ssam if (flags & IEEE80211_CLONE_WDSLEGACY) 522178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; 523178354Ssam break; 524186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 525186904Ssam case IEEE80211_M_AHDEMO: 526186904Ssam if (flags & IEEE80211_CLONE_TDMA) { 527186904Ssam /* NB: checked before clone operation allowed */ 528186904Ssam KASSERT(ic->ic_caps & IEEE80211_C_TDMA, 529186904Ssam ("not TDMA capable, ic_caps 0x%x", ic->ic_caps)); 530186904Ssam /* 531186904Ssam * Propagate TDMA capability to mark vap; this 532186904Ssam * cannot be removed and is used to distinguish 533186904Ssam * regular ahdemo operation from ahdemo+tdma. 534186904Ssam */ 535186904Ssam vap->iv_caps |= IEEE80211_C_TDMA; 536186904Ssam } 537186904Ssam break; 538186904Ssam#endif 539228621Sbschmidt default: 540228621Sbschmidt break; 541178354Ssam } 542184278Ssam /* auto-enable s/w beacon miss support */ 543184278Ssam if (flags & IEEE80211_CLONE_NOBEACONS) 544184278Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; 545202612Sthompsa /* auto-generated or user supplied MAC address */ 546202612Sthompsa if (flags & (IEEE80211_CLONE_BSSID|IEEE80211_CLONE_MACADDR)) 547202612Sthompsa vap->iv_flags_ext |= IEEE80211_FEXT_UNIQMAC; 548178354Ssam /* 549178354Ssam * Enable various functionality by default if we're 550178354Ssam * capable; the driver can override us if it knows better. 551178354Ssam */ 552178354Ssam if (vap->iv_caps & IEEE80211_C_WME) 553178354Ssam vap->iv_flags |= IEEE80211_F_WME; 554178354Ssam if (vap->iv_caps & IEEE80211_C_BURST) 555178354Ssam vap->iv_flags |= IEEE80211_F_BURST; 556178354Ssam /* NB: bg scanning only makes sense for station mode right now */ 557178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 558178354Ssam (vap->iv_caps & IEEE80211_C_BGSCAN)) 559178354Ssam vap->iv_flags |= IEEE80211_F_BGSCAN; 560178957Ssam vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ 561178954Ssam /* NB: DFS support only makes sense for ap mode right now */ 562178954Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 563178954Ssam (vap->iv_caps & IEEE80211_C_DFS)) 564178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_DFS; 565178354Ssam 566178354Ssam vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 567178354Ssam vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 568178354Ssam vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; 569178354Ssam /* 570178354Ssam * Install a default reset method for the ioctl support; 571178354Ssam * the driver can override this. 572178354Ssam */ 573178354Ssam vap->iv_reset = default_reset; 574178354Ssam 575178354Ssam ieee80211_sysctl_vattach(vap); 576178354Ssam ieee80211_crypto_vattach(vap); 577178354Ssam ieee80211_node_vattach(vap); 578178354Ssam ieee80211_power_vattach(vap); 579178354Ssam ieee80211_proto_vattach(vap); 580190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 581190391Ssam ieee80211_superg_vattach(vap); 582190391Ssam#endif 583178354Ssam ieee80211_ht_vattach(vap); 584178354Ssam ieee80211_scan_vattach(vap); 585178354Ssam ieee80211_regdomain_vattach(vap); 586192468Ssam ieee80211_radiotap_vattach(vap); 587214894Sbschmidt ieee80211_ratectl_set(vap, IEEE80211_RATECTL_NONE); 588178354Ssam 589178354Ssam return 0; 590178354Ssam} 591178354Ssam 592178354Ssam/* 593178354Ssam * Activate a vap. State should have been prepared with a 594178354Ssam * call to ieee80211_vap_setup and by the driver. On return 595178354Ssam * from this call the vap is ready for use. 596178354Ssam */ 597178354Ssamint 598287197Sglebiusieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, 599287197Sglebius ifm_stat_cb_t media_stat, const uint8_t macaddr[IEEE80211_ADDR_LEN]) 600178354Ssam{ 601178354Ssam struct ifnet *ifp = vap->iv_ifp; 602178354Ssam struct ieee80211com *ic = vap->iv_ic; 603178354Ssam struct ifmediareq imr; 604178354Ssam int maxrate; 605178354Ssam 606178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 607178354Ssam "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", 608178354Ssam __func__, ieee80211_opmode_name[vap->iv_opmode], 609283529Sglebius ic->ic_name, vap->iv_flags, vap->iv_flags_ext); 610178354Ssam 611178354Ssam /* 612178354Ssam * Do late attach work that cannot happen until after 613178354Ssam * the driver has had a chance to override defaults. 614178354Ssam */ 615178354Ssam ieee80211_node_latevattach(vap); 616178354Ssam ieee80211_power_latevattach(vap); 617178354Ssam 618178354Ssam maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, 619178354Ssam vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); 620178354Ssam ieee80211_media_status(ifp, &imr); 621178354Ssam /* NB: strip explicit mode; we're actually in autoselect */ 622188106Ssam ifmedia_set(&vap->iv_media, 623188106Ssam imr.ifm_active &~ (IFM_MMASK | IFM_IEEE80211_TURBO)); 624178354Ssam if (maxrate) 625178354Ssam ifp->if_baudrate = IF_Mbps(maxrate); 626178354Ssam 627287197Sglebius ether_ifattach(ifp, macaddr); 628297592Savos IEEE80211_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp)); 629269778Sadrian /* hook output method setup by ether_ifattach */ 630269778Sadrian vap->iv_output = ifp->if_output; 631269778Sadrian ifp->if_output = ieee80211_output; 632178354Ssam /* NB: if_mtu set by ether_ifattach to ETHERMTU */ 633178354Ssam 634178354Ssam IEEE80211_LOCK(ic); 635178354Ssam TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); 636178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 637190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 638178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 639190391Ssam#endif 640178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 641178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 642193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); 643193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); 644178354Ssam IEEE80211_UNLOCK(ic); 645178354Ssam 646178354Ssam return 1; 647178354Ssam} 648178354Ssam 649337950Skevans/* 650178354Ssam * Tear down vap state and reclaim the ifnet. 651178354Ssam * The driver is assumed to have prepared for 652178354Ssam * this; e.g. by turning off interrupts for the 653178354Ssam * underlying device. 654178354Ssam */ 655178354Ssamvoid 656178354Ssamieee80211_vap_detach(struct ieee80211vap *vap) 657178354Ssam{ 658178354Ssam struct ieee80211com *ic = vap->iv_ic; 659178354Ssam struct ifnet *ifp = vap->iv_ifp; 660178354Ssam 661242149Sadrian CURVNET_SET(ifp->if_vnet); 662242149Sadrian 663178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", 664283565Sglebius __func__, ieee80211_opmode_name[vap->iv_opmode], ic->ic_name); 665178354Ssam 666193312Ssam /* NB: bpfdetach is called by ether_ifdetach and claims all taps */ 667193312Ssam ether_ifdetach(ifp); 668178354Ssam 669193312Ssam ieee80211_stop(vap); 670193312Ssam 671191746Sthompsa /* 672191746Sthompsa * Flush any deferred vap tasks. 673191746Sthompsa */ 674191746Sthompsa ieee80211_draintask(ic, &vap->iv_nstate_task); 675191746Sthompsa ieee80211_draintask(ic, &vap->iv_swbmiss_task); 676191746Sthompsa 677196159Ssam /* XXX band-aid until ifnet handles this for us */ 678196159Ssam taskqueue_drain(taskqueue_swi, &ifp->if_linktask); 679196159Ssam 680191746Sthompsa IEEE80211_LOCK(ic); 681191746Sthompsa KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running")); 682178354Ssam TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); 683178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_WME); 684190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 685178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); 686190391Ssam#endif 687178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); 688178354Ssam ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); 689193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); 690193655Ssam ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); 691192468Ssam /* NB: this handles the bpfdetach done below */ 692192468Ssam ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); 693287197Sglebius if (vap->iv_ifflags & IFF_PROMISC) 694287197Sglebius ieee80211_promisc(vap, false); 695287197Sglebius if (vap->iv_ifflags & IFF_ALLMULTI) 696287197Sglebius ieee80211_allmulti(vap, false); 697178354Ssam IEEE80211_UNLOCK(ic); 698178354Ssam 699178354Ssam ifmedia_removeall(&vap->iv_media); 700178354Ssam 701192468Ssam ieee80211_radiotap_vdetach(vap); 702178354Ssam ieee80211_regdomain_vdetach(vap); 703178354Ssam ieee80211_scan_vdetach(vap); 704190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 705190391Ssam ieee80211_superg_vdetach(vap); 706190391Ssam#endif 707178354Ssam ieee80211_ht_vdetach(vap); 708178354Ssam /* NB: must be before ieee80211_node_vdetach */ 709178354Ssam ieee80211_proto_vdetach(vap); 710178354Ssam ieee80211_crypto_vdetach(vap); 711178354Ssam ieee80211_power_vdetach(vap); 712178354Ssam ieee80211_node_vdetach(vap); 713178354Ssam ieee80211_sysctl_vdetach(vap); 714182674Sweongyo 715182674Sweongyo if_free(ifp); 716242149Sadrian 717242149Sadrian CURVNET_RESTORE(); 718116742Ssam} 719116742Ssam 720178354Ssam/* 721287197Sglebius * Count number of vaps in promisc, and issue promisc on 722287197Sglebius * parent respectively. 723178354Ssam */ 724178354Ssamvoid 725287197Sglebiusieee80211_promisc(struct ieee80211vap *vap, bool on) 726178354Ssam{ 727287197Sglebius struct ieee80211com *ic = vap->iv_ic; 728178354Ssam 729296170Savos IEEE80211_LOCK_ASSERT(ic); 730296170Savos 731287197Sglebius if (on) { 732287197Sglebius if (++ic->ic_promisc == 1) 733287197Sglebius ieee80211_runtask(ic, &ic->ic_promisc_task); 734287197Sglebius } else { 735287197Sglebius KASSERT(ic->ic_promisc > 0, ("%s: ic %p not promisc", 736287197Sglebius __func__, ic)); 737287197Sglebius if (--ic->ic_promisc == 0) 738287197Sglebius ieee80211_runtask(ic, &ic->ic_promisc_task); 739178354Ssam } 740178354Ssam} 741178354Ssam 742178354Ssam/* 743287197Sglebius * Count number of vaps in allmulti, and issue allmulti on 744287197Sglebius * parent respectively. 745287197Sglebius */ 746287197Sglebiusvoid 747287197Sglebiusieee80211_allmulti(struct ieee80211vap *vap, bool on) 748287197Sglebius{ 749287197Sglebius struct ieee80211com *ic = vap->iv_ic; 750287197Sglebius 751296170Savos IEEE80211_LOCK_ASSERT(ic); 752296170Savos 753287197Sglebius if (on) { 754287197Sglebius if (++ic->ic_allmulti == 1) 755287197Sglebius ieee80211_runtask(ic, &ic->ic_mcast_task); 756287197Sglebius } else { 757287197Sglebius KASSERT(ic->ic_allmulti > 0, ("%s: ic %p not allmulti", 758287197Sglebius __func__, ic)); 759287197Sglebius if (--ic->ic_allmulti == 0) 760287197Sglebius ieee80211_runtask(ic, &ic->ic_mcast_task); 761287197Sglebius } 762287197Sglebius} 763287197Sglebius 764287197Sglebius/* 765178354Ssam * Synchronize flag bit state in the com structure 766178354Ssam * according to the state of all vap's. This is used, 767178354Ssam * for example, to handle state changes via ioctls. 768178354Ssam */ 769178354Ssamstatic void 770178354Ssamieee80211_syncflag_locked(struct ieee80211com *ic, int flag) 771178354Ssam{ 772178354Ssam struct ieee80211vap *vap; 773178354Ssam int bit; 774178354Ssam 775178354Ssam IEEE80211_LOCK_ASSERT(ic); 776178354Ssam 777178354Ssam bit = 0; 778178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 779178354Ssam if (vap->iv_flags & flag) { 780178354Ssam bit = 1; 781178354Ssam break; 782178354Ssam } 783178354Ssam if (bit) 784178354Ssam ic->ic_flags |= flag; 785178354Ssam else 786178354Ssam ic->ic_flags &= ~flag; 787178354Ssam} 788178354Ssam 789178354Ssamvoid 790178354Ssamieee80211_syncflag(struct ieee80211vap *vap, int flag) 791178354Ssam{ 792178354Ssam struct ieee80211com *ic = vap->iv_ic; 793178354Ssam 794178354Ssam IEEE80211_LOCK(ic); 795178354Ssam if (flag < 0) { 796178354Ssam flag = -flag; 797178354Ssam vap->iv_flags &= ~flag; 798178354Ssam } else 799178354Ssam vap->iv_flags |= flag; 800178354Ssam ieee80211_syncflag_locked(ic, flag); 801178354Ssam IEEE80211_UNLOCK(ic); 802178354Ssam} 803178354Ssam 804178354Ssam/* 805193655Ssam * Synchronize flags_ht bit state in the com structure 806178354Ssam * according to the state of all vap's. This is used, 807178354Ssam * for example, to handle state changes via ioctls. 808178354Ssam */ 809178354Ssamstatic void 810193655Ssamieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag) 811193655Ssam{ 812193655Ssam struct ieee80211vap *vap; 813193655Ssam int bit; 814193655Ssam 815193655Ssam IEEE80211_LOCK_ASSERT(ic); 816193655Ssam 817193655Ssam bit = 0; 818193655Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 819193655Ssam if (vap->iv_flags_ht & flag) { 820193655Ssam bit = 1; 821193655Ssam break; 822193655Ssam } 823193655Ssam if (bit) 824193655Ssam ic->ic_flags_ht |= flag; 825193655Ssam else 826193655Ssam ic->ic_flags_ht &= ~flag; 827193655Ssam} 828193655Ssam 829193655Ssamvoid 830193655Ssamieee80211_syncflag_ht(struct ieee80211vap *vap, int flag) 831193655Ssam{ 832193655Ssam struct ieee80211com *ic = vap->iv_ic; 833193655Ssam 834193655Ssam IEEE80211_LOCK(ic); 835193655Ssam if (flag < 0) { 836193655Ssam flag = -flag; 837193655Ssam vap->iv_flags_ht &= ~flag; 838193655Ssam } else 839193655Ssam vap->iv_flags_ht |= flag; 840193655Ssam ieee80211_syncflag_ht_locked(ic, flag); 841193655Ssam IEEE80211_UNLOCK(ic); 842193655Ssam} 843193655Ssam 844193655Ssam/* 845193655Ssam * Synchronize flags_ext bit state in the com structure 846193655Ssam * according to the state of all vap's. This is used, 847193655Ssam * for example, to handle state changes via ioctls. 848193655Ssam */ 849193655Ssamstatic void 850178354Ssamieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) 851178354Ssam{ 852178354Ssam struct ieee80211vap *vap; 853178354Ssam int bit; 854178354Ssam 855178354Ssam IEEE80211_LOCK_ASSERT(ic); 856178354Ssam 857178354Ssam bit = 0; 858178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 859178354Ssam if (vap->iv_flags_ext & flag) { 860178354Ssam bit = 1; 861178354Ssam break; 862178354Ssam } 863178354Ssam if (bit) 864178354Ssam ic->ic_flags_ext |= flag; 865178354Ssam else 866178354Ssam ic->ic_flags_ext &= ~flag; 867178354Ssam} 868178354Ssam 869178354Ssamvoid 870178354Ssamieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) 871178354Ssam{ 872178354Ssam struct ieee80211com *ic = vap->iv_ic; 873178354Ssam 874178354Ssam IEEE80211_LOCK(ic); 875178354Ssam if (flag < 0) { 876178354Ssam flag = -flag; 877178354Ssam vap->iv_flags_ext &= ~flag; 878178354Ssam } else 879178354Ssam vap->iv_flags_ext |= flag; 880178354Ssam ieee80211_syncflag_ext_locked(ic, flag); 881178354Ssam IEEE80211_UNLOCK(ic); 882178354Ssam} 883178354Ssam 884166012Ssamstatic __inline int 885166012Ssammapgsm(u_int freq, u_int flags) 886166012Ssam{ 887166012Ssam freq *= 10; 888166012Ssam if (flags & IEEE80211_CHAN_QUARTER) 889166012Ssam freq += 5; 890166012Ssam else if (flags & IEEE80211_CHAN_HALF) 891166012Ssam freq += 10; 892166012Ssam else 893166012Ssam freq += 20; 894166012Ssam /* NB: there is no 907/20 wide but leave room */ 895166012Ssam return (freq - 906*10) / 5; 896166012Ssam} 897166012Ssam 898166012Ssamstatic __inline int 899166012Ssammappsb(u_int freq, u_int flags) 900166012Ssam{ 901166012Ssam return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; 902166012Ssam} 903166012Ssam 904116742Ssam/* 905116742Ssam * Convert MHz frequency to IEEE channel number. 906116742Ssam */ 907152450Ssamint 908116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags) 909116742Ssam{ 910167430Ssam#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) 911166012Ssam if (flags & IEEE80211_CHAN_GSM) 912166012Ssam return mapgsm(freq, flags); 913116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 914116742Ssam if (freq == 2484) 915116742Ssam return 14; 916116742Ssam if (freq < 2484) 917152450Ssam return ((int) freq - 2407) / 5; 918116742Ssam else 919116742Ssam return 15 + ((freq - 2512) / 20); 920116899Ssam } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 921165569Ssam if (freq <= 5000) { 922170530Ssam /* XXX check regdomain? */ 923167430Ssam if (IS_FREQ_IN_PSB(freq)) 924166012Ssam return mappsb(freq, flags); 925152450Ssam return (freq - 4000) / 5; 926165569Ssam } else 927152450Ssam return (freq - 5000) / 5; 928116742Ssam } else { /* either, guess */ 929116742Ssam if (freq == 2484) 930116742Ssam return 14; 931166012Ssam if (freq < 2484) { 932166012Ssam if (907 <= freq && freq <= 922) 933166012Ssam return mapgsm(freq, flags); 934152450Ssam return ((int) freq - 2407) / 5; 935166012Ssam } 936152450Ssam if (freq < 5000) { 937167430Ssam if (IS_FREQ_IN_PSB(freq)) 938166012Ssam return mappsb(freq, flags); 939165569Ssam else if (freq > 4900) 940152450Ssam return (freq - 4000) / 5; 941152450Ssam else 942152450Ssam return 15 + ((freq - 2512) / 20); 943152450Ssam } 944116742Ssam return (freq - 5000) / 5; 945116742Ssam } 946167430Ssam#undef IS_FREQ_IN_PSB 947116742Ssam} 948116742Ssam 949116742Ssam/* 950116742Ssam * Convert channel to IEEE channel number. 951116742Ssam */ 952152450Ssamint 953165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) 954116742Ssam{ 955170530Ssam if (c == NULL) { 956283529Sglebius ic_printf(ic, "invalid channel (NULL)\n"); 957117039Ssam return 0; /* XXX */ 958116742Ssam } 959170530Ssam return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); 960116742Ssam} 961116742Ssam 962116742Ssam/* 963116742Ssam * Convert IEEE channel number to MHz frequency. 964116742Ssam */ 965116742Ssamu_int 966116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags) 967116742Ssam{ 968166012Ssam if (flags & IEEE80211_CHAN_GSM) 969166012Ssam return 907 + 5 * (chan / 10); 970116742Ssam if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 971116742Ssam if (chan == 14) 972116742Ssam return 2484; 973116742Ssam if (chan < 14) 974116742Ssam return 2407 + chan*5; 975116742Ssam else 976116742Ssam return 2512 + ((chan-15)*20); 977116742Ssam } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ 978165569Ssam if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { 979165569Ssam chan -= 37; 980165569Ssam return 4940 + chan*5 + (chan % 5 ? 2 : 0); 981165569Ssam } 982116742Ssam return 5000 + (chan*5); 983116742Ssam } else { /* either, guess */ 984166012Ssam /* XXX can't distinguish PSB+GSM channels */ 985116742Ssam if (chan == 14) 986116742Ssam return 2484; 987116742Ssam if (chan < 14) /* 0-13 */ 988116742Ssam return 2407 + chan*5; 989116742Ssam if (chan < 27) /* 15-26 */ 990116742Ssam return 2512 + ((chan-15)*20); 991116742Ssam return 5000 + (chan*5); 992116742Ssam } 993116742Ssam} 994116742Ssam 995298812Savosstatic __inline void 996298812Savosset_extchan(struct ieee80211_channel *c) 997298812Savos{ 998298812Savos 999298812Savos /* 1000298812Savos * IEEE Std 802.11-2012, page 1738, subclause 20.3.15.4: 1001298812Savos * "the secondary channel number shall be 'N + [1,-1] * 4' 1002298812Savos */ 1003298812Savos if (c->ic_flags & IEEE80211_CHAN_HT40U) 1004298812Savos c->ic_extieee = c->ic_ieee + 4; 1005298812Savos else if (c->ic_flags & IEEE80211_CHAN_HT40D) 1006298812Savos c->ic_extieee = c->ic_ieee - 4; 1007298812Savos else 1008298812Savos c->ic_extieee = 0; 1009298812Savos} 1010298812Savos 1011298812Savosstatic int 1012298812Savosaddchan(struct ieee80211_channel chans[], int maxchans, int *nchans, 1013298812Savos uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags) 1014298812Savos{ 1015298812Savos struct ieee80211_channel *c; 1016298812Savos 1017298812Savos if (*nchans >= maxchans) 1018298812Savos return (ENOBUFS); 1019298812Savos 1020298812Savos c = &chans[(*nchans)++]; 1021298812Savos c->ic_ieee = ieee; 1022298812Savos c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags); 1023298812Savos c->ic_maxregpower = maxregpower; 1024298812Savos c->ic_maxpower = 2 * maxregpower; 1025298812Savos c->ic_flags = flags; 1026298812Savos set_extchan(c); 1027298812Savos 1028298812Savos return (0); 1029298812Savos} 1030298812Savos 1031298812Savosstatic int 1032298812Savoscopychan_prev(struct ieee80211_channel chans[], int maxchans, int *nchans, 1033298812Savos uint32_t flags) 1034298812Savos{ 1035298812Savos struct ieee80211_channel *c; 1036298812Savos 1037298812Savos KASSERT(*nchans > 0, ("channel list is empty\n")); 1038298812Savos 1039298812Savos if (*nchans >= maxchans) 1040298812Savos return (ENOBUFS); 1041298812Savos 1042298812Savos c = &chans[(*nchans)++]; 1043298812Savos c[0] = c[-1]; 1044298812Savos c->ic_flags = flags; 1045298812Savos set_extchan(c); 1046298812Savos 1047298812Savos return (0); 1048298812Savos} 1049298812Savos 1050298812Savosstatic void 1051298812Savosgetflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40) 1052298812Savos{ 1053298812Savos int nmodes; 1054298812Savos 1055298812Savos nmodes = 0; 1056298812Savos if (isset(bands, IEEE80211_MODE_11B)) 1057298812Savos flags[nmodes++] = IEEE80211_CHAN_B; 1058298812Savos if (isset(bands, IEEE80211_MODE_11G)) 1059298812Savos flags[nmodes++] = IEEE80211_CHAN_G; 1060298812Savos if (isset(bands, IEEE80211_MODE_11NG)) 1061298812Savos flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT20; 1062298812Savos if (ht40) { 1063298812Savos flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U; 1064298812Savos flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D; 1065298812Savos } 1066298812Savos flags[nmodes] = 0; 1067298812Savos} 1068298812Savos 1069298812Savosstatic void 1070298812Savosgetflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40) 1071298812Savos{ 1072298812Savos int nmodes; 1073298812Savos 1074298812Savos nmodes = 0; 1075298812Savos if (isset(bands, IEEE80211_MODE_11A)) 1076298812Savos flags[nmodes++] = IEEE80211_CHAN_A; 1077298812Savos if (isset(bands, IEEE80211_MODE_11NA)) 1078298812Savos flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; 1079298812Savos if (ht40) { 1080298812Savos flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U; 1081298812Savos flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D; 1082298812Savos } 1083298812Savos flags[nmodes] = 0; 1084298812Savos} 1085298812Savos 1086298812Savosstatic void 1087298812Savosgetflags(const uint8_t bands[], uint32_t flags[], int ht40) 1088298812Savos{ 1089298812Savos 1090298812Savos flags[0] = 0; 1091298812Savos if (isset(bands, IEEE80211_MODE_11A) || 1092298812Savos isset(bands, IEEE80211_MODE_11NA)) { 1093298812Savos if (isset(bands, IEEE80211_MODE_11B) || 1094298812Savos isset(bands, IEEE80211_MODE_11G) || 1095298812Savos isset(bands, IEEE80211_MODE_11NG)) 1096298812Savos return; 1097298812Savos 1098298812Savos getflags_5ghz(bands, flags, ht40); 1099298812Savos } else 1100298812Savos getflags_2ghz(bands, flags, ht40); 1101298812Savos} 1102298812Savos 1103116742Ssam/* 1104298812Savos * Add one 20 MHz channel into specified channel list. 1105298812Savos */ 1106298812Savosint 1107298812Savosieee80211_add_channel(struct ieee80211_channel chans[], int maxchans, 1108298812Savos int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, 1109298812Savos uint32_t chan_flags, const uint8_t bands[]) 1110298812Savos{ 1111298812Savos uint32_t flags[IEEE80211_MODE_MAX]; 1112298812Savos int i, error; 1113298812Savos 1114298812Savos getflags(bands, flags, 0); 1115298812Savos KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); 1116298812Savos 1117298812Savos error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower, 1118298812Savos flags[0] | chan_flags); 1119298812Savos for (i = 1; flags[i] != 0 && error == 0; i++) { 1120298812Savos error = copychan_prev(chans, maxchans, nchans, 1121298812Savos flags[i] | chan_flags); 1122298812Savos } 1123298812Savos 1124298812Savos return (error); 1125298812Savos} 1126298812Savos 1127298812Savosstatic struct ieee80211_channel * 1128298812Savosfindchannel(struct ieee80211_channel chans[], int nchans, uint16_t freq, 1129298812Savos uint32_t flags) 1130298812Savos{ 1131298812Savos struct ieee80211_channel *c; 1132298812Savos int i; 1133298812Savos 1134298812Savos flags &= IEEE80211_CHAN_ALLTURBO; 1135298812Savos /* brute force search */ 1136298812Savos for (i = 0; i < nchans; i++) { 1137298812Savos c = &chans[i]; 1138298812Savos if (c->ic_freq == freq && 1139298812Savos (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 1140298812Savos return c; 1141298812Savos } 1142298812Savos return NULL; 1143298812Savos} 1144298812Savos 1145298812Savos/* 1146298812Savos * Add 40 MHz channel pair into specified channel list. 1147298812Savos */ 1148298812Savosint 1149298812Savosieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans, 1150298812Savos int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags) 1151298812Savos{ 1152298812Savos struct ieee80211_channel *cent, *extc; 1153298812Savos uint16_t freq; 1154298812Savos int error; 1155298812Savos 1156298812Savos freq = ieee80211_ieee2mhz(ieee, flags); 1157298812Savos 1158298812Savos /* 1159298812Savos * Each entry defines an HT40 channel pair; find the 1160298812Savos * center channel, then the extension channel above. 1161298812Savos */ 1162298812Savos flags |= IEEE80211_CHAN_HT20; 1163298812Savos cent = findchannel(chans, *nchans, freq, flags); 1164298812Savos if (cent == NULL) 1165298812Savos return (EINVAL); 1166298812Savos 1167298812Savos extc = findchannel(chans, *nchans, freq + 20, flags); 1168298812Savos if (extc == NULL) 1169298812Savos return (ENOENT); 1170298812Savos 1171298812Savos flags &= ~IEEE80211_CHAN_HT; 1172298812Savos error = addchan(chans, maxchans, nchans, cent->ic_ieee, cent->ic_freq, 1173298812Savos maxregpower, flags | IEEE80211_CHAN_HT40U); 1174298812Savos if (error != 0) 1175298812Savos return (error); 1176298812Savos 1177298812Savos error = addchan(chans, maxchans, nchans, extc->ic_ieee, extc->ic_freq, 1178298812Savos maxregpower, flags | IEEE80211_CHAN_HT40D); 1179298812Savos 1180298812Savos return (error); 1181298812Savos} 1182298812Savos 1183298812Savos/* 1184298812Savos * Adds channels into specified channel list (ieee[] array must be sorted). 1185298812Savos * Channels are already sorted. 1186298812Savos */ 1187298812Savosstatic int 1188298812Savosadd_chanlist(struct ieee80211_channel chans[], int maxchans, int *nchans, 1189298812Savos const uint8_t ieee[], int nieee, uint32_t flags[]) 1190298812Savos{ 1191298812Savos uint16_t freq; 1192298812Savos int i, j, error; 1193298812Savos 1194298812Savos for (i = 0; i < nieee; i++) { 1195298812Savos freq = ieee80211_ieee2mhz(ieee[i], flags[0]); 1196298812Savos for (j = 0; flags[j] != 0; j++) { 1197298812Savos if (flags[j] & IEEE80211_CHAN_HT40D) 1198298812Savos if (i == 0 || ieee[i] < ieee[0] + 4 || 1199298812Savos freq - 20 != 1200298812Savos ieee80211_ieee2mhz(ieee[i] - 4, flags[j])) 1201298812Savos continue; 1202298812Savos if (flags[j] & IEEE80211_CHAN_HT40U) 1203298812Savos if (i == nieee - 1 || 1204298812Savos ieee[i] + 4 > ieee[nieee - 1] || 1205298812Savos freq + 20 != 1206298812Savos ieee80211_ieee2mhz(ieee[i] + 4, flags[j])) 1207298812Savos continue; 1208298812Savos 1209298812Savos if (j == 0) { 1210298812Savos error = addchan(chans, maxchans, nchans, 1211298812Savos ieee[i], freq, 0, flags[j]); 1212298812Savos } else { 1213298812Savos error = copychan_prev(chans, maxchans, nchans, 1214298812Savos flags[j]); 1215298812Savos } 1216298812Savos if (error != 0) 1217298812Savos return (error); 1218298812Savos } 1219298812Savos } 1220298812Savos 1221300232Savos return (0); 1222298812Savos} 1223298812Savos 1224298812Savosint 1225298812Savosieee80211_add_channel_list_2ghz(struct ieee80211_channel chans[], int maxchans, 1226298812Savos int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], 1227298812Savos int ht40) 1228298812Savos{ 1229298812Savos uint32_t flags[IEEE80211_MODE_MAX]; 1230298812Savos 1231298812Savos getflags_2ghz(bands, flags, ht40); 1232298812Savos KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); 1233298812Savos 1234298812Savos return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); 1235298812Savos} 1236298812Savos 1237298812Savosint 1238343976Savosieee80211_add_channels_default_2ghz(struct ieee80211_channel chans[], 1239343976Savos int maxchans, int *nchans, const uint8_t bands[], int ht40) 1240343976Savos{ 1241343976Savos const uint8_t default_chan_list[] = 1242343976Savos { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; 1243343976Savos 1244343976Savos return (ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, 1245343976Savos default_chan_list, nitems(default_chan_list), bands, ht40)); 1246343976Savos} 1247343976Savos 1248343976Savosint 1249298812Savosieee80211_add_channel_list_5ghz(struct ieee80211_channel chans[], int maxchans, 1250298812Savos int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], 1251298812Savos int ht40) 1252298812Savos{ 1253298812Savos uint32_t flags[IEEE80211_MODE_MAX]; 1254298812Savos 1255298812Savos getflags_5ghz(bands, flags, ht40); 1256298812Savos KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); 1257298812Savos 1258298812Savos return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); 1259298812Savos} 1260298812Savos 1261298812Savos/* 1262170530Ssam * Locate a channel given a frequency+flags. We cache 1263178354Ssam * the previous lookup to optimize switching between two 1264170530Ssam * channels--as happens with dynamic turbo. 1265170530Ssam */ 1266170530Ssamstruct ieee80211_channel * 1267170530Ssamieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) 1268170530Ssam{ 1269170530Ssam struct ieee80211_channel *c; 1270170530Ssam 1271170530Ssam flags &= IEEE80211_CHAN_ALLTURBO; 1272170530Ssam c = ic->ic_prevchan; 1273170530Ssam if (c != NULL && c->ic_freq == freq && 1274170530Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 1275170530Ssam return c; 1276170530Ssam /* brute force search */ 1277298812Savos return (findchannel(ic->ic_channels, ic->ic_nchans, freq, flags)); 1278170530Ssam} 1279170530Ssam 1280173861Ssam/* 1281173861Ssam * Locate a channel given a channel number+flags. We cache 1282173861Ssam * the previous lookup to optimize switching between two 1283173861Ssam * channels--as happens with dynamic turbo. 1284173861Ssam */ 1285173861Ssamstruct ieee80211_channel * 1286173861Ssamieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) 1287173861Ssam{ 1288173861Ssam struct ieee80211_channel *c; 1289173861Ssam int i; 1290173861Ssam 1291173861Ssam flags &= IEEE80211_CHAN_ALLTURBO; 1292173861Ssam c = ic->ic_prevchan; 1293173861Ssam if (c != NULL && c->ic_ieee == ieee && 1294173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 1295173861Ssam return c; 1296173861Ssam /* brute force search */ 1297173861Ssam for (i = 0; i < ic->ic_nchans; i++) { 1298173861Ssam c = &ic->ic_channels[i]; 1299173861Ssam if (c->ic_ieee == ieee && 1300173861Ssam (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) 1301173861Ssam return c; 1302173861Ssam } 1303173861Ssam return NULL; 1304173861Ssam} 1305173861Ssam 1306283535Sadrian/* 1307283535Sadrian * Lookup a channel suitable for the given rx status. 1308283535Sadrian * 1309283535Sadrian * This is used to find a channel for a frame (eg beacon, probe 1310283535Sadrian * response) based purely on the received PHY information. 1311283535Sadrian * 1312283535Sadrian * For now it tries to do it based on R_FREQ / R_IEEE. 1313283535Sadrian * This is enough for 11bg and 11a (and thus 11ng/11na) 1314283535Sadrian * but it will not be enough for GSM, PSB channels and the 1315283535Sadrian * like. It also doesn't know about legacy-turbog and 1316283535Sadrian * legacy-turbo modes, which some offload NICs actually 1317283535Sadrian * support in weird ways. 1318283535Sadrian * 1319283535Sadrian * Takes the ic and rxstatus; returns the channel or NULL 1320283535Sadrian * if not found. 1321283535Sadrian * 1322283535Sadrian * XXX TODO: Add support for that when the need arises. 1323283535Sadrian */ 1324283535Sadrianstruct ieee80211_channel * 1325283535Sadrianieee80211_lookup_channel_rxstatus(struct ieee80211vap *vap, 1326283535Sadrian const struct ieee80211_rx_stats *rxs) 1327283535Sadrian{ 1328283535Sadrian struct ieee80211com *ic = vap->iv_ic; 1329283535Sadrian uint32_t flags; 1330283535Sadrian struct ieee80211_channel *c; 1331283535Sadrian 1332283535Sadrian if (rxs == NULL) 1333283535Sadrian return (NULL); 1334283535Sadrian 1335283535Sadrian /* 1336283535Sadrian * Strictly speaking we only use freq for now, 1337283535Sadrian * however later on we may wish to just store 1338283535Sadrian * the ieee for verification. 1339283535Sadrian */ 1340283535Sadrian if ((rxs->r_flags & IEEE80211_R_FREQ) == 0) 1341283535Sadrian return (NULL); 1342283535Sadrian if ((rxs->r_flags & IEEE80211_R_IEEE) == 0) 1343283535Sadrian return (NULL); 1344283535Sadrian 1345283535Sadrian /* 1346283535Sadrian * If the rx status contains a valid ieee/freq, then 1347283535Sadrian * ensure we populate the correct channel information 1348283535Sadrian * in rxchan before passing it up to the scan infrastructure. 1349283535Sadrian * Offload NICs will pass up beacons from all channels 1350283535Sadrian * during background scans. 1351283535Sadrian */ 1352283535Sadrian 1353283535Sadrian /* Determine a band */ 1354283535Sadrian /* XXX should be done by the driver? */ 1355283535Sadrian if (rxs->c_freq < 3000) { 1356284014Sadrian flags = IEEE80211_CHAN_G; 1357283535Sadrian } else { 1358283535Sadrian flags = IEEE80211_CHAN_A; 1359283535Sadrian } 1360283535Sadrian 1361283535Sadrian /* Channel lookup */ 1362283535Sadrian c = ieee80211_find_channel(ic, rxs->c_freq, flags); 1363283535Sadrian 1364283535Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_INPUT, 1365283535Sadrian "%s: freq=%d, ieee=%d, flags=0x%08x; c=%p\n", 1366283535Sadrian __func__, 1367283535Sadrian (int) rxs->c_freq, 1368283535Sadrian (int) rxs->c_ieee, 1369283535Sadrian flags, 1370283535Sadrian c); 1371283535Sadrian 1372283535Sadrian return (c); 1373283535Sadrian} 1374283535Sadrian 1375170530Ssamstatic void 1376178354Ssamaddmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) 1377170530Ssam{ 1378170530Ssam#define ADD(_ic, _s, _o) \ 1379178354Ssam ifmedia_add(media, \ 1380170530Ssam IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) 1381337950Skevans static const u_int mopts[IEEE80211_MODE_MAX] = { 1382188106Ssam [IEEE80211_MODE_AUTO] = IFM_AUTO, 1383188106Ssam [IEEE80211_MODE_11A] = IFM_IEEE80211_11A, 1384188106Ssam [IEEE80211_MODE_11B] = IFM_IEEE80211_11B, 1385188106Ssam [IEEE80211_MODE_11G] = IFM_IEEE80211_11G, 1386188106Ssam [IEEE80211_MODE_FH] = IFM_IEEE80211_FH, 1387188106Ssam [IEEE80211_MODE_TURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 1388188106Ssam [IEEE80211_MODE_TURBO_G] = IFM_IEEE80211_11G|IFM_IEEE80211_TURBO, 1389188106Ssam [IEEE80211_MODE_STURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, 1390188782Ssam [IEEE80211_MODE_HALF] = IFM_IEEE80211_11A, /* XXX */ 1391188782Ssam [IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */ 1392188106Ssam [IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA, 1393188106Ssam [IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG, 1394170530Ssam }; 1395170530Ssam u_int mopt; 1396170530Ssam 1397170530Ssam mopt = mopts[mode]; 1398178354Ssam if (addsta) 1399178354Ssam ADD(ic, mword, mopt); /* STA mode has no cap */ 1400178354Ssam if (caps & IEEE80211_C_IBSS) 1401178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); 1402178354Ssam if (caps & IEEE80211_C_HOSTAP) 1403178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); 1404178354Ssam if (caps & IEEE80211_C_AHDEMO) 1405178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); 1406178354Ssam if (caps & IEEE80211_C_MONITOR) 1407178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); 1408178354Ssam if (caps & IEEE80211_C_WDS) 1409178354Ssam ADD(media, mword, mopt | IFM_IEEE80211_WDS); 1410195618Srpaulo if (caps & IEEE80211_C_MBSS) 1411195618Srpaulo ADD(media, mword, mopt | IFM_IEEE80211_MBSS); 1412170530Ssam#undef ADD 1413170530Ssam} 1414170530Ssam 1415170530Ssam/* 1416116742Ssam * Setup the media data structures according to the channel and 1417178354Ssam * rate tables. 1418116742Ssam */ 1419178354Ssamstatic int 1420178354Ssamieee80211_media_setup(struct ieee80211com *ic, 1421178354Ssam struct ifmedia *media, int caps, int addsta, 1422116742Ssam ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) 1423116742Ssam{ 1424228621Sbschmidt int i, j, rate, maxrate, mword, r; 1425228621Sbschmidt enum ieee80211_phymode mode; 1426170530Ssam const struct ieee80211_rateset *rs; 1427116742Ssam struct ieee80211_rateset allrates; 1428116742Ssam 1429118887Ssam /* 1430116742Ssam * Fill in media characteristics. 1431116742Ssam */ 1432178354Ssam ifmedia_init(media, 0, media_change, media_stat); 1433116742Ssam maxrate = 0; 1434170530Ssam /* 1435170530Ssam * Add media for legacy operating modes. 1436170530Ssam */ 1437116742Ssam memset(&allrates, 0, sizeof(allrates)); 1438170530Ssam for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { 1439167468Ssam if (isclr(ic->ic_modecaps, mode)) 1440116742Ssam continue; 1441178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 1442116742Ssam if (mode == IEEE80211_MODE_AUTO) 1443116742Ssam continue; 1444116742Ssam rs = &ic->ic_sup_rates[mode]; 1445116742Ssam for (i = 0; i < rs->rs_nrates; i++) { 1446116742Ssam rate = rs->rs_rates[i]; 1447116742Ssam mword = ieee80211_rate2media(ic, rate, mode); 1448116742Ssam if (mword == 0) 1449116742Ssam continue; 1450178354Ssam addmedia(media, caps, addsta, mode, mword); 1451116742Ssam /* 1452170530Ssam * Add legacy rate to the collection of all rates. 1453116742Ssam */ 1454116742Ssam r = rate & IEEE80211_RATE_VAL; 1455116742Ssam for (j = 0; j < allrates.rs_nrates; j++) 1456116742Ssam if (allrates.rs_rates[j] == r) 1457116742Ssam break; 1458116742Ssam if (j == allrates.rs_nrates) { 1459116742Ssam /* unique, add to the set */ 1460116742Ssam allrates.rs_rates[j] = r; 1461116742Ssam allrates.rs_nrates++; 1462116742Ssam } 1463116742Ssam rate = (rate & IEEE80211_RATE_VAL) / 2; 1464116742Ssam if (rate > maxrate) 1465116742Ssam maxrate = rate; 1466116742Ssam } 1467116742Ssam } 1468116742Ssam for (i = 0; i < allrates.rs_nrates; i++) { 1469116742Ssam mword = ieee80211_rate2media(ic, allrates.rs_rates[i], 1470116742Ssam IEEE80211_MODE_AUTO); 1471116742Ssam if (mword == 0) 1472116742Ssam continue; 1473170530Ssam /* NB: remove media options from mword */ 1474178354Ssam addmedia(media, caps, addsta, 1475178354Ssam IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); 1476116742Ssam } 1477170530Ssam /* 1478170530Ssam * Add HT/11n media. Note that we do not have enough 1479170530Ssam * bits in the media subtype to express the MCS so we 1480170530Ssam * use a "placeholder" media subtype and any fixed MCS 1481170530Ssam * must be specified with a different mechanism. 1482170530Ssam */ 1483188782Ssam for (; mode <= IEEE80211_MODE_11NG; mode++) { 1484170530Ssam if (isclr(ic->ic_modecaps, mode)) 1485170530Ssam continue; 1486178354Ssam addmedia(media, caps, addsta, mode, IFM_AUTO); 1487178354Ssam addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); 1488170530Ssam } 1489170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 1490170530Ssam isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 1491178354Ssam addmedia(media, caps, addsta, 1492178354Ssam IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); 1493219599Sbschmidt i = ic->ic_txstream * 8 - 1; 1494219599Sbschmidt if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && 1495219599Sbschmidt (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) 1496219599Sbschmidt rate = ieee80211_htrates[i].ht40_rate_400ns; 1497219599Sbschmidt else if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40)) 1498219599Sbschmidt rate = ieee80211_htrates[i].ht40_rate_800ns; 1499219599Sbschmidt else if ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)) 1500219599Sbschmidt rate = ieee80211_htrates[i].ht20_rate_400ns; 1501219599Sbschmidt else 1502219599Sbschmidt rate = ieee80211_htrates[i].ht20_rate_800ns; 1503219599Sbschmidt if (rate > maxrate) 1504219599Sbschmidt maxrate = rate; 1505170530Ssam } 1506178354Ssam return maxrate; 1507178354Ssam} 1508116742Ssam 1509188782Ssam/* XXX inline or eliminate? */ 1510165569Ssamconst struct ieee80211_rateset * 1511165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) 1512165569Ssam{ 1513188774Ssam /* XXX does this work for 11ng basic rates? */ 1514170530Ssam return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; 1515165569Ssam} 1516165569Ssam 1517138568Ssamvoid 1518138568Ssamieee80211_announce(struct ieee80211com *ic) 1519138568Ssam{ 1520228621Sbschmidt int i, rate, mword; 1521228621Sbschmidt enum ieee80211_phymode mode; 1522170530Ssam const struct ieee80211_rateset *rs; 1523138568Ssam 1524172227Ssam /* NB: skip AUTO since it has no rates */ 1525172227Ssam for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { 1526167468Ssam if (isclr(ic->ic_modecaps, mode)) 1527138568Ssam continue; 1528283529Sglebius ic_printf(ic, "%s rates: ", ieee80211_phymode_name[mode]); 1529138568Ssam rs = &ic->ic_sup_rates[mode]; 1530138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 1531170530Ssam mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 1532138568Ssam if (mword == 0) 1533138568Ssam continue; 1534170530Ssam rate = ieee80211_media2rate(mword); 1535138568Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 1536170530Ssam rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 1537138568Ssam } 1538138568Ssam printf("\n"); 1539138568Ssam } 1540170530Ssam ieee80211_ht_announce(ic); 1541138568Ssam} 1542138568Ssam 1543170530Ssamvoid 1544170530Ssamieee80211_announce_channels(struct ieee80211com *ic) 1545116742Ssam{ 1546170530Ssam const struct ieee80211_channel *c; 1547170530Ssam char type; 1548170530Ssam int i, cw; 1549170530Ssam 1550170530Ssam printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); 1551170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 1552170530Ssam c = &ic->ic_channels[i]; 1553170530Ssam if (IEEE80211_IS_CHAN_ST(c)) 1554170530Ssam type = 'S'; 1555170530Ssam else if (IEEE80211_IS_CHAN_108A(c)) 1556170530Ssam type = 'T'; 1557170530Ssam else if (IEEE80211_IS_CHAN_108G(c)) 1558170530Ssam type = 'G'; 1559170530Ssam else if (IEEE80211_IS_CHAN_HT(c)) 1560170530Ssam type = 'n'; 1561170530Ssam else if (IEEE80211_IS_CHAN_A(c)) 1562170530Ssam type = 'a'; 1563170530Ssam else if (IEEE80211_IS_CHAN_ANYG(c)) 1564170530Ssam type = 'g'; 1565170530Ssam else if (IEEE80211_IS_CHAN_B(c)) 1566170530Ssam type = 'b'; 1567170530Ssam else 1568170530Ssam type = 'f'; 1569170530Ssam if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) 1570170530Ssam cw = 40; 1571170530Ssam else if (IEEE80211_IS_CHAN_HALF(c)) 1572170530Ssam cw = 10; 1573170530Ssam else if (IEEE80211_IS_CHAN_QUARTER(c)) 1574170530Ssam cw = 5; 1575170530Ssam else 1576170530Ssam cw = 20; 1577170530Ssam printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" 1578170530Ssam , c->ic_ieee, c->ic_freq, type 1579170530Ssam , cw 1580170530Ssam , IEEE80211_IS_CHAN_HT40U(c) ? '+' : 1581170530Ssam IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' 1582170530Ssam , c->ic_maxregpower 1583170530Ssam , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 1584170530Ssam , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 1585170530Ssam ); 1586170530Ssam } 1587116742Ssam} 1588116742Ssam 1589170530Ssamstatic int 1590184273Ssammedia2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) 1591170530Ssam{ 1592116742Ssam switch (IFM_MODE(ime->ifm_media)) { 1593116742Ssam case IFM_IEEE80211_11A: 1594178354Ssam *mode = IEEE80211_MODE_11A; 1595116742Ssam break; 1596116742Ssam case IFM_IEEE80211_11B: 1597178354Ssam *mode = IEEE80211_MODE_11B; 1598116742Ssam break; 1599116742Ssam case IFM_IEEE80211_11G: 1600178354Ssam *mode = IEEE80211_MODE_11G; 1601116742Ssam break; 1602124543Sonoe case IFM_IEEE80211_FH: 1603178354Ssam *mode = IEEE80211_MODE_FH; 1604124543Sonoe break; 1605170530Ssam case IFM_IEEE80211_11NA: 1606178354Ssam *mode = IEEE80211_MODE_11NA; 1607170530Ssam break; 1608170530Ssam case IFM_IEEE80211_11NG: 1609178354Ssam *mode = IEEE80211_MODE_11NG; 1610170530Ssam break; 1611116742Ssam case IFM_AUTO: 1612178354Ssam *mode = IEEE80211_MODE_AUTO; 1613116742Ssam break; 1614116742Ssam default: 1615178354Ssam return 0; 1616116742Ssam } 1617116742Ssam /* 1618138568Ssam * Turbo mode is an ``option''. 1619138568Ssam * XXX does not apply to AUTO 1620116742Ssam */ 1621116742Ssam if (ime->ifm_media & IFM_IEEE80211_TURBO) { 1622178354Ssam if (*mode == IEEE80211_MODE_11A) { 1623184273Ssam if (flags & IEEE80211_F_TURBOP) 1624178354Ssam *mode = IEEE80211_MODE_TURBO_A; 1625170530Ssam else 1626178354Ssam *mode = IEEE80211_MODE_STURBO_A; 1627178354Ssam } else if (*mode == IEEE80211_MODE_11G) 1628178354Ssam *mode = IEEE80211_MODE_TURBO_G; 1629138568Ssam else 1630178354Ssam return 0; 1631116742Ssam } 1632170530Ssam /* XXX HT40 +/- */ 1633178354Ssam return 1; 1634178354Ssam} 1635116742Ssam 1636178354Ssam/* 1637178354Ssam * Handle a media change request on the vap interface. 1638178354Ssam */ 1639178354Ssamint 1640178354Ssamieee80211_media_change(struct ifnet *ifp) 1641178354Ssam{ 1642178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1643178354Ssam struct ifmedia_entry *ime = vap->iv_media.ifm_cur; 1644184273Ssam uint16_t newmode; 1645178354Ssam 1646184273Ssam if (!media2mode(ime, vap->iv_flags, &newmode)) 1647178354Ssam return EINVAL; 1648184273Ssam if (vap->iv_des_mode != newmode) { 1649184273Ssam vap->iv_des_mode = newmode; 1650193340Ssam /* XXX kick state machine if up+running */ 1651178354Ssam } 1652178354Ssam return 0; 1653116742Ssam} 1654116742Ssam 1655170530Ssam/* 1656170530Ssam * Common code to calculate the media status word 1657170530Ssam * from the operating mode and channel state. 1658170530Ssam */ 1659170530Ssamstatic int 1660170530Ssammedia_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) 1661170530Ssam{ 1662170530Ssam int status; 1663170530Ssam 1664170530Ssam status = IFM_IEEE80211; 1665170530Ssam switch (opmode) { 1666170530Ssam case IEEE80211_M_STA: 1667170530Ssam break; 1668170530Ssam case IEEE80211_M_IBSS: 1669170530Ssam status |= IFM_IEEE80211_ADHOC; 1670170530Ssam break; 1671170530Ssam case IEEE80211_M_HOSTAP: 1672170530Ssam status |= IFM_IEEE80211_HOSTAP; 1673170530Ssam break; 1674170530Ssam case IEEE80211_M_MONITOR: 1675170530Ssam status |= IFM_IEEE80211_MONITOR; 1676170530Ssam break; 1677170530Ssam case IEEE80211_M_AHDEMO: 1678170530Ssam status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; 1679170530Ssam break; 1680170530Ssam case IEEE80211_M_WDS: 1681178354Ssam status |= IFM_IEEE80211_WDS; 1682170530Ssam break; 1683195618Srpaulo case IEEE80211_M_MBSS: 1684195618Srpaulo status |= IFM_IEEE80211_MBSS; 1685195618Srpaulo break; 1686170530Ssam } 1687170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) { 1688170530Ssam status |= IFM_IEEE80211_11NA; 1689170530Ssam } else if (IEEE80211_IS_CHAN_HTG(chan)) { 1690170530Ssam status |= IFM_IEEE80211_11NG; 1691170530Ssam } else if (IEEE80211_IS_CHAN_A(chan)) { 1692170530Ssam status |= IFM_IEEE80211_11A; 1693170530Ssam } else if (IEEE80211_IS_CHAN_B(chan)) { 1694170530Ssam status |= IFM_IEEE80211_11B; 1695170530Ssam } else if (IEEE80211_IS_CHAN_ANYG(chan)) { 1696170530Ssam status |= IFM_IEEE80211_11G; 1697170530Ssam } else if (IEEE80211_IS_CHAN_FHSS(chan)) { 1698170530Ssam status |= IFM_IEEE80211_FH; 1699170530Ssam } 1700170530Ssam /* XXX else complain? */ 1701170530Ssam 1702170530Ssam if (IEEE80211_IS_CHAN_TURBO(chan)) 1703170530Ssam status |= IFM_IEEE80211_TURBO; 1704178354Ssam#if 0 1705178354Ssam if (IEEE80211_IS_CHAN_HT20(chan)) 1706178354Ssam status |= IFM_IEEE80211_HT20; 1707178354Ssam if (IEEE80211_IS_CHAN_HT40(chan)) 1708178354Ssam status |= IFM_IEEE80211_HT40; 1709178354Ssam#endif 1710170530Ssam return status; 1711170530Ssam} 1712170530Ssam 1713116742Ssamvoid 1714116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1715116742Ssam{ 1716178354Ssam struct ieee80211vap *vap = ifp->if_softc; 1717178354Ssam struct ieee80211com *ic = vap->iv_ic; 1718170530Ssam enum ieee80211_phymode mode; 1719116742Ssam 1720116742Ssam imr->ifm_status = IFM_AVALID; 1721170530Ssam /* 1722170530Ssam * NB: use the current channel's mode to lock down a xmit 1723170530Ssam * rate only when running; otherwise we may have a mismatch 1724170530Ssam * in which case the rate will not be convertible. 1725170530Ssam */ 1726264843Sadrian if (vap->iv_state == IEEE80211_S_RUN || 1727264843Sadrian vap->iv_state == IEEE80211_S_SLEEP) { 1728116742Ssam imr->ifm_status |= IFM_ACTIVE; 1729170530Ssam mode = ieee80211_chan2mode(ic->ic_curchan); 1730170530Ssam } else 1731170530Ssam mode = IEEE80211_MODE_AUTO; 1732178354Ssam imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); 1733138568Ssam /* 1734138568Ssam * Calculate a current rate if possible. 1735138568Ssam */ 1736178354Ssam if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { 1737138568Ssam /* 1738138568Ssam * A fixed rate is set, report that. 1739138568Ssam */ 1740138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1741178354Ssam vap->iv_txparms[mode].ucastrate, mode); 1742178354Ssam } else if (vap->iv_opmode == IEEE80211_M_STA) { 1743138568Ssam /* 1744138568Ssam * In station mode report the current transmit rate. 1745138568Ssam */ 1746138568Ssam imr->ifm_active |= ieee80211_rate2media(ic, 1747178354Ssam vap->iv_bss->ni_txrate, mode); 1748128966Sandre } else 1749138568Ssam imr->ifm_active |= IFM_AUTO; 1750178354Ssam if (imr->ifm_status & IFM_ACTIVE) 1751178354Ssam imr->ifm_current = imr->ifm_active; 1752116742Ssam} 1753116742Ssam 1754116742Ssam/* 1755116742Ssam * Set the current phy mode and recalculate the active channel 1756116742Ssam * set based on the available channels for this mode. Also 1757116742Ssam * select a new default/current channel if the current one is 1758116742Ssam * inappropriate for this mode. 1759116742Ssam */ 1760116742Ssamint 1761116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) 1762116742Ssam{ 1763116742Ssam /* 1764166012Ssam * Adjust basic rates in 11b/11g supported rate set. 1765166012Ssam * Note that if operating on a hal/quarter rate channel 1766166012Ssam * this is a noop as those rates sets are different 1767166012Ssam * and used instead. 1768116742Ssam */ 1769166012Ssam if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 1770178354Ssam ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); 1771166012Ssam 1772116742Ssam ic->ic_curmode = mode; 1773138568Ssam ieee80211_reset_erp(ic); /* reset ERP state */ 1774138568Ssam 1775116742Ssam return 0; 1776116742Ssam} 1777116742Ssam 1778116742Ssam/* 1779170530Ssam * Return the phy mode for with the specified channel. 1780116742Ssam */ 1781116742Ssamenum ieee80211_phymode 1782170530Ssamieee80211_chan2mode(const struct ieee80211_channel *chan) 1783116742Ssam{ 1784170530Ssam 1785170530Ssam if (IEEE80211_IS_CHAN_HTA(chan)) 1786170530Ssam return IEEE80211_MODE_11NA; 1787170530Ssam else if (IEEE80211_IS_CHAN_HTG(chan)) 1788170530Ssam return IEEE80211_MODE_11NG; 1789170530Ssam else if (IEEE80211_IS_CHAN_108G(chan)) 1790170530Ssam return IEEE80211_MODE_TURBO_G; 1791170530Ssam else if (IEEE80211_IS_CHAN_ST(chan)) 1792170530Ssam return IEEE80211_MODE_STURBO_A; 1793170530Ssam else if (IEEE80211_IS_CHAN_TURBO(chan)) 1794153350Ssam return IEEE80211_MODE_TURBO_A; 1795188782Ssam else if (IEEE80211_IS_CHAN_HALF(chan)) 1796188782Ssam return IEEE80211_MODE_HALF; 1797188782Ssam else if (IEEE80211_IS_CHAN_QUARTER(chan)) 1798188782Ssam return IEEE80211_MODE_QUARTER; 1799170530Ssam else if (IEEE80211_IS_CHAN_A(chan)) 1800116742Ssam return IEEE80211_MODE_11A; 1801170530Ssam else if (IEEE80211_IS_CHAN_ANYG(chan)) 1802116742Ssam return IEEE80211_MODE_11G; 1803170530Ssam else if (IEEE80211_IS_CHAN_B(chan)) 1804116742Ssam return IEEE80211_MODE_11B; 1805170530Ssam else if (IEEE80211_IS_CHAN_FHSS(chan)) 1806170530Ssam return IEEE80211_MODE_FH; 1807170530Ssam 1808170530Ssam /* NB: should not get here */ 1809170530Ssam printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", 1810170530Ssam __func__, chan->ic_freq, chan->ic_flags); 1811170530Ssam return IEEE80211_MODE_11B; 1812116742Ssam} 1813116742Ssam 1814170530Ssamstruct ratemedia { 1815170530Ssam u_int match; /* rate + mode */ 1816170530Ssam u_int media; /* if_media rate */ 1817170530Ssam}; 1818170530Ssam 1819170530Ssamstatic int 1820170530Ssamfindmedia(const struct ratemedia rates[], int n, u_int match) 1821170530Ssam{ 1822170530Ssam int i; 1823170530Ssam 1824170530Ssam for (i = 0; i < n; i++) 1825170530Ssam if (rates[i].match == match) 1826170530Ssam return rates[i].media; 1827170530Ssam return IFM_AUTO; 1828170530Ssam} 1829170530Ssam 1830116742Ssam/* 1831170530Ssam * Convert IEEE80211 rate value to ifmedia subtype. 1832170530Ssam * Rate is either a legacy rate in units of 0.5Mbps 1833170530Ssam * or an MCS index. 1834116742Ssam */ 1835116742Ssamint 1836116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) 1837116742Ssam{ 1838170530Ssam static const struct ratemedia rates[] = { 1839124543Sonoe { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, 1840124543Sonoe { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, 1841124543Sonoe { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, 1842124543Sonoe { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, 1843124543Sonoe { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, 1844124543Sonoe { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, 1845124543Sonoe { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, 1846124543Sonoe { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, 1847124543Sonoe { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, 1848124543Sonoe { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, 1849124543Sonoe { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, 1850124543Sonoe { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, 1851124543Sonoe { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, 1852124543Sonoe { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, 1853124543Sonoe { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, 1854124543Sonoe { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, 1855124543Sonoe { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, 1856124543Sonoe { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, 1857124543Sonoe { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, 1858124543Sonoe { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, 1859124543Sonoe { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, 1860124543Sonoe { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, 1861124543Sonoe { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, 1862124543Sonoe { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, 1863124543Sonoe { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, 1864124543Sonoe { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, 1865124543Sonoe { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, 1866165569Ssam { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, 1867165569Ssam { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, 1868165569Ssam { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, 1869298995Spfg /* NB: OFDM72 doesn't really exist so we don't handle it */ 1870116742Ssam }; 1871170530Ssam static const struct ratemedia htrates[] = { 1872170530Ssam { 0, IFM_IEEE80211_MCS }, 1873170530Ssam { 1, IFM_IEEE80211_MCS }, 1874170530Ssam { 2, IFM_IEEE80211_MCS }, 1875170530Ssam { 3, IFM_IEEE80211_MCS }, 1876170530Ssam { 4, IFM_IEEE80211_MCS }, 1877170530Ssam { 5, IFM_IEEE80211_MCS }, 1878170530Ssam { 6, IFM_IEEE80211_MCS }, 1879170530Ssam { 7, IFM_IEEE80211_MCS }, 1880170530Ssam { 8, IFM_IEEE80211_MCS }, 1881170530Ssam { 9, IFM_IEEE80211_MCS }, 1882170530Ssam { 10, IFM_IEEE80211_MCS }, 1883170530Ssam { 11, IFM_IEEE80211_MCS }, 1884170530Ssam { 12, IFM_IEEE80211_MCS }, 1885170530Ssam { 13, IFM_IEEE80211_MCS }, 1886170530Ssam { 14, IFM_IEEE80211_MCS }, 1887170530Ssam { 15, IFM_IEEE80211_MCS }, 1888219456Sbschmidt { 16, IFM_IEEE80211_MCS }, 1889219456Sbschmidt { 17, IFM_IEEE80211_MCS }, 1890219456Sbschmidt { 18, IFM_IEEE80211_MCS }, 1891219456Sbschmidt { 19, IFM_IEEE80211_MCS }, 1892219456Sbschmidt { 20, IFM_IEEE80211_MCS }, 1893219456Sbschmidt { 21, IFM_IEEE80211_MCS }, 1894219456Sbschmidt { 22, IFM_IEEE80211_MCS }, 1895219456Sbschmidt { 23, IFM_IEEE80211_MCS }, 1896219456Sbschmidt { 24, IFM_IEEE80211_MCS }, 1897219456Sbschmidt { 25, IFM_IEEE80211_MCS }, 1898219456Sbschmidt { 26, IFM_IEEE80211_MCS }, 1899219456Sbschmidt { 27, IFM_IEEE80211_MCS }, 1900219456Sbschmidt { 28, IFM_IEEE80211_MCS }, 1901219456Sbschmidt { 29, IFM_IEEE80211_MCS }, 1902219456Sbschmidt { 30, IFM_IEEE80211_MCS }, 1903219456Sbschmidt { 31, IFM_IEEE80211_MCS }, 1904219456Sbschmidt { 32, IFM_IEEE80211_MCS }, 1905219456Sbschmidt { 33, IFM_IEEE80211_MCS }, 1906219456Sbschmidt { 34, IFM_IEEE80211_MCS }, 1907219456Sbschmidt { 35, IFM_IEEE80211_MCS }, 1908219456Sbschmidt { 36, IFM_IEEE80211_MCS }, 1909219456Sbschmidt { 37, IFM_IEEE80211_MCS }, 1910219456Sbschmidt { 38, IFM_IEEE80211_MCS }, 1911219456Sbschmidt { 39, IFM_IEEE80211_MCS }, 1912219456Sbschmidt { 40, IFM_IEEE80211_MCS }, 1913219456Sbschmidt { 41, IFM_IEEE80211_MCS }, 1914219456Sbschmidt { 42, IFM_IEEE80211_MCS }, 1915219456Sbschmidt { 43, IFM_IEEE80211_MCS }, 1916219456Sbschmidt { 44, IFM_IEEE80211_MCS }, 1917219456Sbschmidt { 45, IFM_IEEE80211_MCS }, 1918219456Sbschmidt { 46, IFM_IEEE80211_MCS }, 1919219456Sbschmidt { 47, IFM_IEEE80211_MCS }, 1920219456Sbschmidt { 48, IFM_IEEE80211_MCS }, 1921219456Sbschmidt { 49, IFM_IEEE80211_MCS }, 1922219456Sbschmidt { 50, IFM_IEEE80211_MCS }, 1923219456Sbschmidt { 51, IFM_IEEE80211_MCS }, 1924219456Sbschmidt { 52, IFM_IEEE80211_MCS }, 1925219456Sbschmidt { 53, IFM_IEEE80211_MCS }, 1926219456Sbschmidt { 54, IFM_IEEE80211_MCS }, 1927219456Sbschmidt { 55, IFM_IEEE80211_MCS }, 1928219456Sbschmidt { 56, IFM_IEEE80211_MCS }, 1929219456Sbschmidt { 57, IFM_IEEE80211_MCS }, 1930219456Sbschmidt { 58, IFM_IEEE80211_MCS }, 1931219456Sbschmidt { 59, IFM_IEEE80211_MCS }, 1932219456Sbschmidt { 60, IFM_IEEE80211_MCS }, 1933219456Sbschmidt { 61, IFM_IEEE80211_MCS }, 1934219456Sbschmidt { 62, IFM_IEEE80211_MCS }, 1935219456Sbschmidt { 63, IFM_IEEE80211_MCS }, 1936219456Sbschmidt { 64, IFM_IEEE80211_MCS }, 1937219456Sbschmidt { 65, IFM_IEEE80211_MCS }, 1938219456Sbschmidt { 66, IFM_IEEE80211_MCS }, 1939219456Sbschmidt { 67, IFM_IEEE80211_MCS }, 1940219456Sbschmidt { 68, IFM_IEEE80211_MCS }, 1941219456Sbschmidt { 69, IFM_IEEE80211_MCS }, 1942219456Sbschmidt { 70, IFM_IEEE80211_MCS }, 1943219456Sbschmidt { 71, IFM_IEEE80211_MCS }, 1944219456Sbschmidt { 72, IFM_IEEE80211_MCS }, 1945219456Sbschmidt { 73, IFM_IEEE80211_MCS }, 1946219456Sbschmidt { 74, IFM_IEEE80211_MCS }, 1947219456Sbschmidt { 75, IFM_IEEE80211_MCS }, 1948219456Sbschmidt { 76, IFM_IEEE80211_MCS }, 1949170530Ssam }; 1950170530Ssam int m; 1951116742Ssam 1952170530Ssam /* 1953170530Ssam * Check 11n rates first for match as an MCS. 1954170530Ssam */ 1955170530Ssam if (mode == IEEE80211_MODE_11NA) { 1956172226Ssam if (rate & IEEE80211_RATE_MCS) { 1957172226Ssam rate &= ~IEEE80211_RATE_MCS; 1958254315Srpaulo m = findmedia(htrates, nitems(htrates), rate); 1959170530Ssam if (m != IFM_AUTO) 1960170530Ssam return m | IFM_IEEE80211_11NA; 1961170530Ssam } 1962170530Ssam } else if (mode == IEEE80211_MODE_11NG) { 1963170530Ssam /* NB: 12 is ambiguous, it will be treated as an MCS */ 1964172226Ssam if (rate & IEEE80211_RATE_MCS) { 1965172226Ssam rate &= ~IEEE80211_RATE_MCS; 1966254315Srpaulo m = findmedia(htrates, nitems(htrates), rate); 1967170530Ssam if (m != IFM_AUTO) 1968170530Ssam return m | IFM_IEEE80211_11NG; 1969170530Ssam } 1970170530Ssam } 1971170530Ssam rate &= IEEE80211_RATE_VAL; 1972116742Ssam switch (mode) { 1973116742Ssam case IEEE80211_MODE_11A: 1974188782Ssam case IEEE80211_MODE_HALF: /* XXX good 'nuf */ 1975188782Ssam case IEEE80211_MODE_QUARTER: 1976170530Ssam case IEEE80211_MODE_11NA: 1977138568Ssam case IEEE80211_MODE_TURBO_A: 1978170530Ssam case IEEE80211_MODE_STURBO_A: 1979337950Skevans return findmedia(rates, nitems(rates), 1980254315Srpaulo rate | IFM_IEEE80211_11A); 1981116742Ssam case IEEE80211_MODE_11B: 1982337950Skevans return findmedia(rates, nitems(rates), 1983254315Srpaulo rate | IFM_IEEE80211_11B); 1984124543Sonoe case IEEE80211_MODE_FH: 1985337950Skevans return findmedia(rates, nitems(rates), 1986254315Srpaulo rate | IFM_IEEE80211_FH); 1987116742Ssam case IEEE80211_MODE_AUTO: 1988116742Ssam /* NB: ic may be NULL for some drivers */ 1989188775Ssam if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) 1990254315Srpaulo return findmedia(rates, nitems(rates), 1991170530Ssam rate | IFM_IEEE80211_FH); 1992116742Ssam /* NB: hack, 11g matches both 11b+11a rates */ 1993116742Ssam /* fall thru... */ 1994116742Ssam case IEEE80211_MODE_11G: 1995170530Ssam case IEEE80211_MODE_11NG: 1996138568Ssam case IEEE80211_MODE_TURBO_G: 1997254315Srpaulo return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11G); 1998116742Ssam } 1999116742Ssam return IFM_AUTO; 2000116742Ssam} 2001116742Ssam 2002116742Ssamint 2003116742Ssamieee80211_media2rate(int mword) 2004116742Ssam{ 2005116742Ssam static const int ieeerates[] = { 2006116742Ssam -1, /* IFM_AUTO */ 2007116742Ssam 0, /* IFM_MANUAL */ 2008116742Ssam 0, /* IFM_NONE */ 2009116742Ssam 2, /* IFM_IEEE80211_FH1 */ 2010116742Ssam 4, /* IFM_IEEE80211_FH2 */ 2011116742Ssam 2, /* IFM_IEEE80211_DS1 */ 2012116742Ssam 4, /* IFM_IEEE80211_DS2 */ 2013116742Ssam 11, /* IFM_IEEE80211_DS5 */ 2014116742Ssam 22, /* IFM_IEEE80211_DS11 */ 2015116742Ssam 44, /* IFM_IEEE80211_DS22 */ 2016116742Ssam 12, /* IFM_IEEE80211_OFDM6 */ 2017116742Ssam 18, /* IFM_IEEE80211_OFDM9 */ 2018116742Ssam 24, /* IFM_IEEE80211_OFDM12 */ 2019116742Ssam 36, /* IFM_IEEE80211_OFDM18 */ 2020116742Ssam 48, /* IFM_IEEE80211_OFDM24 */ 2021116742Ssam 72, /* IFM_IEEE80211_OFDM36 */ 2022116742Ssam 96, /* IFM_IEEE80211_OFDM48 */ 2023116742Ssam 108, /* IFM_IEEE80211_OFDM54 */ 2024116742Ssam 144, /* IFM_IEEE80211_OFDM72 */ 2025165569Ssam 0, /* IFM_IEEE80211_DS354k */ 2026165569Ssam 0, /* IFM_IEEE80211_DS512k */ 2027165569Ssam 6, /* IFM_IEEE80211_OFDM3 */ 2028165569Ssam 9, /* IFM_IEEE80211_OFDM4 */ 2029165569Ssam 54, /* IFM_IEEE80211_OFDM27 */ 2030170530Ssam -1, /* IFM_IEEE80211_MCS */ 2031116742Ssam }; 2032254315Srpaulo return IFM_SUBTYPE(mword) < nitems(ieeerates) ? 2033116742Ssam ieeerates[IFM_SUBTYPE(mword)] : 0; 2034116742Ssam} 2035195379Ssam 2036195379Ssam/* 2037195379Ssam * The following hash function is adapted from "Hash Functions" by Bob Jenkins 2038195379Ssam * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). 2039195379Ssam */ 2040195379Ssam#define mix(a, b, c) \ 2041195379Ssamdo { \ 2042195379Ssam a -= b; a -= c; a ^= (c >> 13); \ 2043195379Ssam b -= c; b -= a; b ^= (a << 8); \ 2044195379Ssam c -= a; c -= b; c ^= (b >> 13); \ 2045195379Ssam a -= b; a -= c; a ^= (c >> 12); \ 2046195379Ssam b -= c; b -= a; b ^= (a << 16); \ 2047195379Ssam c -= a; c -= b; c ^= (b >> 5); \ 2048195379Ssam a -= b; a -= c; a ^= (c >> 3); \ 2049195379Ssam b -= c; b -= a; b ^= (a << 10); \ 2050195379Ssam c -= a; c -= b; c ^= (b >> 15); \ 2051195379Ssam} while (/*CONSTCOND*/0) 2052195379Ssam 2053195379Ssamuint32_t 2054195379Ssamieee80211_mac_hash(const struct ieee80211com *ic, 2055195379Ssam const uint8_t addr[IEEE80211_ADDR_LEN]) 2056195379Ssam{ 2057195379Ssam uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = ic->ic_hash_key; 2058195379Ssam 2059195379Ssam b += addr[5] << 8; 2060195379Ssam b += addr[4]; 2061195379Ssam a += addr[3] << 24; 2062195379Ssam a += addr[2] << 16; 2063195379Ssam a += addr[1] << 8; 2064195379Ssam a += addr[0]; 2065195379Ssam 2066195379Ssam mix(a, b, c); 2067195379Ssam 2068195379Ssam return c; 2069195379Ssam} 2070195379Ssam#undef mix 2071276757Sadrian 2072276757Sadrianchar 2073276757Sadrianieee80211_channel_type_char(const struct ieee80211_channel *c) 2074276757Sadrian{ 2075276757Sadrian if (IEEE80211_IS_CHAN_ST(c)) 2076276757Sadrian return 'S'; 2077276757Sadrian if (IEEE80211_IS_CHAN_108A(c)) 2078276757Sadrian return 'T'; 2079276757Sadrian if (IEEE80211_IS_CHAN_108G(c)) 2080276757Sadrian return 'G'; 2081276757Sadrian if (IEEE80211_IS_CHAN_HT(c)) 2082276757Sadrian return 'n'; 2083276757Sadrian if (IEEE80211_IS_CHAN_A(c)) 2084276757Sadrian return 'a'; 2085276757Sadrian if (IEEE80211_IS_CHAN_ANYG(c)) 2086276757Sadrian return 'g'; 2087276757Sadrian if (IEEE80211_IS_CHAN_B(c)) 2088276757Sadrian return 'b'; 2089276757Sadrian return 'f'; 2090276757Sadrian} 2091