Commit | Line | Data |
---|---|---|
32176cfd | 1 | /*- |
f186073c | 2 | * Copyright (c) 2001 Atsushi Onoe |
32176cfd | 3 | * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting |
f186073c JS |
4 | * All rights reserved. |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
f186073c JS |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
f186073c JS |
25 | */ |
26 | ||
085ff963 MD |
27 | #include <sys/cdefs.h> |
28 | __FBSDID("$FreeBSD$"); | |
29 | ||
f186073c JS |
30 | /* |
31 | * IEEE 802.11 generic handler | |
32 | */ | |
32176cfd | 33 | #include "opt_wlan.h" |
f186073c | 34 | |
f186073c | 35 | #include <sys/param.h> |
32176cfd | 36 | #include <sys/systm.h> |
f186073c | 37 | #include <sys/kernel.h> |
4f655ef5 | 38 | #include <sys/malloc.h> |
841ab66c | 39 | #include <sys/socket.h> |
4f655ef5 | 40 | #include <sys/sbuf.h> |
841ab66c | 41 | |
4f898719 IV |
42 | #include <machine/stdarg.h> |
43 | ||
f186073c | 44 | #include <net/if.h> |
085ff963 | 45 | #include <net/if_var.h> |
32176cfd | 46 | #include <net/if_dl.h> |
841ab66c | 47 | #include <net/if_media.h> |
32176cfd | 48 | #include <net/if_types.h> |
f186073c | 49 | #include <net/ethernet.h> |
f186073c JS |
50 | |
51 | #include <netproto/802_11/ieee80211_var.h> | |
32176cfd RP |
52 | #include <netproto/802_11/ieee80211_regdomain.h> |
53 | #ifdef IEEE80211_SUPPORT_SUPERG | |
54 | #include <netproto/802_11/ieee80211_superg.h> | |
55 | #endif | |
4028af95 | 56 | #include <netproto/802_11/ieee80211_ratectl.h> |
f186073c JS |
57 | |
58 | #include <net/bpf.h> | |
59 | ||
32176cfd RP |
60 | const char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = { |
61 | [IEEE80211_MODE_AUTO] = "auto", | |
62 | [IEEE80211_MODE_11A] = "11a", | |
63 | [IEEE80211_MODE_11B] = "11b", | |
64 | [IEEE80211_MODE_11G] = "11g", | |
65 | [IEEE80211_MODE_FH] = "FH", | |
66 | [IEEE80211_MODE_TURBO_A] = "turboA", | |
67 | [IEEE80211_MODE_TURBO_G] = "turboG", | |
68 | [IEEE80211_MODE_STURBO_A] = "sturboA", | |
69 | [IEEE80211_MODE_HALF] = "half", | |
70 | [IEEE80211_MODE_QUARTER] = "quarter", | |
71 | [IEEE80211_MODE_11NA] = "11na", | |
72 | [IEEE80211_MODE_11NG] = "11ng", | |
797b05a5 AL |
73 | [IEEE80211_MODE_VHT_2GHZ] = "11acg", |
74 | [IEEE80211_MODE_VHT_5GHZ] = "11ac", | |
f186073c | 75 | }; |
32176cfd RP |
76 | /* map ieee80211_opmode to the corresponding capability bit */ |
77 | const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = { | |
78 | [IEEE80211_M_IBSS] = IEEE80211_C_IBSS, | |
79 | [IEEE80211_M_WDS] = IEEE80211_C_WDS, | |
80 | [IEEE80211_M_STA] = IEEE80211_C_STA, | |
81 | [IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO, | |
82 | [IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP, | |
83 | [IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR, | |
84 | #ifdef IEEE80211_SUPPORT_MESH | |
85 | [IEEE80211_M_MBSS] = IEEE80211_C_MBSS, | |
86 | #endif | |
87 | }; | |
88 | ||
8daa6c6e | 89 | const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = |
32176cfd RP |
90 | { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
91 | ||
92 | static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); | |
93 | static void ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag); | |
94 | static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); | |
95 | static int ieee80211_media_setup(struct ieee80211com *ic, | |
96 | struct ifmedia *media, int caps, int addsta, | |
97 | ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); | |
32176cfd RP |
98 | static int media_status(enum ieee80211_opmode, |
99 | const struct ieee80211_channel *); | |
4f655ef5 MD |
100 | #if defined(__DragonFly__) |
101 | #else | |
102 | static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); | |
103 | #endif | |
f186073c | 104 | |
32176cfd | 105 | MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); |
841ab66c | 106 | |
32176cfd RP |
107 | /* |
108 | * Default supported rates for 802.11 operation (in IEEE .5Mb units). | |
109 | */ | |
110 | #define B(r) ((r) | IEEE80211_RATE_BASIC) | |
111 | static const struct ieee80211_rateset ieee80211_rateset_11a = | |
112 | { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; | |
113 | static const struct ieee80211_rateset ieee80211_rateset_half = | |
114 | { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; | |
115 | static const struct ieee80211_rateset ieee80211_rateset_quarter = | |
116 | { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; | |
117 | static const struct ieee80211_rateset ieee80211_rateset_11b = | |
118 | { 4, { B(2), B(4), B(11), B(22) } }; | |
119 | /* NB: OFDM rates are handled specially based on mode */ | |
120 | static const struct ieee80211_rateset ieee80211_rateset_11g = | |
121 | { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; | |
122 | #undef B | |
123 | ||
124 | /* | |
125 | * Fill in 802.11 available channel set, mark | |
126 | * all available channels as active, and pick | |
127 | * a default channel if not already specified. | |
128 | */ | |
4f655ef5 | 129 | void |
32176cfd | 130 | ieee80211_chan_init(struct ieee80211com *ic) |
841ab66c | 131 | { |
32176cfd RP |
132 | #define DEFAULTRATES(m, def) do { \ |
133 | if (ic->ic_sup_rates[m].rs_nrates == 0) \ | |
134 | ic->ic_sup_rates[m] = def; \ | |
135 | } while (0) | |
136 | struct ieee80211_channel *c; | |
841ab66c | 137 | int i; |
841ab66c | 138 | |
32176cfd RP |
139 | KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, |
140 | ("invalid number of channels specified: %u", ic->ic_nchans)); | |
141 | memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); | |
142 | memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); | |
143 | setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); | |
144 | for (i = 0; i < ic->ic_nchans; i++) { | |
145 | c = &ic->ic_channels[i]; | |
146 | KASSERT(c->ic_flags != 0, ("channel with no flags")); | |
147 | /* | |
148 | * Help drivers that work only with frequencies by filling | |
149 | * in IEEE channel #'s if not already calculated. Note this | |
150 | * mimics similar work done in ieee80211_setregdomain when | |
151 | * changing regulatory state. | |
152 | */ | |
153 | if (c->ic_ieee == 0) | |
154 | c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); | |
155 | if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) | |
156 | c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + | |
157 | (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), | |
158 | c->ic_flags); | |
159 | /* default max tx power to max regulatory */ | |
160 | if (c->ic_maxpower == 0) | |
161 | c->ic_maxpower = 2*c->ic_maxregpower; | |
162 | setbit(ic->ic_chan_avail, c->ic_ieee); | |
163 | /* | |
164 | * Identify mode capabilities. | |
165 | */ | |
166 | if (IEEE80211_IS_CHAN_A(c)) | |
167 | setbit(ic->ic_modecaps, IEEE80211_MODE_11A); | |
168 | if (IEEE80211_IS_CHAN_B(c)) | |
169 | setbit(ic->ic_modecaps, IEEE80211_MODE_11B); | |
170 | if (IEEE80211_IS_CHAN_ANYG(c)) | |
171 | setbit(ic->ic_modecaps, IEEE80211_MODE_11G); | |
172 | if (IEEE80211_IS_CHAN_FHSS(c)) | |
173 | setbit(ic->ic_modecaps, IEEE80211_MODE_FH); | |
174 | if (IEEE80211_IS_CHAN_108A(c)) | |
175 | setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); | |
176 | if (IEEE80211_IS_CHAN_108G(c)) | |
177 | setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); | |
178 | if (IEEE80211_IS_CHAN_ST(c)) | |
179 | setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); | |
180 | if (IEEE80211_IS_CHAN_HALF(c)) | |
181 | setbit(ic->ic_modecaps, IEEE80211_MODE_HALF); | |
182 | if (IEEE80211_IS_CHAN_QUARTER(c)) | |
183 | setbit(ic->ic_modecaps, IEEE80211_MODE_QUARTER); | |
184 | if (IEEE80211_IS_CHAN_HTA(c)) | |
185 | setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); | |
186 | if (IEEE80211_IS_CHAN_HTG(c)) | |
187 | setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); | |
188 | } | |
189 | /* initialize candidate channels to all available */ | |
190 | memcpy(ic->ic_chan_active, ic->ic_chan_avail, | |
191 | sizeof(ic->ic_chan_avail)); | |
192 | ||
193 | /* sort channel table to allow lookup optimizations */ | |
194 | ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); | |
195 | ||
196 | /* invalidate any previous state */ | |
197 | ic->ic_bsschan = IEEE80211_CHAN_ANYC; | |
198 | ic->ic_prevchan = NULL; | |
199 | ic->ic_csa_newchan = NULL; | |
200 | /* arbitrarily pick the first channel */ | |
201 | ic->ic_curchan = &ic->ic_channels[0]; | |
202 | ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); | |
203 | ||
204 | /* fillin well-known rate sets if driver has not specified */ | |
205 | DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); | |
206 | DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); | |
207 | DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); | |
208 | DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); | |
209 | DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); | |
210 | DEFAULTRATES(IEEE80211_MODE_STURBO_A, ieee80211_rateset_11a); | |
211 | DEFAULTRATES(IEEE80211_MODE_HALF, ieee80211_rateset_half); | |
212 | DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter); | |
213 | DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); | |
214 | DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); | |
215 | ||
085ff963 MD |
216 | /* |
217 | * Setup required information to fill the mcsset field, if driver did | |
218 | * not. Assume a 2T2R setup for historic reasons. | |
219 | */ | |
220 | if (ic->ic_rxstream == 0) | |
221 | ic->ic_rxstream = 2; | |
222 | if (ic->ic_txstream == 0) | |
223 | ic->ic_txstream = 2; | |
224 | ||
32176cfd RP |
225 | /* |
226 | * Set auto mode to reset active channel state and any desired channel. | |
227 | */ | |
228 | (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); | |
229 | #undef DEFAULTRATES | |
230 | } | |
841ab66c | 231 | |
32176cfd | 232 | static void |
4f898719 | 233 | null_update_mcast(struct ieee80211com *ic) |
32176cfd | 234 | { |
4f655ef5 | 235 | |
4f898719 | 236 | ic_printf(ic, "need multicast update callback\n"); |
32176cfd | 237 | } |
841ab66c | 238 | |
32176cfd | 239 | static void |
4f898719 | 240 | null_update_promisc(struct ieee80211com *ic) |
32176cfd | 241 | { |
32176cfd | 242 | |
4f655ef5 | 243 | ic_printf(ic, "need promiscuous mode update callback\n"); |
32176cfd RP |
244 | } |
245 | ||
085ff963 MD |
246 | static void |
247 | null_update_chw(struct ieee80211com *ic) | |
248 | { | |
249 | ||
4f898719 IV |
250 | ic_printf(ic, "%s: need callback\n", __func__); |
251 | } | |
252 | ||
253 | int | |
254 | ic_printf(struct ieee80211com *ic, const char * fmt, ...) | |
255 | { | |
4f655ef5 | 256 | #if defined(__DragonFly__) |
4f898719 IV |
257 | osdep_va_list ap; |
258 | int retval; | |
259 | ||
260 | retval = kprintf("%s: ", ic->ic_name); | |
261 | osdep_va_start(ap, fmt); | |
262 | retval += kvprintf(fmt, ap); | |
263 | osdep_va_end(ap); | |
4f655ef5 MD |
264 | #else |
265 | va_list ap; | |
266 | int retval; | |
267 | ||
268 | retval = printf("%s: ", ic->ic_name); | |
269 | va_start(ap, fmt); | |
270 | retval += vprintf(fmt, ap); | |
271 | va_end(ap); | |
272 | #endif | |
4f898719 | 273 | return (retval); |
085ff963 MD |
274 | } |
275 | ||
4f655ef5 MD |
276 | static LIST_HEAD(, ieee80211com) ic_head = LIST_HEAD_INITIALIZER(ic_head); |
277 | #if defined(__DragonFly__) | |
278 | static struct lock ic_list_lock = | |
279 | LOCK_INITIALIZER("80211list", 0, LK_CANRECURSE); | |
280 | #else | |
281 | static struct mtx ic_list_mtx; | |
282 | MTX_SYSINIT(ic_list, &ic_list_mtx, "ieee80211com list", MTX_DEF); | |
283 | #endif | |
284 | ||
285 | static int | |
286 | sysctl_ieee80211coms(SYSCTL_HANDLER_ARGS) | |
287 | { | |
288 | struct ieee80211com *ic; | |
289 | struct sbuf sb; | |
290 | char *sp; | |
291 | int error; | |
292 | ||
293 | #if defined(__DragonFly__) | |
294 | #else | |
295 | error = sysctl_wire_old_buffer(req, 0); | |
296 | if (error) | |
297 | return (error); | |
298 | #endif | |
299 | sbuf_new_for_sysctl(&sb, NULL, 8, req); | |
300 | #if defined(__DragonFly__) | |
301 | #else | |
302 | sbuf_clear_flags(&sb, SBUF_INCLUDENUL); | |
303 | #endif | |
304 | sp = ""; | |
305 | #if defined(__DragonFly__) | |
306 | lockmgr(&ic_list_lock, LK_EXCLUSIVE); | |
307 | #else | |
308 | mtx_lock(&ic_list_mtx); | |
309 | #endif | |
310 | LIST_FOREACH(ic, &ic_head, ic_next) { | |
311 | sbuf_printf(&sb, "%s%s", sp, ic->ic_name); | |
312 | sp = " "; | |
313 | } | |
314 | #if defined(__DragonFly__) | |
315 | lockmgr(&ic_list_lock, LK_RELEASE); | |
316 | #else | |
317 | mtx_unlock(&ic_list_mtx); | |
318 | #endif | |
319 | error = sbuf_finish(&sb); | |
320 | sbuf_delete(&sb); | |
321 | return (error); | |
322 | } | |
323 | ||
324 | #if defined(__DragonFly__) | |
325 | SYSCTL_PROC(_net_wlan, OID_AUTO, devices, | |
326 | CTLTYPE_STRING | CTLFLAG_RD, NULL, 0, | |
327 | sysctl_ieee80211coms, "A", "names of available 802.11 devices"); | |
328 | #else | |
329 | SYSCTL_PROC(_net_wlan, OID_AUTO, devices, | |
330 | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, | |
331 | sysctl_ieee80211coms, "A", "names of available 802.11 devices"); | |
332 | #endif | |
333 | ||
334 | ||
32176cfd RP |
335 | /* |
336 | * Attach/setup the common net80211 state. Called by | |
337 | * the driver on attach to prior to creating any vap's. | |
338 | */ | |
339 | void | |
4f655ef5 | 340 | ieee80211_ifattach(struct ieee80211com *ic) |
32176cfd | 341 | { |
32176cfd | 342 | |
4f898719 IV |
343 | IEEE80211_LOCK_INIT(ic, ic->ic_name); |
344 | IEEE80211_TX_LOCK_INIT(ic, ic->ic_name); | |
32176cfd RP |
345 | TAILQ_INIT(&ic->ic_vaps); |
346 | ||
347 | /* Create a taskqueue for all state changes */ | |
348 | ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO, | |
349 | taskqueue_thread_enqueue, &ic->ic_tq); | |
085ff963 | 350 | #if defined(__DragonFly__) |
32176cfd | 351 | taskqueue_start_threads(&ic->ic_tq, 1, TDPRI_KERN_DAEMON, -1, |
4f898719 | 352 | "%s net80211 taskq", ic->ic_name); |
085ff963 MD |
353 | #else |
354 | taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s net80211 taskq", | |
4f898719 | 355 | ic->ic_name); |
4f655ef5 MD |
356 | ic->ic_ierrors = counter_u64_alloc(M_WAITOK); |
357 | ic->ic_oerrors = counter_u64_alloc(M_WAITOK); | |
085ff963 | 358 | #endif |
32176cfd RP |
359 | /* |
360 | * Fill in 802.11 available channel set, mark all | |
361 | * available channels as active, and pick a default | |
362 | * channel if not already specified. | |
363 | */ | |
4f655ef5 | 364 | ieee80211_chan_init(ic); |
32176cfd RP |
365 | |
366 | ic->ic_update_mcast = null_update_mcast; | |
367 | ic->ic_update_promisc = null_update_promisc; | |
085ff963 | 368 | ic->ic_update_chw = null_update_chw; |
32176cfd | 369 | |
085ff963 | 370 | ic->ic_hash_key = arc4random(); |
32176cfd RP |
371 | ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; |
372 | ic->ic_lintval = ic->ic_bintval; | |
373 | ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; | |
374 | ||
375 | ieee80211_crypto_attach(ic); | |
376 | ieee80211_node_attach(ic); | |
377 | ieee80211_power_attach(ic); | |
378 | ieee80211_proto_attach(ic); | |
379 | #ifdef IEEE80211_SUPPORT_SUPERG | |
380 | ieee80211_superg_attach(ic); | |
381 | #endif | |
382 | ieee80211_ht_attach(ic); | |
383 | ieee80211_scan_attach(ic); | |
384 | ieee80211_regdomain_attach(ic); | |
385 | ieee80211_dfs_attach(ic); | |
386 | ||
387 | ieee80211_sysctl_attach(ic); | |
388 | ||
085ff963 | 389 | #if defined(__DragonFly__) |
4f655ef5 | 390 | lockmgr(&ic_list_lock, LK_EXCLUSIVE); |
085ff963 | 391 | #else |
4f655ef5 MD |
392 | mtx_lock(&ic_list_mtx); |
393 | #endif | |
394 | LIST_INSERT_HEAD(&ic_head, ic, ic_next); | |
395 | #if defined(__DragonFly__) | |
396 | lockmgr(&ic_list_lock, LK_RELEASE); | |
397 | #else | |
398 | mtx_unlock(&ic_list_mtx); | |
085ff963 | 399 | #endif |
32176cfd RP |
400 | } |
401 | ||
402 | /* | |
403 | * Detach net80211 state on device detach. Tear down | |
404 | * all vap's and reclaim all common state prior to the | |
405 | * device state going away. Note we may call back into | |
406 | * driver; it must be prepared for this. | |
407 | */ | |
408 | void | |
409 | ieee80211_ifdetach(struct ieee80211com *ic) | |
841ab66c | 410 | { |
32176cfd RP |
411 | struct ieee80211vap *vap; |
412 | ||
7dfe50f5 IV |
413 | #if defined(__DragonFly__) |
414 | wlan_serialize_enter(); | |
415 | #endif | |
416 | ||
4f655ef5 MD |
417 | #if defined(__DragonFly__) |
418 | lockmgr(&ic_list_lock, LK_EXCLUSIVE); | |
419 | #else | |
420 | mtx_lock(&ic_list_mtx); | |
421 | #endif | |
422 | LIST_REMOVE(ic, ic_next); | |
423 | #if defined(__DragonFly__) | |
424 | lockmgr(&ic_list_lock, LK_RELEASE); | |
425 | #else | |
426 | mtx_unlock(&ic_list_mtx); | |
427 | #endif | |
428 | ||
429 | #if defined(__DragonFly__) | |
dd3f3f08 | 430 | taskqueue_drain(taskqueue_thread[0], &ic->ic_restart_task); |
4f655ef5 MD |
431 | #else |
432 | taskqueue_drain(taskqueue_thread, &ic->ic_restart_task); | |
433 | #endif | |
434 | ||
a583ece6 | 435 | /* |
cfc4faf7 SZ |
436 | * The VAP is responsible for setting and clearing |
437 | * the VIMAGE context. | |
438 | */ | |
439 | while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) | |
440 | ieee80211_vap_destroy(vap); | |
32176cfd RP |
441 | ieee80211_waitfor_parent(ic); |
442 | ||
443 | ieee80211_sysctl_detach(ic); | |
444 | ieee80211_dfs_detach(ic); | |
445 | ieee80211_regdomain_detach(ic); | |
446 | ieee80211_scan_detach(ic); | |
447 | #ifdef IEEE80211_SUPPORT_SUPERG | |
448 | ieee80211_superg_detach(ic); | |
449 | #endif | |
450 | ieee80211_ht_detach(ic); | |
451 | /* NB: must be called before ieee80211_node_detach */ | |
452 | ieee80211_proto_detach(ic); | |
453 | ieee80211_crypto_detach(ic); | |
454 | ieee80211_power_detach(ic); | |
455 | ieee80211_node_detach(ic); | |
841ab66c | 456 | |
4f655ef5 MD |
457 | #if defined(__DragonFly__) |
458 | #else | |
459 | counter_u64_free(ic->ic_ierrors); | |
460 | counter_u64_free(ic->ic_oerrors); | |
461 | #endif | |
085ff963 | 462 | |
32176cfd | 463 | taskqueue_free(ic->ic_tq); |
085ff963 MD |
464 | IEEE80211_TX_LOCK_DESTROY(ic); |
465 | IEEE80211_LOCK_DESTROY(ic); | |
7dfe50f5 IV |
466 | |
467 | #if defined(__DragonFly__) | |
468 | wlan_serialize_exit(); | |
469 | #endif | |
841ab66c SZ |
470 | } |
471 | ||
4f655ef5 MD |
472 | struct ieee80211com * |
473 | ieee80211_find_com(const char *name) | |
474 | { | |
475 | struct ieee80211com *ic; | |
476 | ||
477 | #if defined(__DragonFly__) | |
478 | lockmgr(&ic_list_lock, LK_EXCLUSIVE); | |
479 | #else | |
480 | mtx_lock(&ic_list_mtx); | |
481 | #endif | |
482 | LIST_FOREACH(ic, &ic_head, ic_next) { | |
483 | if (strcmp(ic->ic_name, name) == 0) | |
484 | break; | |
485 | } | |
486 | #if defined(__DragonFly__) | |
487 | lockmgr(&ic_list_lock, LK_RELEASE); | |
488 | #else | |
489 | mtx_unlock(&ic_list_mtx); | |
490 | #endif | |
491 | ||
492 | return(ic); | |
493 | } | |
494 | ||
841ab66c SZ |
495 | /* |
496 | * Default reset method for use with the ioctl support. This | |
497 | * method is invoked after any state change in the 802.11 | |
498 | * layer that should be propagated to the hardware but not | |
499 | * require re-initialization of the 802.11 state machine (e.g | |
500 | * rescanning for an ap). We always return ENETRESET which | |
501 | * should cause the driver to re-initialize the device. Drivers | |
502 | * can override this method to implement more optimized support. | |
503 | */ | |
504 | static int | |
32176cfd | 505 | default_reset(struct ieee80211vap *vap, u_long cmd) |
841ab66c SZ |
506 | { |
507 | return ENETRESET; | |
508 | } | |
509 | ||
4f655ef5 MD |
510 | #if defined(__DragonFly__) |
511 | #else | |
512 | /* | |
513 | * Add underlying device errors to vap errors. | |
514 | */ | |
515 | static uint64_t | |
516 | ieee80211_get_counter(struct ifnet *ifp, ift_counter cnt) | |
517 | { | |
518 | struct ieee80211vap *vap = ifp->if_softc; | |
519 | struct ieee80211com *ic = vap->iv_ic; | |
520 | uint64_t rv; | |
521 | ||
522 | rv = if_get_counter_default(ifp, cnt); | |
523 | switch (cnt) { | |
524 | case IFCOUNTER_OERRORS: | |
525 | rv += counter_u64_fetch(ic->ic_oerrors); | |
526 | break; | |
527 | case IFCOUNTER_IERRORS: | |
528 | rv += counter_u64_fetch(ic->ic_ierrors); | |
529 | break; | |
530 | default: | |
531 | break; | |
532 | } | |
533 | ||
534 | return (rv); | |
535 | } | |
536 | ||
537 | #endif | |
538 | ||
32176cfd RP |
539 | /* |
540 | * Prepare a vap for use. Drivers use this call to | |
541 | * setup net80211 state in new vap's prior attaching | |
542 | * them with ieee80211_vap_attach (below). | |
543 | */ | |
544 | int | |
545 | ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, | |
085ff963 | 546 | const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, |
4f655ef5 | 547 | int flags, const uint8_t bssid[IEEE80211_ADDR_LEN]) |
f186073c | 548 | { |
841ab66c | 549 | struct ifnet *ifp; |
841ab66c | 550 | |
7e395935 | 551 | ifp = if_alloc(IFT_ETHER); |
32176cfd | 552 | if (ifp == NULL) { |
4f898719 | 553 | ic_printf(ic, "%s: unable to allocate ifnet\n", |
32176cfd RP |
554 | __func__); |
555 | return ENOMEM; | |
556 | } | |
557 | if_initname(ifp, name, unit); | |
558 | ifp->if_softc = vap; /* back pointer */ | |
559 | ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; | |
4f655ef5 | 560 | #if defined(__DragonFly__) |
085ff963 | 561 | ifp->if_start = ieee80211_vap_start; |
4f655ef5 | 562 | #else |
085ff963 MD |
563 | ifp->if_transmit = ieee80211_vap_transmit; |
564 | ifp->if_qflush = ieee80211_vap_qflush; | |
565 | #endif | |
32176cfd RP |
566 | ifp->if_ioctl = ieee80211_ioctl; |
567 | ifp->if_init = ieee80211_init; | |
4f655ef5 MD |
568 | #if defined(__DragonFly__) |
569 | #else | |
570 | ifp->if_get_counter = ieee80211_get_counter; | |
571 | #endif | |
32176cfd RP |
572 | |
573 | vap->iv_ifp = ifp; | |
574 | vap->iv_ic = ic; | |
575 | vap->iv_flags = ic->ic_flags; /* propagate common flags */ | |
576 | vap->iv_flags_ext = ic->ic_flags_ext; | |
577 | vap->iv_flags_ven = ic->ic_flags_ven; | |
578 | vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; | |
579 | vap->iv_htcaps = ic->ic_htcaps; | |
085ff963 | 580 | vap->iv_htextcaps = ic->ic_htextcaps; |
32176cfd RP |
581 | vap->iv_opmode = opmode; |
582 | vap->iv_caps |= ieee80211_opcap[opmode]; | |
4f655ef5 | 583 | IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr); |
32176cfd RP |
584 | switch (opmode) { |
585 | case IEEE80211_M_WDS: | |
586 | /* | |
587 | * WDS links must specify the bssid of the far end. | |
588 | * For legacy operation this is a static relationship. | |
589 | * For non-legacy operation the station must associate | |
590 | * and be authorized to pass traffic. Plumbing the | |
591 | * vap to the proper node happens when the vap | |
592 | * transitions to RUN state. | |
593 | */ | |
594 | IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); | |
595 | vap->iv_flags |= IEEE80211_F_DESBSSID; | |
596 | if (flags & IEEE80211_CLONE_WDSLEGACY) | |
597 | vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; | |
598 | break; | |
599 | #ifdef IEEE80211_SUPPORT_TDMA | |
600 | case IEEE80211_M_AHDEMO: | |
601 | if (flags & IEEE80211_CLONE_TDMA) { | |
602 | /* NB: checked before clone operation allowed */ | |
603 | KASSERT(ic->ic_caps & IEEE80211_C_TDMA, | |
604 | ("not TDMA capable, ic_caps 0x%x", ic->ic_caps)); | |
f186073c | 605 | /* |
32176cfd RP |
606 | * Propagate TDMA capability to mark vap; this |
607 | * cannot be removed and is used to distinguish | |
608 | * regular ahdemo operation from ahdemo+tdma. | |
f186073c | 609 | */ |
32176cfd | 610 | vap->iv_caps |= IEEE80211_C_TDMA; |
f186073c | 611 | } |
32176cfd RP |
612 | break; |
613 | #endif | |
085ff963 MD |
614 | default: |
615 | break; | |
f186073c | 616 | } |
32176cfd RP |
617 | /* auto-enable s/w beacon miss support */ |
618 | if (flags & IEEE80211_CLONE_NOBEACONS) | |
619 | vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; | |
620 | /* auto-generated or user supplied MAC address */ | |
621 | if (flags & (IEEE80211_CLONE_BSSID|IEEE80211_CLONE_MACADDR)) | |
622 | vap->iv_flags_ext |= IEEE80211_FEXT_UNIQMAC; | |
841ab66c | 623 | /* |
32176cfd RP |
624 | * Enable various functionality by default if we're |
625 | * capable; the driver can override us if it knows better. | |
841ab66c | 626 | */ |
32176cfd RP |
627 | if (vap->iv_caps & IEEE80211_C_WME) |
628 | vap->iv_flags |= IEEE80211_F_WME; | |
629 | if (vap->iv_caps & IEEE80211_C_BURST) | |
630 | vap->iv_flags |= IEEE80211_F_BURST; | |
085ff963 | 631 | /* NB: bg scanning only makes sense for station mode right now */ |
4f655ef5 | 632 | #if defined(__DragonFly__) |
4584f705 | 633 | /* |
085ff963 MD |
634 | * DISABLE BGSCAN BY DEFAULT, many issues can crop up including |
635 | * the link going dead. | |
4584f705 | 636 | */ |
4f655ef5 MD |
637 | /* empty */ |
638 | #else | |
32176cfd RP |
639 | if (vap->iv_opmode == IEEE80211_M_STA && |
640 | (vap->iv_caps & IEEE80211_C_BGSCAN)) | |
641 | vap->iv_flags |= IEEE80211_F_BGSCAN; | |
4584f705 | 642 | #endif |
32176cfd RP |
643 | vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ |
644 | /* NB: DFS support only makes sense for ap mode right now */ | |
645 | if (vap->iv_opmode == IEEE80211_M_HOSTAP && | |
646 | (vap->iv_caps & IEEE80211_C_DFS)) | |
647 | vap->iv_flags_ext |= IEEE80211_FEXT_DFS; | |
648 | ||
649 | vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ | |
650 | vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; | |
651 | vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; | |
652 | /* | |
653 | * Install a default reset method for the ioctl support; | |
654 | * the driver can override this. | |
655 | */ | |
656 | vap->iv_reset = default_reset; | |
657 | ||
32176cfd RP |
658 | ieee80211_sysctl_vattach(vap); |
659 | ieee80211_crypto_vattach(vap); | |
660 | ieee80211_node_vattach(vap); | |
661 | ieee80211_power_vattach(vap); | |
662 | ieee80211_proto_vattach(vap); | |
663 | #ifdef IEEE80211_SUPPORT_SUPERG | |
664 | ieee80211_superg_vattach(vap); | |
841ab66c | 665 | #endif |
32176cfd RP |
666 | ieee80211_ht_vattach(vap); |
667 | ieee80211_scan_vattach(vap); | |
668 | ieee80211_regdomain_vattach(vap); | |
669 | ieee80211_radiotap_vattach(vap); | |
4fbce6bd | 670 | ieee80211_ratectl_set(vap, IEEE80211_RATECTL_NONE); |
f186073c | 671 | |
32176cfd RP |
672 | return 0; |
673 | } | |
841ab66c | 674 | |
32176cfd RP |
675 | /* |
676 | * Activate a vap. State should have been prepared with a | |
677 | * call to ieee80211_vap_setup and by the driver. On return | |
678 | * from this call the vap is ready for use. | |
679 | */ | |
680 | int | |
4f655ef5 MD |
681 | ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, |
682 | ifm_stat_cb_t media_stat, const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
32176cfd RP |
683 | { |
684 | struct ifnet *ifp = vap->iv_ifp; | |
685 | struct ieee80211com *ic = vap->iv_ic; | |
686 | struct ifmediareq imr; | |
687 | int maxrate; | |
f186073c | 688 | |
4f655ef5 | 689 | #if defined(__DragonFly__) |
a583ece6 SZ |
690 | /* |
691 | * This function must _not_ be serialized by the WLAN serializer, | |
692 | * since it could dead-lock the domsg to netisrs in ether_ifattach(). | |
693 | */ | |
694 | wlan_assert_notserialized(); | |
4f655ef5 | 695 | #endif |
32176cfd RP |
696 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, |
697 | "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", | |
698 | __func__, ieee80211_opmode_name[vap->iv_opmode], | |
4f898719 | 699 | ic->ic_name, vap->iv_flags, vap->iv_flags_ext); |
841ab66c | 700 | |
32176cfd RP |
701 | /* |
702 | * Do late attach work that cannot happen until after | |
703 | * the driver has had a chance to override defaults. | |
704 | */ | |
705 | ieee80211_node_latevattach(vap); | |
706 | ieee80211_power_latevattach(vap); | |
841ab66c | 707 | |
32176cfd RP |
708 | maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, |
709 | vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); | |
710 | ieee80211_media_status(ifp, &imr); | |
711 | /* NB: strip explicit mode; we're actually in autoselect */ | |
712 | ifmedia_set(&vap->iv_media, | |
713 | imr.ifm_active &~ (IFM_MMASK | IFM_IEEE80211_TURBO)); | |
714 | if (maxrate) | |
715 | ifp->if_baudrate = IF_Mbps(maxrate); | |
841ab66c | 716 | |
085ff963 | 717 | #if defined(__DragonFly__) |
4f655ef5 | 718 | ether_ifattach(ifp, macaddr, &wlan_global_serializer); |
085ff963 | 719 | #else |
4f655ef5 | 720 | ether_ifattach(ifp, macaddr); |
34a60cf6 | 721 | #endif |
4f655ef5 | 722 | IEEE80211_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp)); |
085ff963 MD |
723 | /* hook output method setup by ether_ifattach */ |
724 | vap->iv_output = ifp->if_output; | |
725 | ifp->if_output = ieee80211_output; | |
32176cfd RP |
726 | /* NB: if_mtu set by ether_ifattach to ETHERMTU */ |
727 | ||
085ff963 | 728 | IEEE80211_LOCK(ic); |
32176cfd RP |
729 | TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); |
730 | ieee80211_syncflag_locked(ic, IEEE80211_F_WME); | |
731 | #ifdef IEEE80211_SUPPORT_SUPERG | |
732 | ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); | |
733 | #endif | |
734 | ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); | |
735 | ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); | |
736 | ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); | |
737 | ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); | |
085ff963 | 738 | IEEE80211_UNLOCK(ic); |
32176cfd RP |
739 | |
740 | return 1; | |
741 | } | |
742 | ||
743 | /* | |
744 | * Tear down vap state and reclaim the ifnet. | |
745 | * The driver is assumed to have prepared for | |
746 | * this; e.g. by turning off interrupts for the | |
747 | * underlying device. | |
748 | */ | |
749 | void | |
750 | ieee80211_vap_detach(struct ieee80211vap *vap) | |
751 | { | |
752 | struct ieee80211com *ic = vap->iv_ic; | |
753 | struct ifnet *ifp = vap->iv_ifp; | |
754 | ||
4f655ef5 | 755 | #if defined(__DragonFly__) |
a583ece6 SZ |
756 | /* |
757 | * This function must _not_ be serialized by the WLAN serializer, | |
758 | * since it could dead-lock the domsg to netisrs in ether_ifdettach(). | |
759 | */ | |
760 | wlan_assert_notserialized(); | |
4f655ef5 | 761 | #endif |
085ff963 MD |
762 | CURVNET_SET(ifp->if_vnet); |
763 | ||
32176cfd | 764 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", |
4f898719 | 765 | __func__, ieee80211_opmode_name[vap->iv_opmode], ic->ic_name); |
32176cfd | 766 | |
085ff963 | 767 | /* NB: bpfdetach is called by ether_ifdetach and claims all taps */ |
32176cfd RP |
768 | ether_ifdetach(ifp); |
769 | ||
770 | ieee80211_stop(vap); | |
b9334f94 | 771 | |
841ab66c | 772 | /* |
32176cfd | 773 | * Flush any deferred vap tasks. |
841ab66c | 774 | */ |
32176cfd RP |
775 | ieee80211_draintask(ic, &vap->iv_nstate_task); |
776 | ieee80211_draintask(ic, &vap->iv_swbmiss_task); | |
777 | ||
4f655ef5 MD |
778 | #if defined(__DragonFly__) |
779 | /* XXX hmm, not sure what we should do here */ | |
780 | #else | |
32176cfd RP |
781 | /* XXX band-aid until ifnet handles this for us */ |
782 | taskqueue_drain(taskqueue_swi, &ifp->if_linktask); | |
34a60cf6 | 783 | #endif |
32176cfd | 784 | |
085ff963 | 785 | IEEE80211_LOCK(ic); |
32176cfd RP |
786 | KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running")); |
787 | TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); | |
788 | ieee80211_syncflag_locked(ic, IEEE80211_F_WME); | |
789 | #ifdef IEEE80211_SUPPORT_SUPERG | |
790 | ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); | |
791 | #endif | |
792 | ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); | |
793 | ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); | |
794 | ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); | |
795 | ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); | |
796 | /* NB: this handles the bpfdetach done below */ | |
797 | ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); | |
4f655ef5 MD |
798 | #if defined(__DragonFly__) |
799 | if (vap->iv_ifflags & IFF_PROMISC) | |
800 | ieee80211_promisc(vap, 0); | |
801 | if (vap->iv_ifflags & IFF_ALLMULTI) | |
802 | ieee80211_allmulti(vap, 0); | |
803 | #else | |
804 | if (vap->iv_ifflags & IFF_PROMISC) | |
805 | ieee80211_promisc(vap, false); | |
806 | if (vap->iv_ifflags & IFF_ALLMULTI) | |
807 | ieee80211_allmulti(vap, false); | |
808 | #endif | |
085ff963 | 809 | IEEE80211_UNLOCK(ic); |
32176cfd RP |
810 | |
811 | ifmedia_removeall(&vap->iv_media); | |
812 | ||
813 | ieee80211_radiotap_vdetach(vap); | |
814 | ieee80211_regdomain_vdetach(vap); | |
815 | ieee80211_scan_vdetach(vap); | |
816 | #ifdef IEEE80211_SUPPORT_SUPERG | |
817 | ieee80211_superg_vdetach(vap); | |
818 | #endif | |
819 | ieee80211_ht_vdetach(vap); | |
820 | /* NB: must be before ieee80211_node_vdetach */ | |
821 | ieee80211_proto_vdetach(vap); | |
822 | ieee80211_crypto_vdetach(vap); | |
823 | ieee80211_power_vdetach(vap); | |
824 | ieee80211_node_vdetach(vap); | |
825 | ieee80211_sysctl_vdetach(vap); | |
826 | ||
827 | if_free(ifp); | |
085ff963 MD |
828 | |
829 | CURVNET_RESTORE(); | |
f186073c JS |
830 | } |
831 | ||
32176cfd | 832 | /* |
4f655ef5 MD |
833 | * Count number of vaps in promisc, and issue promisc on |
834 | * parent respectively. | |
32176cfd | 835 | */ |
4f655ef5 | 836 | #if defined(__DragonFly__) |
f186073c | 837 | void |
4f655ef5 MD |
838 | ieee80211_promisc(struct ieee80211vap *vap, int on) |
839 | #else | |
840 | void | |
841 | ieee80211_promisc(struct ieee80211vap *vap, bool on) | |
842 | #endif | |
f186073c | 843 | { |
4f655ef5 | 844 | struct ieee80211com *ic = vap->iv_ic; |
841ab66c | 845 | |
085ff963 MD |
846 | IEEE80211_LOCK_ASSERT(ic); |
847 | ||
4f655ef5 MD |
848 | if (on) { |
849 | if (++ic->ic_promisc == 1) | |
850 | ieee80211_runtask(ic, &ic->ic_promisc_task); | |
851 | } else { | |
852 | KASSERT(ic->ic_promisc > 0, ("%s: ic %p not promisc", | |
853 | __func__, ic)); | |
854 | if (--ic->ic_promisc == 0) | |
855 | ieee80211_runtask(ic, &ic->ic_promisc_task); | |
856 | } | |
857 | } | |
858 | ||
859 | /* | |
860 | * Count number of vaps in allmulti, and issue allmulti on | |
861 | * parent respectively. | |
862 | */ | |
863 | #if defined(__DragonFly__) | |
864 | void | |
865 | ieee80211_allmulti(struct ieee80211vap *vap, int on) | |
866 | #else | |
867 | void | |
868 | ieee80211_allmulti(struct ieee80211vap *vap, bool on) | |
869 | #endif | |
870 | { | |
871 | struct ieee80211com *ic = vap->iv_ic; | |
872 | ||
873 | IEEE80211_LOCK_ASSERT(ic); | |
874 | ||
875 | if (on) { | |
876 | if (++ic->ic_allmulti == 1) | |
877 | ieee80211_runtask(ic, &ic->ic_mcast_task); | |
878 | } else { | |
879 | KASSERT(ic->ic_allmulti > 0, ("%s: ic %p not allmulti", | |
880 | __func__, ic)); | |
881 | if (--ic->ic_allmulti == 0) | |
882 | ieee80211_runtask(ic, &ic->ic_mcast_task); | |
32176cfd RP |
883 | } |
884 | } | |
841ab66c | 885 | |
32176cfd RP |
886 | /* |
887 | * Synchronize flag bit state in the com structure | |
888 | * according to the state of all vap's. This is used, | |
889 | * for example, to handle state changes via ioctls. | |
890 | */ | |
891 | static void | |
892 | ieee80211_syncflag_locked(struct ieee80211com *ic, int flag) | |
893 | { | |
894 | struct ieee80211vap *vap; | |
895 | int bit; | |
841ab66c | 896 | |
085ff963 MD |
897 | IEEE80211_LOCK_ASSERT(ic); |
898 | ||
32176cfd RP |
899 | bit = 0; |
900 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) | |
901 | if (vap->iv_flags & flag) { | |
902 | bit = 1; | |
903 | break; | |
904 | } | |
905 | if (bit) | |
906 | ic->ic_flags |= flag; | |
907 | else | |
908 | ic->ic_flags &= ~flag; | |
909 | } | |
841ab66c | 910 | |
32176cfd RP |
911 | void |
912 | ieee80211_syncflag(struct ieee80211vap *vap, int flag) | |
913 | { | |
914 | struct ieee80211com *ic = vap->iv_ic; | |
915 | ||
085ff963 | 916 | IEEE80211_LOCK(ic); |
32176cfd RP |
917 | if (flag < 0) { |
918 | flag = -flag; | |
919 | vap->iv_flags &= ~flag; | |
920 | } else | |
921 | vap->iv_flags |= flag; | |
922 | ieee80211_syncflag_locked(ic, flag); | |
085ff963 | 923 | IEEE80211_UNLOCK(ic); |
32176cfd RP |
924 | } |
925 | ||
926 | /* | |
927 | * Synchronize flags_ht bit state in the com structure | |
928 | * according to the state of all vap's. This is used, | |
929 | * for example, to handle state changes via ioctls. | |
930 | */ | |
931 | static void | |
932 | ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag) | |
933 | { | |
934 | struct ieee80211vap *vap; | |
935 | int bit; | |
936 | ||
085ff963 MD |
937 | IEEE80211_LOCK_ASSERT(ic); |
938 | ||
32176cfd RP |
939 | bit = 0; |
940 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) | |
941 | if (vap->iv_flags_ht & flag) { | |
942 | bit = 1; | |
943 | break; | |
944 | } | |
945 | if (bit) | |
946 | ic->ic_flags_ht |= flag; | |
947 | else | |
948 | ic->ic_flags_ht &= ~flag; | |
949 | } | |
950 | ||
951 | void | |
952 | ieee80211_syncflag_ht(struct ieee80211vap *vap, int flag) | |
953 | { | |
954 | struct ieee80211com *ic = vap->iv_ic; | |
955 | ||
085ff963 | 956 | IEEE80211_LOCK(ic); |
32176cfd RP |
957 | if (flag < 0) { |
958 | flag = -flag; | |
959 | vap->iv_flags_ht &= ~flag; | |
960 | } else | |
961 | vap->iv_flags_ht |= flag; | |
962 | ieee80211_syncflag_ht_locked(ic, flag); | |
085ff963 | 963 | IEEE80211_UNLOCK(ic); |
32176cfd RP |
964 | } |
965 | ||
966 | /* | |
967 | * Synchronize flags_ext bit state in the com structure | |
968 | * according to the state of all vap's. This is used, | |
969 | * for example, to handle state changes via ioctls. | |
970 | */ | |
971 | static void | |
972 | ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) | |
973 | { | |
974 | struct ieee80211vap *vap; | |
975 | int bit; | |
976 | ||
085ff963 MD |
977 | IEEE80211_LOCK_ASSERT(ic); |
978 | ||
32176cfd RP |
979 | bit = 0; |
980 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) | |
981 | if (vap->iv_flags_ext & flag) { | |
982 | bit = 1; | |
983 | break; | |
984 | } | |
985 | if (bit) | |
986 | ic->ic_flags_ext |= flag; | |
987 | else | |
988 | ic->ic_flags_ext &= ~flag; | |
989 | } | |
990 | ||
991 | void | |
992 | ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) | |
993 | { | |
994 | struct ieee80211com *ic = vap->iv_ic; | |
995 | ||
085ff963 | 996 | IEEE80211_LOCK(ic); |
32176cfd RP |
997 | if (flag < 0) { |
998 | flag = -flag; | |
999 | vap->iv_flags_ext &= ~flag; | |
1000 | } else | |
1001 | vap->iv_flags_ext |= flag; | |
1002 | ieee80211_syncflag_ext_locked(ic, flag); | |
085ff963 | 1003 | IEEE80211_UNLOCK(ic); |
32176cfd RP |
1004 | } |
1005 | ||
1006 | static __inline int | |
1007 | mapgsm(u_int freq, u_int flags) | |
1008 | { | |
1009 | freq *= 10; | |
1010 | if (flags & IEEE80211_CHAN_QUARTER) | |
1011 | freq += 5; | |
1012 | else if (flags & IEEE80211_CHAN_HALF) | |
1013 | freq += 10; | |
1014 | else | |
1015 | freq += 20; | |
1016 | /* NB: there is no 907/20 wide but leave room */ | |
1017 | return (freq - 906*10) / 5; | |
1018 | } | |
1019 | ||
1020 | static __inline int | |
1021 | mappsb(u_int freq, u_int flags) | |
1022 | { | |
1023 | return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; | |
f186073c JS |
1024 | } |
1025 | ||
1026 | /* | |
1027 | * Convert MHz frequency to IEEE channel number. | |
1028 | */ | |
32176cfd | 1029 | int |
f186073c JS |
1030 | ieee80211_mhz2ieee(u_int freq, u_int flags) |
1031 | { | |
32176cfd RP |
1032 | #define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) |
1033 | if (flags & IEEE80211_CHAN_GSM) | |
1034 | return mapgsm(freq, flags); | |
f186073c JS |
1035 | if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ |
1036 | if (freq == 2484) | |
1037 | return 14; | |
1038 | if (freq < 2484) | |
32176cfd | 1039 | return ((int) freq - 2407) / 5; |
f186073c JS |
1040 | else |
1041 | return 15 + ((freq - 2512) / 20); | |
1042 | } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ | |
32176cfd RP |
1043 | if (freq <= 5000) { |
1044 | /* XXX check regdomain? */ | |
1045 | if (IS_FREQ_IN_PSB(freq)) | |
1046 | return mappsb(freq, flags); | |
1047 | return (freq - 4000) / 5; | |
1048 | } else | |
1049 | return (freq - 5000) / 5; | |
f186073c JS |
1050 | } else { /* either, guess */ |
1051 | if (freq == 2484) | |
1052 | return 14; | |
32176cfd RP |
1053 | if (freq < 2484) { |
1054 | if (907 <= freq && freq <= 922) | |
1055 | return mapgsm(freq, flags); | |
1056 | return ((int) freq - 2407) / 5; | |
1057 | } | |
1058 | if (freq < 5000) { | |
1059 | if (IS_FREQ_IN_PSB(freq)) | |
1060 | return mappsb(freq, flags); | |
1061 | else if (freq > 4900) | |
1062 | return (freq - 4000) / 5; | |
1063 | else | |
1064 | return 15 + ((freq - 2512) / 20); | |
1065 | } | |
f186073c JS |
1066 | return (freq - 5000) / 5; |
1067 | } | |
32176cfd | 1068 | #undef IS_FREQ_IN_PSB |
f186073c JS |
1069 | } |
1070 | ||
1071 | /* | |
1072 | * Convert channel to IEEE channel number. | |
1073 | */ | |
32176cfd | 1074 | int |
47f525a7 | 1075 | ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) |
f186073c | 1076 | { |
32176cfd | 1077 | if (c == NULL) { |
4f898719 | 1078 | ic_printf(ic, "invalid channel (NULL)\n"); |
f186073c JS |
1079 | return 0; /* XXX */ |
1080 | } | |
32176cfd | 1081 | return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); |
f186073c JS |
1082 | } |
1083 | ||
1084 | /* | |
1085 | * Convert IEEE channel number to MHz frequency. | |
1086 | */ | |
1087 | u_int | |
1088 | ieee80211_ieee2mhz(u_int chan, u_int flags) | |
1089 | { | |
32176cfd RP |
1090 | if (flags & IEEE80211_CHAN_GSM) |
1091 | return 907 + 5 * (chan / 10); | |
f186073c JS |
1092 | if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ |
1093 | if (chan == 14) | |
1094 | return 2484; | |
1095 | if (chan < 14) | |
1096 | return 2407 + chan*5; | |
1097 | else | |
1098 | return 2512 + ((chan-15)*20); | |
1099 | } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ | |
32176cfd RP |
1100 | if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { |
1101 | chan -= 37; | |
1102 | return 4940 + chan*5 + (chan % 5 ? 2 : 0); | |
1103 | } | |
f186073c JS |
1104 | return 5000 + (chan*5); |
1105 | } else { /* either, guess */ | |
32176cfd | 1106 | /* XXX can't distinguish PSB+GSM channels */ |
f186073c JS |
1107 | if (chan == 14) |
1108 | return 2484; | |
1109 | if (chan < 14) /* 0-13 */ | |
1110 | return 2407 + chan*5; | |
1111 | if (chan < 27) /* 15-26 */ | |
1112 | return 2512 + ((chan-15)*20); | |
1113 | return 5000 + (chan*5); | |
1114 | } | |
1115 | } | |
1116 | ||
4f655ef5 MD |
1117 | static __inline void |
1118 | set_extchan(struct ieee80211_channel *c) | |
1119 | { | |
1120 | ||
1121 | /* | |
1122 | * IEEE Std 802.11-2012, page 1738, subclause 20.3.15.4: | |
1123 | * "the secondary channel number shall be 'N + [1,-1] * 4' | |
1124 | */ | |
1125 | if (c->ic_flags & IEEE80211_CHAN_HT40U) | |
1126 | c->ic_extieee = c->ic_ieee + 4; | |
1127 | else if (c->ic_flags & IEEE80211_CHAN_HT40D) | |
1128 | c->ic_extieee = c->ic_ieee - 4; | |
1129 | else | |
1130 | c->ic_extieee = 0; | |
1131 | } | |
1132 | ||
1133 | static int | |
1134 | addchan(struct ieee80211_channel chans[], int maxchans, int *nchans, | |
1135 | uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags) | |
1136 | { | |
1137 | struct ieee80211_channel *c; | |
1138 | ||
1139 | if (*nchans >= maxchans) | |
1140 | return (ENOBUFS); | |
1141 | ||
1142 | c = &chans[(*nchans)++]; | |
1143 | c->ic_ieee = ieee; | |
1144 | c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags); | |
1145 | c->ic_maxregpower = maxregpower; | |
1146 | c->ic_maxpower = 2 * maxregpower; | |
1147 | c->ic_flags = flags; | |
1148 | set_extchan(c); | |
1149 | ||
1150 | return (0); | |
1151 | } | |
1152 | ||
1153 | static int | |
1154 | copychan_prev(struct ieee80211_channel chans[], int maxchans, int *nchans, | |
1155 | uint32_t flags) | |
1156 | { | |
1157 | struct ieee80211_channel *c; | |
1158 | ||
1159 | KASSERT(*nchans > 0, ("channel list is empty\n")); | |
1160 | ||
1161 | if (*nchans >= maxchans) | |
1162 | return (ENOBUFS); | |
1163 | ||
1164 | c = &chans[(*nchans)++]; | |
1165 | c[0] = c[-1]; | |
1166 | c->ic_flags = flags; | |
1167 | set_extchan(c); | |
1168 | ||
1169 | return (0); | |
1170 | } | |
1171 | ||
1172 | static void | |
1173 | getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40) | |
1174 | { | |
1175 | int nmodes; | |
1176 | ||
1177 | nmodes = 0; | |
1178 | if (isset(bands, IEEE80211_MODE_11B)) | |
1179 | flags[nmodes++] = IEEE80211_CHAN_B; | |
1180 | if (isset(bands, IEEE80211_MODE_11G)) | |
1181 | flags[nmodes++] = IEEE80211_CHAN_G; | |
1182 | if (isset(bands, IEEE80211_MODE_11NG)) | |
1183 | flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT20; | |
1184 | if (ht40) { | |
1185 | flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U; | |
1186 | flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D; | |
1187 | } | |
1188 | flags[nmodes] = 0; | |
1189 | } | |
1190 | ||
1191 | static void | |
1192 | getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40) | |
1193 | { | |
1194 | int nmodes; | |
1195 | ||
1196 | nmodes = 0; | |
1197 | if (isset(bands, IEEE80211_MODE_11A)) | |
1198 | flags[nmodes++] = IEEE80211_CHAN_A; | |
1199 | if (isset(bands, IEEE80211_MODE_11NA)) | |
1200 | flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; | |
1201 | if (ht40) { | |
1202 | flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U; | |
1203 | flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D; | |
1204 | } | |
1205 | flags[nmodes] = 0; | |
1206 | } | |
1207 | ||
1208 | static void | |
1209 | getflags(const uint8_t bands[], uint32_t flags[], int ht40) | |
1210 | { | |
1211 | ||
1212 | flags[0] = 0; | |
1213 | if (isset(bands, IEEE80211_MODE_11A) || | |
1214 | isset(bands, IEEE80211_MODE_11NA)) { | |
1215 | if (isset(bands, IEEE80211_MODE_11B) || | |
1216 | isset(bands, IEEE80211_MODE_11G) || | |
1217 | isset(bands, IEEE80211_MODE_11NG)) | |
1218 | return; | |
1219 | ||
1220 | getflags_5ghz(bands, flags, ht40); | |
1221 | } else | |
1222 | getflags_2ghz(bands, flags, ht40); | |
1223 | } | |
1224 | ||
1225 | /* | |
1226 | * Add one 20 MHz channel into specified channel list. | |
1227 | */ | |
1228 | int | |
1229 | ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans, | |
1230 | int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, | |
1231 | uint32_t chan_flags, const uint8_t bands[]) | |
1232 | { | |
1233 | uint32_t flags[IEEE80211_MODE_MAX]; | |
1234 | int i, error; | |
1235 | ||
1236 | getflags(bands, flags, 0); | |
1237 | KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); | |
1238 | ||
1239 | error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower, | |
1240 | flags[0] | chan_flags); | |
1241 | for (i = 1; flags[i] != 0 && error == 0; i++) { | |
1242 | error = copychan_prev(chans, maxchans, nchans, | |
1243 | flags[i] | chan_flags); | |
1244 | } | |
1245 | ||
1246 | return (error); | |
1247 | } | |
1248 | ||
1249 | static struct ieee80211_channel * | |
1250 | findchannel(struct ieee80211_channel chans[], int nchans, uint16_t freq, | |
1251 | uint32_t flags) | |
1252 | { | |
1253 | struct ieee80211_channel *c; | |
1254 | int i; | |
1255 | ||
1256 | flags &= IEEE80211_CHAN_ALLTURBO; | |
1257 | /* brute force search */ | |
1258 | for (i = 0; i < nchans; i++) { | |
1259 | c = &chans[i]; | |
1260 | if (c->ic_freq == freq && | |
1261 | (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) | |
1262 | return c; | |
1263 | } | |
1264 | return NULL; | |
1265 | } | |
1266 | ||
1267 | /* | |
1268 | * Add 40 MHz channel pair into specified channel list. | |
1269 | */ | |
1270 | int | |
1271 | ieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans, | |
1272 | int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags) | |
1273 | { | |
1274 | struct ieee80211_channel *cent, *extc; | |
1275 | uint16_t freq; | |
1276 | int error; | |
1277 | ||
1278 | freq = ieee80211_ieee2mhz(ieee, flags); | |
1279 | ||
1280 | /* | |
1281 | * Each entry defines an HT40 channel pair; find the | |
1282 | * center channel, then the extension channel above. | |
1283 | */ | |
1284 | flags |= IEEE80211_CHAN_HT20; | |
1285 | cent = findchannel(chans, *nchans, freq, flags); | |
1286 | if (cent == NULL) | |
1287 | return (EINVAL); | |
1288 | ||
1289 | extc = findchannel(chans, *nchans, freq + 20, flags); | |
1290 | if (extc == NULL) | |
1291 | return (ENOENT); | |
1292 | ||
1293 | flags &= ~IEEE80211_CHAN_HT; | |
1294 | error = addchan(chans, maxchans, nchans, cent->ic_ieee, cent->ic_freq, | |
1295 | maxregpower, flags | IEEE80211_CHAN_HT40U); | |
1296 | if (error != 0) | |
1297 | return (error); | |
1298 | ||
1299 | error = addchan(chans, maxchans, nchans, extc->ic_ieee, extc->ic_freq, | |
1300 | maxregpower, flags | IEEE80211_CHAN_HT40D); | |
1301 | ||
1302 | return (error); | |
1303 | } | |
1304 | ||
1305 | /* | |
1306 | * Adds channels into specified channel list (ieee[] array must be sorted). | |
1307 | * Channels are already sorted. | |
1308 | */ | |
1309 | static int | |
1310 | add_chanlist(struct ieee80211_channel chans[], int maxchans, int *nchans, | |
1311 | const uint8_t ieee[], int nieee, uint32_t flags[]) | |
1312 | { | |
1313 | uint16_t freq; | |
1314 | int i, j, error; | |
1315 | ||
1316 | #if defined(__DragonFly__) | |
1317 | error = 0; /* work-around GCC uninitialized variable warning */ | |
1318 | #endif | |
1319 | for (i = 0; i < nieee; i++) { | |
1320 | freq = ieee80211_ieee2mhz(ieee[i], flags[0]); | |
1321 | for (j = 0; flags[j] != 0; j++) { | |
1322 | if (flags[j] & IEEE80211_CHAN_HT40D) | |
1323 | if (i == 0 || ieee[i] < ieee[0] + 4 || | |
1324 | freq - 20 != | |
1325 | ieee80211_ieee2mhz(ieee[i] - 4, flags[j])) | |
1326 | continue; | |
1327 | if (flags[j] & IEEE80211_CHAN_HT40U) | |
1328 | if (i == nieee - 1 || | |
1329 | ieee[i] + 4 > ieee[nieee - 1] || | |
1330 | freq + 20 != | |
1331 | ieee80211_ieee2mhz(ieee[i] + 4, flags[j])) | |
1332 | continue; | |
1333 | ||
1334 | if (j == 0) { | |
1335 | error = addchan(chans, maxchans, nchans, | |
1336 | ieee[i], freq, 0, flags[j]); | |
1337 | } else { | |
1338 | error = copychan_prev(chans, maxchans, nchans, | |
1339 | flags[j]); | |
1340 | } | |
1341 | if (error != 0) | |
1342 | return (error); | |
1343 | } | |
1344 | } | |
1345 | ||
1346 | return (error); | |
1347 | } | |
1348 | ||
1349 | int | |
1350 | ieee80211_add_channel_list_2ghz(struct ieee80211_channel chans[], int maxchans, | |
1351 | int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], | |
1352 | int ht40) | |
1353 | { | |
1354 | uint32_t flags[IEEE80211_MODE_MAX]; | |
1355 | ||
1356 | getflags_2ghz(bands, flags, ht40); | |
1357 | KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); | |
1358 | ||
1359 | return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); | |
1360 | } | |
1361 | ||
1362 | int | |
1363 | ieee80211_add_channel_list_5ghz(struct ieee80211_channel chans[], int maxchans, | |
1364 | int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], | |
1365 | int ht40) | |
1366 | { | |
1367 | uint32_t flags[IEEE80211_MODE_MAX]; | |
1368 | ||
1369 | getflags_5ghz(bands, flags, ht40); | |
1370 | KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); | |
1371 | ||
1372 | return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); | |
1373 | } | |
1374 | ||
f186073c | 1375 | /* |
32176cfd RP |
1376 | * Locate a channel given a frequency+flags. We cache |
1377 | * the previous lookup to optimize switching between two | |
1378 | * channels--as happens with dynamic turbo. | |
f186073c | 1379 | */ |
32176cfd RP |
1380 | struct ieee80211_channel * |
1381 | ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) | |
f186073c | 1382 | { |
32176cfd | 1383 | struct ieee80211_channel *c; |
f186073c | 1384 | |
32176cfd RP |
1385 | flags &= IEEE80211_CHAN_ALLTURBO; |
1386 | c = ic->ic_prevchan; | |
1387 | if (c != NULL && c->ic_freq == freq && | |
1388 | (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) | |
1389 | return c; | |
1390 | /* brute force search */ | |
4f655ef5 | 1391 | return (findchannel(ic->ic_channels, ic->ic_nchans, freq, flags)); |
32176cfd | 1392 | } |
841ab66c | 1393 | |
32176cfd RP |
1394 | /* |
1395 | * Locate a channel given a channel number+flags. We cache | |
1396 | * the previous lookup to optimize switching between two | |
1397 | * channels--as happens with dynamic turbo. | |
1398 | */ | |
1399 | struct ieee80211_channel * | |
1400 | ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) | |
1401 | { | |
1402 | struct ieee80211_channel *c; | |
1403 | int i; | |
841ab66c | 1404 | |
32176cfd RP |
1405 | flags &= IEEE80211_CHAN_ALLTURBO; |
1406 | c = ic->ic_prevchan; | |
1407 | if (c != NULL && c->ic_ieee == ieee && | |
1408 | (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) | |
1409 | return c; | |
1410 | /* brute force search */ | |
1411 | for (i = 0; i < ic->ic_nchans; i++) { | |
1412 | c = &ic->ic_channels[i]; | |
1413 | if (c->ic_ieee == ieee && | |
1414 | (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) | |
1415 | return c; | |
1416 | } | |
1417 | return NULL; | |
1418 | } | |
1419 | ||
4f898719 IV |
1420 | /* |
1421 | * Lookup a channel suitable for the given rx status. | |
1422 | * | |
1423 | * This is used to find a channel for a frame (eg beacon, probe | |
1424 | * response) based purely on the received PHY information. | |
1425 | * | |
1426 | * For now it tries to do it based on R_FREQ / R_IEEE. | |
1427 | * This is enough for 11bg and 11a (and thus 11ng/11na) | |
1428 | * but it will not be enough for GSM, PSB channels and the | |
1429 | * like. It also doesn't know about legacy-turbog and | |
1430 | * legacy-turbo modes, which some offload NICs actually | |
1431 | * support in weird ways. | |
1432 | * | |
1433 | * Takes the ic and rxstatus; returns the channel or NULL | |
1434 | * if not found. | |
1435 | * | |
1436 | * XXX TODO: Add support for that when the need arises. | |
1437 | */ | |
1438 | struct ieee80211_channel * | |
1439 | ieee80211_lookup_channel_rxstatus(struct ieee80211vap *vap, | |
1440 | const struct ieee80211_rx_stats *rxs) | |
1441 | { | |
1442 | struct ieee80211com *ic = vap->iv_ic; | |
1443 | uint32_t flags; | |
1444 | struct ieee80211_channel *c; | |
1445 | ||
1446 | if (rxs == NULL) | |
1447 | return (NULL); | |
1448 | ||
1449 | /* | |
1450 | * Strictly speaking we only use freq for now, | |
1451 | * however later on we may wish to just store | |
1452 | * the ieee for verification. | |
1453 | */ | |
1454 | if ((rxs->r_flags & IEEE80211_R_FREQ) == 0) | |
1455 | return (NULL); | |
1456 | if ((rxs->r_flags & IEEE80211_R_IEEE) == 0) | |
1457 | return (NULL); | |
1458 | ||
1459 | /* | |
1460 | * If the rx status contains a valid ieee/freq, then | |
1461 | * ensure we populate the correct channel information | |
1462 | * in rxchan before passing it up to the scan infrastructure. | |
1463 | * Offload NICs will pass up beacons from all channels | |
1464 | * during background scans. | |
1465 | */ | |
1466 | ||
1467 | /* Determine a band */ | |
1468 | /* XXX should be done by the driver? */ | |
1469 | if (rxs->c_freq < 3000) { | |
1470 | flags = IEEE80211_CHAN_G; | |
1471 | } else { | |
1472 | flags = IEEE80211_CHAN_A; | |
1473 | } | |
1474 | ||
1475 | /* Channel lookup */ | |
1476 | c = ieee80211_find_channel(ic, rxs->c_freq, flags); | |
1477 | ||
1478 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_INPUT, | |
1479 | "%s: freq=%d, ieee=%d, flags=0x%08x; c=%p\n", | |
1480 | __func__, | |
1481 | (int) rxs->c_freq, | |
1482 | (int) rxs->c_ieee, | |
1483 | flags, | |
1484 | c); | |
1485 | ||
1486 | return (c); | |
1487 | } | |
1488 | ||
32176cfd RP |
1489 | static void |
1490 | addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) | |
1491 | { | |
1492 | #define ADD(_ic, _s, _o) \ | |
1493 | ifmedia_add(media, \ | |
1494 | IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) | |
1495 | static const u_int mopts[IEEE80211_MODE_MAX] = { | |
1496 | [IEEE80211_MODE_AUTO] = IFM_AUTO, | |
1497 | [IEEE80211_MODE_11A] = IFM_IEEE80211_11A, | |
1498 | [IEEE80211_MODE_11B] = IFM_IEEE80211_11B, | |
1499 | [IEEE80211_MODE_11G] = IFM_IEEE80211_11G, | |
1500 | [IEEE80211_MODE_FH] = IFM_IEEE80211_FH, | |
1501 | [IEEE80211_MODE_TURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, | |
1502 | [IEEE80211_MODE_TURBO_G] = IFM_IEEE80211_11G|IFM_IEEE80211_TURBO, | |
1503 | [IEEE80211_MODE_STURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, | |
1504 | [IEEE80211_MODE_HALF] = IFM_IEEE80211_11A, /* XXX */ | |
1505 | [IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */ | |
1506 | [IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA, | |
1507 | [IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG, | |
1508 | }; | |
1509 | u_int mopt; | |
1510 | ||
1511 | mopt = mopts[mode]; | |
1512 | if (addsta) | |
1513 | ADD(ic, mword, mopt); /* STA mode has no cap */ | |
1514 | if (caps & IEEE80211_C_IBSS) | |
1515 | ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); | |
1516 | if (caps & IEEE80211_C_HOSTAP) | |
1517 | ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); | |
1518 | if (caps & IEEE80211_C_AHDEMO) | |
1519 | ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); | |
1520 | if (caps & IEEE80211_C_MONITOR) | |
1521 | ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); | |
1522 | if (caps & IEEE80211_C_WDS) | |
1523 | ADD(media, mword, mopt | IFM_IEEE80211_WDS); | |
1524 | if (caps & IEEE80211_C_MBSS) | |
1525 | ADD(media, mword, mopt | IFM_IEEE80211_MBSS); | |
1526 | #undef ADD | |
1527 | } | |
1528 | ||
1529 | /* | |
1530 | * Setup the media data structures according to the channel and | |
1531 | * rate tables. | |
1532 | */ | |
1533 | static int | |
1534 | ieee80211_media_setup(struct ieee80211com *ic, | |
1535 | struct ifmedia *media, int caps, int addsta, | |
1536 | ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) | |
1537 | { | |
085ff963 MD |
1538 | int i, j, rate, maxrate, mword, r; |
1539 | enum ieee80211_phymode mode; | |
32176cfd RP |
1540 | const struct ieee80211_rateset *rs; |
1541 | struct ieee80211_rateset allrates; | |
f186073c JS |
1542 | |
1543 | /* | |
1544 | * Fill in media characteristics. | |
1545 | */ | |
32176cfd | 1546 | ifmedia_init(media, 0, media_change, media_stat); |
f186073c | 1547 | maxrate = 0; |
32176cfd RP |
1548 | /* |
1549 | * Add media for legacy operating modes. | |
1550 | */ | |
f186073c | 1551 | memset(&allrates, 0, sizeof(allrates)); |
32176cfd RP |
1552 | for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { |
1553 | if (isclr(ic->ic_modecaps, mode)) | |
f186073c | 1554 | continue; |
32176cfd | 1555 | addmedia(media, caps, addsta, mode, IFM_AUTO); |
f186073c JS |
1556 | if (mode == IEEE80211_MODE_AUTO) |
1557 | continue; | |
f186073c JS |
1558 | rs = &ic->ic_sup_rates[mode]; |
1559 | for (i = 0; i < rs->rs_nrates; i++) { | |
1560 | rate = rs->rs_rates[i]; | |
1561 | mword = ieee80211_rate2media(ic, rate, mode); | |
1562 | if (mword == 0) | |
1563 | continue; | |
32176cfd | 1564 | addmedia(media, caps, addsta, mode, mword); |
f186073c | 1565 | /* |
32176cfd | 1566 | * Add legacy rate to the collection of all rates. |
f186073c JS |
1567 | */ |
1568 | r = rate & IEEE80211_RATE_VAL; | |
1569 | for (j = 0; j < allrates.rs_nrates; j++) | |
1570 | if (allrates.rs_rates[j] == r) | |
1571 | break; | |
1572 | if (j == allrates.rs_nrates) { | |
1573 | /* unique, add to the set */ | |
1574 | allrates.rs_rates[j] = r; | |
1575 | allrates.rs_nrates++; | |
1576 | } | |
1577 | rate = (rate & IEEE80211_RATE_VAL) / 2; | |
1578 | if (rate > maxrate) | |
1579 | maxrate = rate; | |
1580 | } | |
f186073c JS |
1581 | } |
1582 | for (i = 0; i < allrates.rs_nrates; i++) { | |
1583 | mword = ieee80211_rate2media(ic, allrates.rs_rates[i], | |
1584 | IEEE80211_MODE_AUTO); | |
1585 | if (mword == 0) | |
1586 | continue; | |
32176cfd RP |
1587 | /* NB: remove media options from mword */ |
1588 | addmedia(media, caps, addsta, | |
1589 | IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); | |
f186073c | 1590 | } |
32176cfd RP |
1591 | /* |
1592 | * Add HT/11n media. Note that we do not have enough | |
1593 | * bits in the media subtype to express the MCS so we | |
1594 | * use a "placeholder" media subtype and any fixed MCS | |
1595 | * must be specified with a different mechanism. | |
1596 | */ | |
1597 | for (; mode <= IEEE80211_MODE_11NG; mode++) { | |
1598 | if (isclr(ic->ic_modecaps, mode)) | |
1599 | continue; | |
1600 | addmedia(media, caps, addsta, mode, IFM_AUTO); | |
1601 | addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); | |
1602 | } | |
1603 | if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || | |
1604 | isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { | |
1605 | addmedia(media, caps, addsta, | |
1606 | IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); | |
085ff963 MD |
1607 | i = ic->ic_txstream * 8 - 1; |
1608 | if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && | |
1609 | (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) | |
1610 | rate = ieee80211_htrates[i].ht40_rate_400ns; | |
1611 | else if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40)) | |
1612 | rate = ieee80211_htrates[i].ht40_rate_800ns; | |
1613 | else if ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)) | |
1614 | rate = ieee80211_htrates[i].ht20_rate_400ns; | |
1615 | else | |
1616 | rate = ieee80211_htrates[i].ht20_rate_800ns; | |
1617 | if (rate > maxrate) | |
1618 | maxrate = rate; | |
32176cfd RP |
1619 | } |
1620 | return maxrate; | |
1621 | } | |
1622 | ||
32176cfd RP |
1623 | /* XXX inline or eliminate? */ |
1624 | const struct ieee80211_rateset * | |
1625 | ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) | |
1626 | { | |
1627 | /* XXX does this work for 11ng basic rates? */ | |
1628 | return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; | |
f186073c JS |
1629 | } |
1630 | ||
841ab66c SZ |
1631 | void |
1632 | ieee80211_announce(struct ieee80211com *ic) | |
1633 | { | |
085ff963 MD |
1634 | int i, rate, mword; |
1635 | enum ieee80211_phymode mode; | |
32176cfd | 1636 | const struct ieee80211_rateset *rs; |
841ab66c | 1637 | |
32176cfd RP |
1638 | /* NB: skip AUTO since it has no rates */ |
1639 | for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { | |
1640 | if (isclr(ic->ic_modecaps, mode)) | |
841ab66c | 1641 | continue; |
4f898719 | 1642 | ic_printf(ic, "%s rates: ", ieee80211_phymode_name[mode]); |
841ab66c SZ |
1643 | rs = &ic->ic_sup_rates[mode]; |
1644 | for (i = 0; i < rs->rs_nrates; i++) { | |
32176cfd | 1645 | mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); |
841ab66c SZ |
1646 | if (mword == 0) |
1647 | continue; | |
32176cfd | 1648 | rate = ieee80211_media2rate(mword); |
a6ec04bc | 1649 | kprintf("%s%d%sMbps", (i != 0 ? " " : ""), |
32176cfd | 1650 | rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); |
841ab66c | 1651 | } |
a6ec04bc | 1652 | kprintf("\n"); |
841ab66c | 1653 | } |
32176cfd | 1654 | ieee80211_ht_announce(ic); |
841ab66c SZ |
1655 | } |
1656 | ||
32176cfd RP |
1657 | void |
1658 | ieee80211_announce_channels(struct ieee80211com *ic) | |
841ab66c | 1659 | { |
32176cfd RP |
1660 | const struct ieee80211_channel *c; |
1661 | char type; | |
1662 | int i, cw; | |
841ab66c | 1663 | |
32176cfd RP |
1664 | kprintf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); |
1665 | for (i = 0; i < ic->ic_nchans; i++) { | |
1666 | c = &ic->ic_channels[i]; | |
1667 | if (IEEE80211_IS_CHAN_ST(c)) | |
1668 | type = 'S'; | |
1669 | else if (IEEE80211_IS_CHAN_108A(c)) | |
1670 | type = 'T'; | |
1671 | else if (IEEE80211_IS_CHAN_108G(c)) | |
1672 | type = 'G'; | |
1673 | else if (IEEE80211_IS_CHAN_HT(c)) | |
1674 | type = 'n'; | |
1675 | else if (IEEE80211_IS_CHAN_A(c)) | |
1676 | type = 'a'; | |
1677 | else if (IEEE80211_IS_CHAN_ANYG(c)) | |
1678 | type = 'g'; | |
1679 | else if (IEEE80211_IS_CHAN_B(c)) | |
1680 | type = 'b'; | |
1681 | else | |
1682 | type = 'f'; | |
1683 | if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) | |
1684 | cw = 40; | |
1685 | else if (IEEE80211_IS_CHAN_HALF(c)) | |
1686 | cw = 10; | |
1687 | else if (IEEE80211_IS_CHAN_QUARTER(c)) | |
1688 | cw = 5; | |
1689 | else | |
1690 | cw = 20; | |
1691 | kprintf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" | |
1692 | , c->ic_ieee, c->ic_freq, type | |
1693 | , cw | |
1694 | , IEEE80211_IS_CHAN_HT40U(c) ? '+' : | |
1695 | IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' | |
1696 | , c->ic_maxregpower | |
1697 | , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 | |
1698 | , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 | |
1699 | ); | |
1700 | } | |
841ab66c SZ |
1701 | } |
1702 | ||
32176cfd RP |
1703 | static int |
1704 | media2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) | |
f186073c | 1705 | { |
f186073c JS |
1706 | switch (IFM_MODE(ime->ifm_media)) { |
1707 | case IFM_IEEE80211_11A: | |
32176cfd | 1708 | *mode = IEEE80211_MODE_11A; |
f186073c JS |
1709 | break; |
1710 | case IFM_IEEE80211_11B: | |
32176cfd | 1711 | *mode = IEEE80211_MODE_11B; |
f186073c JS |
1712 | break; |
1713 | case IFM_IEEE80211_11G: | |
32176cfd | 1714 | *mode = IEEE80211_MODE_11G; |
f186073c JS |
1715 | break; |
1716 | case IFM_IEEE80211_FH: | |
32176cfd RP |
1717 | *mode = IEEE80211_MODE_FH; |
1718 | break; | |
1719 | case IFM_IEEE80211_11NA: | |
1720 | *mode = IEEE80211_MODE_11NA; | |
1721 | break; | |
1722 | case IFM_IEEE80211_11NG: | |
1723 | *mode = IEEE80211_MODE_11NG; | |
f186073c JS |
1724 | break; |
1725 | case IFM_AUTO: | |
32176cfd | 1726 | *mode = IEEE80211_MODE_AUTO; |
f186073c JS |
1727 | break; |
1728 | default: | |
32176cfd | 1729 | return 0; |
f186073c JS |
1730 | } |
1731 | /* | |
841ab66c SZ |
1732 | * Turbo mode is an ``option''. |
1733 | * XXX does not apply to AUTO | |
f186073c JS |
1734 | */ |
1735 | if (ime->ifm_media & IFM_IEEE80211_TURBO) { | |
32176cfd RP |
1736 | if (*mode == IEEE80211_MODE_11A) { |
1737 | if (flags & IEEE80211_F_TURBOP) | |
1738 | *mode = IEEE80211_MODE_TURBO_A; | |
1739 | else | |
1740 | *mode = IEEE80211_MODE_STURBO_A; | |
1741 | } else if (*mode == IEEE80211_MODE_11G) | |
1742 | *mode = IEEE80211_MODE_TURBO_G; | |
841ab66c | 1743 | else |
32176cfd | 1744 | return 0; |
f186073c | 1745 | } |
32176cfd RP |
1746 | /* XXX HT40 +/- */ |
1747 | return 1; | |
1748 | } | |
f186073c | 1749 | |
32176cfd RP |
1750 | /* |
1751 | * Handle a media change request on the vap interface. | |
1752 | */ | |
1753 | int | |
1754 | ieee80211_media_change(struct ifnet *ifp) | |
1755 | { | |
1756 | struct ieee80211vap *vap = ifp->if_softc; | |
1757 | struct ifmedia_entry *ime = vap->iv_media.ifm_cur; | |
1758 | uint16_t newmode; | |
f186073c | 1759 | |
32176cfd RP |
1760 | if (!media2mode(ime, vap->iv_flags, &newmode)) |
1761 | return EINVAL; | |
1762 | if (vap->iv_des_mode != newmode) { | |
1763 | vap->iv_des_mode = newmode; | |
1764 | /* XXX kick state machine if up+running */ | |
f186073c | 1765 | } |
32176cfd RP |
1766 | return 0; |
1767 | } | |
f186073c | 1768 | |
32176cfd RP |
1769 | /* |
1770 | * Common code to calculate the media status word | |
1771 | * from the operating mode and channel state. | |
1772 | */ | |
1773 | static int | |
1774 | media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) | |
1775 | { | |
1776 | int status; | |
f186073c | 1777 | |
32176cfd RP |
1778 | status = IFM_IEEE80211; |
1779 | switch (opmode) { | |
1780 | case IEEE80211_M_STA: | |
1781 | break; | |
1782 | case IEEE80211_M_IBSS: | |
1783 | status |= IFM_IEEE80211_ADHOC; | |
1784 | break; | |
1785 | case IEEE80211_M_HOSTAP: | |
1786 | status |= IFM_IEEE80211_HOSTAP; | |
1787 | break; | |
1788 | case IEEE80211_M_MONITOR: | |
1789 | status |= IFM_IEEE80211_MONITOR; | |
1790 | break; | |
1791 | case IEEE80211_M_AHDEMO: | |
1792 | status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; | |
1793 | break; | |
1794 | case IEEE80211_M_WDS: | |
1795 | status |= IFM_IEEE80211_WDS; | |
1796 | break; | |
1797 | case IEEE80211_M_MBSS: | |
1798 | status |= IFM_IEEE80211_MBSS; | |
1799 | break; | |
f186073c | 1800 | } |
32176cfd RP |
1801 | if (IEEE80211_IS_CHAN_HTA(chan)) { |
1802 | status |= IFM_IEEE80211_11NA; | |
1803 | } else if (IEEE80211_IS_CHAN_HTG(chan)) { | |
1804 | status |= IFM_IEEE80211_11NG; | |
1805 | } else if (IEEE80211_IS_CHAN_A(chan)) { | |
1806 | status |= IFM_IEEE80211_11A; | |
1807 | } else if (IEEE80211_IS_CHAN_B(chan)) { | |
1808 | status |= IFM_IEEE80211_11B; | |
1809 | } else if (IEEE80211_IS_CHAN_ANYG(chan)) { | |
1810 | status |= IFM_IEEE80211_11G; | |
1811 | } else if (IEEE80211_IS_CHAN_FHSS(chan)) { | |
1812 | status |= IFM_IEEE80211_FH; | |
1813 | } | |
1814 | /* XXX else complain? */ | |
f186073c | 1815 | |
32176cfd RP |
1816 | if (IEEE80211_IS_CHAN_TURBO(chan)) |
1817 | status |= IFM_IEEE80211_TURBO; | |
1818 | #if 0 | |
1819 | if (IEEE80211_IS_CHAN_HT20(chan)) | |
1820 | status |= IFM_IEEE80211_HT20; | |
1821 | if (IEEE80211_IS_CHAN_HT40(chan)) | |
1822 | status |= IFM_IEEE80211_HT40; | |
1823 | #endif | |
1824 | return status; | |
1825 | } | |
1826 | ||
f186073c JS |
1827 | void |
1828 | ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) | |
1829 | { | |
32176cfd RP |
1830 | struct ieee80211vap *vap = ifp->if_softc; |
1831 | struct ieee80211com *ic = vap->iv_ic; | |
1832 | enum ieee80211_phymode mode; | |
f186073c JS |
1833 | |
1834 | imr->ifm_status = IFM_AVALID; | |
32176cfd RP |
1835 | /* |
1836 | * NB: use the current channel's mode to lock down a xmit | |
1837 | * rate only when running; otherwise we may have a mismatch | |
1838 | * in which case the rate will not be convertible. | |
1839 | */ | |
d98a0bcf MD |
1840 | if (vap->iv_state == IEEE80211_S_RUN || |
1841 | vap->iv_state == IEEE80211_S_SLEEP) { | |
f186073c | 1842 | imr->ifm_status |= IFM_ACTIVE; |
32176cfd RP |
1843 | mode = ieee80211_chan2mode(ic->ic_curchan); |
1844 | } else | |
1845 | mode = IEEE80211_MODE_AUTO; | |
1846 | imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); | |
841ab66c SZ |
1847 | /* |
1848 | * Calculate a current rate if possible. | |
1849 | */ | |
32176cfd | 1850 | if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { |
841ab66c SZ |
1851 | /* |
1852 | * A fixed rate is set, report that. | |
1853 | */ | |
841ab66c | 1854 | imr->ifm_active |= ieee80211_rate2media(ic, |
32176cfd RP |
1855 | vap->iv_txparms[mode].ucastrate, mode); |
1856 | } else if (vap->iv_opmode == IEEE80211_M_STA) { | |
841ab66c SZ |
1857 | /* |
1858 | * In station mode report the current transmit rate. | |
1859 | */ | |
841ab66c | 1860 | imr->ifm_active |= ieee80211_rate2media(ic, |
32176cfd | 1861 | vap->iv_bss->ni_txrate, mode); |
f186073c | 1862 | } else |
841ab66c | 1863 | imr->ifm_active |= IFM_AUTO; |
32176cfd RP |
1864 | if (imr->ifm_status & IFM_ACTIVE) |
1865 | imr->ifm_current = imr->ifm_active; | |
f186073c JS |
1866 | } |
1867 | ||
1868 | /* | |
1869 | * Set the current phy mode and recalculate the active channel | |
1870 | * set based on the available channels for this mode. Also | |
1871 | * select a new default/current channel if the current one is | |
1872 | * inappropriate for this mode. | |
1873 | */ | |
1874 | int | |
1875 | ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) | |
1876 | { | |
f186073c | 1877 | /* |
32176cfd RP |
1878 | * Adjust basic rates in 11b/11g supported rate set. |
1879 | * Note that if operating on a hal/quarter rate channel | |
1880 | * this is a noop as those rates sets are different | |
1881 | * and used instead. | |
f186073c | 1882 | */ |
32176cfd RP |
1883 | if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) |
1884 | ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); | |
f186073c JS |
1885 | |
1886 | ic->ic_curmode = mode; | |
841ab66c | 1887 | ieee80211_reset_erp(ic); /* reset ERP state */ |
841ab66c | 1888 | |
f186073c | 1889 | return 0; |
f186073c JS |
1890 | } |
1891 | ||
1892 | /* | |
32176cfd | 1893 | * Return the phy mode for with the specified channel. |
f186073c JS |
1894 | */ |
1895 | enum ieee80211_phymode | |
32176cfd | 1896 | ieee80211_chan2mode(const struct ieee80211_channel *chan) |
f186073c | 1897 | { |
32176cfd RP |
1898 | |
1899 | if (IEEE80211_IS_CHAN_HTA(chan)) | |
1900 | return IEEE80211_MODE_11NA; | |
1901 | else if (IEEE80211_IS_CHAN_HTG(chan)) | |
1902 | return IEEE80211_MODE_11NG; | |
1903 | else if (IEEE80211_IS_CHAN_108G(chan)) | |
1904 | return IEEE80211_MODE_TURBO_G; | |
1905 | else if (IEEE80211_IS_CHAN_ST(chan)) | |
1906 | return IEEE80211_MODE_STURBO_A; | |
1907 | else if (IEEE80211_IS_CHAN_TURBO(chan)) | |
841ab66c | 1908 | return IEEE80211_MODE_TURBO_A; |
32176cfd RP |
1909 | else if (IEEE80211_IS_CHAN_HALF(chan)) |
1910 | return IEEE80211_MODE_HALF; | |
1911 | else if (IEEE80211_IS_CHAN_QUARTER(chan)) | |
1912 | return IEEE80211_MODE_QUARTER; | |
1913 | else if (IEEE80211_IS_CHAN_A(chan)) | |
f186073c | 1914 | return IEEE80211_MODE_11A; |
32176cfd | 1915 | else if (IEEE80211_IS_CHAN_ANYG(chan)) |
f186073c | 1916 | return IEEE80211_MODE_11G; |
32176cfd | 1917 | else if (IEEE80211_IS_CHAN_B(chan)) |
f186073c | 1918 | return IEEE80211_MODE_11B; |
32176cfd RP |
1919 | else if (IEEE80211_IS_CHAN_FHSS(chan)) |
1920 | return IEEE80211_MODE_FH; | |
1921 | ||
1922 | /* NB: should not get here */ | |
1923 | kprintf("%s: cannot map channel to mode; freq %u flags 0x%x\n", | |
1924 | __func__, chan->ic_freq, chan->ic_flags); | |
1925 | return IEEE80211_MODE_11B; | |
1926 | } | |
1927 | ||
1928 | struct ratemedia { | |
1929 | u_int match; /* rate + mode */ | |
1930 | u_int media; /* if_media rate */ | |
1931 | }; | |
1932 | ||
1933 | static int | |
1934 | findmedia(const struct ratemedia rates[], int n, u_int match) | |
1935 | { | |
1936 | int i; | |
1937 | ||
1938 | for (i = 0; i < n; i++) | |
1939 | if (rates[i].match == match) | |
1940 | return rates[i].media; | |
1941 | return IFM_AUTO; | |
f186073c JS |
1942 | } |
1943 | ||
1944 | /* | |
32176cfd RP |
1945 | * Convert IEEE80211 rate value to ifmedia subtype. |
1946 | * Rate is either a legacy rate in units of 0.5Mbps | |
1947 | * or an MCS index. | |
f186073c JS |
1948 | */ |
1949 | int | |
1950 | ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) | |
1951 | { | |
32176cfd | 1952 | static const struct ratemedia rates[] = { |
f186073c JS |
1953 | { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, |
1954 | { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, | |
1955 | { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, | |
1956 | { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, | |
1957 | { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, | |
1958 | { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, | |
1959 | { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, | |
1960 | { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, | |
1961 | { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, | |
1962 | { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, | |
1963 | { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, | |
1964 | { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, | |
1965 | { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, | |
1966 | { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, | |
1967 | { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, | |
1968 | { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, | |
1969 | { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, | |
1970 | { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, | |
1971 | { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, | |
1972 | { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, | |
1973 | { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, | |
1974 | { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, | |
1975 | { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, | |
1976 | { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, | |
1977 | { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, | |
1978 | { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, | |
1979 | { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, | |
32176cfd RP |
1980 | { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, |
1981 | { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, | |
1982 | { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, | |
4f655ef5 | 1983 | /* NB: OFDM72 doesn't really exist so we don't handle it */ |
f186073c | 1984 | }; |
32176cfd RP |
1985 | static const struct ratemedia htrates[] = { |
1986 | { 0, IFM_IEEE80211_MCS }, | |
1987 | { 1, IFM_IEEE80211_MCS }, | |
1988 | { 2, IFM_IEEE80211_MCS }, | |
1989 | { 3, IFM_IEEE80211_MCS }, | |
1990 | { 4, IFM_IEEE80211_MCS }, | |
1991 | { 5, IFM_IEEE80211_MCS }, | |
1992 | { 6, IFM_IEEE80211_MCS }, | |
1993 | { 7, IFM_IEEE80211_MCS }, | |
1994 | { 8, IFM_IEEE80211_MCS }, | |
1995 | { 9, IFM_IEEE80211_MCS }, | |
1996 | { 10, IFM_IEEE80211_MCS }, | |
1997 | { 11, IFM_IEEE80211_MCS }, | |
1998 | { 12, IFM_IEEE80211_MCS }, | |
1999 | { 13, IFM_IEEE80211_MCS }, | |
2000 | { 14, IFM_IEEE80211_MCS }, | |
2001 | { 15, IFM_IEEE80211_MCS }, | |
085ff963 MD |
2002 | { 16, IFM_IEEE80211_MCS }, |
2003 | { 17, IFM_IEEE80211_MCS }, | |
2004 | { 18, IFM_IEEE80211_MCS }, | |
2005 | { 19, IFM_IEEE80211_MCS }, | |
2006 | { 20, IFM_IEEE80211_MCS }, | |
2007 | { 21, IFM_IEEE80211_MCS }, | |
2008 | { 22, IFM_IEEE80211_MCS }, | |
2009 | { 23, IFM_IEEE80211_MCS }, | |
2010 | { 24, IFM_IEEE80211_MCS }, | |
2011 | { 25, IFM_IEEE80211_MCS }, | |
2012 | { 26, IFM_IEEE80211_MCS }, | |
2013 | { 27, IFM_IEEE80211_MCS }, | |
2014 | { 28, IFM_IEEE80211_MCS }, | |
2015 | { 29, IFM_IEEE80211_MCS }, | |
2016 | { 30, IFM_IEEE80211_MCS }, | |
2017 | { 31, IFM_IEEE80211_MCS }, | |
2018 | { 32, IFM_IEEE80211_MCS }, | |
2019 | { 33, IFM_IEEE80211_MCS }, | |
2020 | { 34, IFM_IEEE80211_MCS }, | |
2021 | { 35, IFM_IEEE80211_MCS }, | |
2022 | { 36, IFM_IEEE80211_MCS }, | |
2023 | { 37, IFM_IEEE80211_MCS }, | |
2024 | { 38, IFM_IEEE80211_MCS }, | |
2025 | { 39, IFM_IEEE80211_MCS }, | |
2026 | { 40, IFM_IEEE80211_MCS }, | |
2027 | { 41, IFM_IEEE80211_MCS }, | |
2028 | { 42, IFM_IEEE80211_MCS }, | |
2029 | { 43, IFM_IEEE80211_MCS }, | |
2030 | { 44, IFM_IEEE80211_MCS }, | |
2031 | { 45, IFM_IEEE80211_MCS }, | |
2032 | { 46, IFM_IEEE80211_MCS }, | |
2033 | { 47, IFM_IEEE80211_MCS }, | |
2034 | { 48, IFM_IEEE80211_MCS }, | |
2035 | { 49, IFM_IEEE80211_MCS }, | |
2036 | { 50, IFM_IEEE80211_MCS }, | |
2037 | { 51, IFM_IEEE80211_MCS }, | |
2038 | { 52, IFM_IEEE80211_MCS }, | |
2039 | { 53, IFM_IEEE80211_MCS }, | |
2040 | { 54, IFM_IEEE80211_MCS }, | |
2041 | { 55, IFM_IEEE80211_MCS }, | |
2042 | { 56, IFM_IEEE80211_MCS }, | |
2043 | { 57, IFM_IEEE80211_MCS }, | |
2044 | { 58, IFM_IEEE80211_MCS }, | |
2045 | { 59, IFM_IEEE80211_MCS }, | |
2046 | { 60, IFM_IEEE80211_MCS }, | |
2047 | { 61, IFM_IEEE80211_MCS }, | |
2048 | { 62, IFM_IEEE80211_MCS }, | |
2049 | { 63, IFM_IEEE80211_MCS }, | |
2050 | { 64, IFM_IEEE80211_MCS }, | |
2051 | { 65, IFM_IEEE80211_MCS }, | |
2052 | { 66, IFM_IEEE80211_MCS }, | |
2053 | { 67, IFM_IEEE80211_MCS }, | |
2054 | { 68, IFM_IEEE80211_MCS }, | |
2055 | { 69, IFM_IEEE80211_MCS }, | |
2056 | { 70, IFM_IEEE80211_MCS }, | |
2057 | { 71, IFM_IEEE80211_MCS }, | |
2058 | { 72, IFM_IEEE80211_MCS }, | |
2059 | { 73, IFM_IEEE80211_MCS }, | |
2060 | { 74, IFM_IEEE80211_MCS }, | |
2061 | { 75, IFM_IEEE80211_MCS }, | |
2062 | { 76, IFM_IEEE80211_MCS }, | |
32176cfd RP |
2063 | }; |
2064 | int m; | |
f186073c | 2065 | |
32176cfd RP |
2066 | /* |
2067 | * Check 11n rates first for match as an MCS. | |
2068 | */ | |
2069 | if (mode == IEEE80211_MODE_11NA) { | |
2070 | if (rate & IEEE80211_RATE_MCS) { | |
2071 | rate &= ~IEEE80211_RATE_MCS; | |
085ff963 | 2072 | m = findmedia(htrates, nitems(htrates), rate); |
32176cfd RP |
2073 | if (m != IFM_AUTO) |
2074 | return m | IFM_IEEE80211_11NA; | |
2075 | } | |
2076 | } else if (mode == IEEE80211_MODE_11NG) { | |
2077 | /* NB: 12 is ambiguous, it will be treated as an MCS */ | |
2078 | if (rate & IEEE80211_RATE_MCS) { | |
2079 | rate &= ~IEEE80211_RATE_MCS; | |
085ff963 | 2080 | m = findmedia(htrates, nitems(htrates), rate); |
32176cfd RP |
2081 | if (m != IFM_AUTO) |
2082 | return m | IFM_IEEE80211_11NG; | |
2083 | } | |
2084 | } | |
2085 | rate &= IEEE80211_RATE_VAL; | |
f186073c JS |
2086 | switch (mode) { |
2087 | case IEEE80211_MODE_11A: | |
32176cfd RP |
2088 | case IEEE80211_MODE_HALF: /* XXX good 'nuf */ |
2089 | case IEEE80211_MODE_QUARTER: | |
2090 | case IEEE80211_MODE_11NA: | |
841ab66c | 2091 | case IEEE80211_MODE_TURBO_A: |
32176cfd | 2092 | case IEEE80211_MODE_STURBO_A: |
085ff963 MD |
2093 | return findmedia(rates, nitems(rates), |
2094 | rate | IFM_IEEE80211_11A); | |
f186073c | 2095 | case IEEE80211_MODE_11B: |
085ff963 MD |
2096 | return findmedia(rates, nitems(rates), |
2097 | rate | IFM_IEEE80211_11B); | |
f186073c | 2098 | case IEEE80211_MODE_FH: |
085ff963 MD |
2099 | return findmedia(rates, nitems(rates), |
2100 | rate | IFM_IEEE80211_FH); | |
f186073c JS |
2101 | case IEEE80211_MODE_AUTO: |
2102 | /* NB: ic may be NULL for some drivers */ | |
32176cfd | 2103 | if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) |
085ff963 | 2104 | return findmedia(rates, nitems(rates), |
32176cfd | 2105 | rate | IFM_IEEE80211_FH); |
f186073c JS |
2106 | /* NB: hack, 11g matches both 11b+11a rates */ |
2107 | /* fall thru... */ | |
2108 | case IEEE80211_MODE_11G: | |
32176cfd | 2109 | case IEEE80211_MODE_11NG: |
841ab66c | 2110 | case IEEE80211_MODE_TURBO_G: |
085ff963 | 2111 | return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11G); |
797b05a5 AL |
2112 | case IEEE80211_MODE_VHT_2GHZ: |
2113 | case IEEE80211_MODE_VHT_5GHZ: | |
2114 | /* XXX TODO: need to figure out mapping for VHT rates */ | |
2115 | return IFM_AUTO; | |
f186073c | 2116 | } |
f186073c | 2117 | return IFM_AUTO; |
f186073c JS |
2118 | } |
2119 | ||
2120 | int | |
2121 | ieee80211_media2rate(int mword) | |
2122 | { | |
f186073c JS |
2123 | static const int ieeerates[] = { |
2124 | -1, /* IFM_AUTO */ | |
2125 | 0, /* IFM_MANUAL */ | |
2126 | 0, /* IFM_NONE */ | |
2127 | 2, /* IFM_IEEE80211_FH1 */ | |
2128 | 4, /* IFM_IEEE80211_FH2 */ | |
2129 | 2, /* IFM_IEEE80211_DS1 */ | |
2130 | 4, /* IFM_IEEE80211_DS2 */ | |
2131 | 11, /* IFM_IEEE80211_DS5 */ | |
2132 | 22, /* IFM_IEEE80211_DS11 */ | |
2133 | 44, /* IFM_IEEE80211_DS22 */ | |
2134 | 12, /* IFM_IEEE80211_OFDM6 */ | |
2135 | 18, /* IFM_IEEE80211_OFDM9 */ | |
2136 | 24, /* IFM_IEEE80211_OFDM12 */ | |
2137 | 36, /* IFM_IEEE80211_OFDM18 */ | |
2138 | 48, /* IFM_IEEE80211_OFDM24 */ | |
2139 | 72, /* IFM_IEEE80211_OFDM36 */ | |
2140 | 96, /* IFM_IEEE80211_OFDM48 */ | |
2141 | 108, /* IFM_IEEE80211_OFDM54 */ | |
2142 | 144, /* IFM_IEEE80211_OFDM72 */ | |
32176cfd RP |
2143 | 0, /* IFM_IEEE80211_DS354k */ |
2144 | 0, /* IFM_IEEE80211_DS512k */ | |
2145 | 6, /* IFM_IEEE80211_OFDM3 */ | |
2146 | 9, /* IFM_IEEE80211_OFDM4 */ | |
2147 | 54, /* IFM_IEEE80211_OFDM27 */ | |
2148 | -1, /* IFM_IEEE80211_MCS */ | |
f186073c | 2149 | }; |
085ff963 | 2150 | return IFM_SUBTYPE(mword) < nitems(ieeerates) ? |
f186073c | 2151 | ieeerates[IFM_SUBTYPE(mword)] : 0; |
f186073c | 2152 | } |
f467e28e SZ |
2153 | |
2154 | /* | |
32176cfd RP |
2155 | * The following hash function is adapted from "Hash Functions" by Bob Jenkins |
2156 | * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). | |
f467e28e | 2157 | */ |
32176cfd RP |
2158 | #define mix(a, b, c) \ |
2159 | do { \ | |
2160 | a -= b; a -= c; a ^= (c >> 13); \ | |
2161 | b -= c; b -= a; b ^= (a << 8); \ | |
2162 | c -= a; c -= b; c ^= (b >> 13); \ | |
2163 | a -= b; a -= c; a ^= (c >> 12); \ | |
2164 | b -= c; b -= a; b ^= (a << 16); \ | |
2165 | c -= a; c -= b; c ^= (b >> 5); \ | |
2166 | a -= b; a -= c; a ^= (c >> 3); \ | |
2167 | b -= c; b -= a; b ^= (a << 10); \ | |
2168 | c -= a; c -= b; c ^= (b >> 15); \ | |
2169 | } while (/*CONSTCOND*/0) | |
2170 | ||
2171 | uint32_t | |
2172 | ieee80211_mac_hash(const struct ieee80211com *ic, | |
2173 | const uint8_t addr[IEEE80211_ADDR_LEN]) | |
f467e28e | 2174 | { |
32176cfd | 2175 | uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = ic->ic_hash_key; |
0dba45fe | 2176 | |
32176cfd RP |
2177 | b += addr[5] << 8; |
2178 | b += addr[4]; | |
2179 | a += addr[3] << 24; | |
2180 | a += addr[2] << 16; | |
2181 | a += addr[1] << 8; | |
2182 | a += addr[0]; | |
0dba45fe | 2183 | |
32176cfd RP |
2184 | mix(a, b, c); |
2185 | ||
2186 | return c; | |
0dba45fe | 2187 | } |
32176cfd | 2188 | #undef mix |
4f898719 IV |
2189 | |
2190 | char | |
2191 | ieee80211_channel_type_char(const struct ieee80211_channel *c) | |
2192 | { | |
2193 | if (IEEE80211_IS_CHAN_ST(c)) | |
2194 | return 'S'; | |
2195 | if (IEEE80211_IS_CHAN_108A(c)) | |
2196 | return 'T'; | |
2197 | if (IEEE80211_IS_CHAN_108G(c)) | |
2198 | return 'G'; | |
2199 | if (IEEE80211_IS_CHAN_HT(c)) | |
2200 | return 'n'; | |
2201 | if (IEEE80211_IS_CHAN_A(c)) | |
2202 | return 'a'; | |
2203 | if (IEEE80211_IS_CHAN_ANYG(c)) | |
2204 | return 'g'; | |
2205 | if (IEEE80211_IS_CHAN_B(c)) | |
2206 | return 'b'; | |
2207 | return 'f'; | |
2208 | } |