1 | /* $NetBSD: ieee80211.c,v 1.56 2015/08/24 22:21:26 pooka Exp $ */ |
2 | /*- |
3 | * Copyright (c) 2001 Atsushi Onoe |
4 | * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. The name of the author may not be used to endorse or promote products |
16 | * derived from this software without specific prior written permission. |
17 | * |
18 | * Alternatively, this software may be distributed under the terms of the |
19 | * GNU General Public License ("GPL") version 2 as published by the Free |
20 | * Software Foundation. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
23 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
25 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
27 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | */ |
33 | |
34 | #include <sys/cdefs.h> |
35 | #ifdef __FreeBSD__ |
36 | __FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.22 2005/08/10 16:22:29 sam Exp $" ); |
37 | #endif |
38 | #ifdef __NetBSD__ |
39 | __KERNEL_RCSID(0, "$NetBSD: ieee80211.c,v 1.56 2015/08/24 22:21:26 pooka Exp $" ); |
40 | #endif |
41 | |
42 | /* |
43 | * IEEE 802.11 generic handler |
44 | */ |
45 | |
46 | #ifdef _KERNEL_OPT |
47 | #include "opt_inet.h" |
48 | #endif |
49 | |
50 | #include <sys/param.h> |
51 | #include <sys/systm.h> |
52 | #include <sys/kernel.h> |
53 | |
54 | #include <sys/socket.h> |
55 | #include <sys/sockio.h> |
56 | #include <sys/endian.h> |
57 | #include <sys/errno.h> |
58 | #include <sys/proc.h> |
59 | #include <sys/sysctl.h> |
60 | |
61 | #include <net/if.h> |
62 | #include <net/if_media.h> |
63 | #include <net/if_arp.h> |
64 | #include <net/if_ether.h> |
65 | #include <net/if_llc.h> |
66 | |
67 | #include <net80211/ieee80211_netbsd.h> |
68 | #include <net80211/ieee80211_var.h> |
69 | #include <net80211/ieee80211_sysctl.h> |
70 | |
71 | #include <net/bpf.h> |
72 | |
73 | #ifdef INET |
74 | #include <netinet/in.h> |
75 | #include <net/if_ether.h> |
76 | #endif |
77 | |
78 | const struct ieee80211_channel ieee80211_channel_anyc = { |
79 | 0, 0 |
80 | }; |
81 | |
82 | struct ieee80211com_head ieee80211com_head = |
83 | LIST_HEAD_INITIALIZER(ieee80211com_head); |
84 | |
85 | const char *ieee80211_phymode_name[] = { |
86 | "auto" , /* IEEE80211_MODE_AUTO */ |
87 | "11a" , /* IEEE80211_MODE_11A */ |
88 | "11b" , /* IEEE80211_MODE_11B */ |
89 | "11g" , /* IEEE80211_MODE_11G */ |
90 | "FH" , /* IEEE80211_MODE_FH */ |
91 | "turboA" , /* IEEE80211_MODE_TURBO_A */ |
92 | "turboG" , /* IEEE80211_MODE_TURBO_G */ |
93 | }; |
94 | |
95 | /* list of all instances */ |
96 | SLIST_HEAD(ieee80211_list, ieee80211com); |
97 | static struct ieee80211_list ieee80211_list = |
98 | SLIST_HEAD_INITIALIZER(ieee80211_list); |
99 | static u_int8_t ieee80211_vapmap[32]; /* enough for 256 */ |
100 | |
101 | static void |
102 | ieee80211_add_vap(struct ieee80211com *ic) |
103 | { |
104 | #define N(a) (sizeof(a)/sizeof(a[0])) |
105 | int i; |
106 | int s; |
107 | u_int8_t b; |
108 | |
109 | s = splnet(); |
110 | ic->ic_vap = 0; |
111 | for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++) |
112 | ic->ic_vap += NBBY; |
113 | if (i == N(ieee80211_vapmap)) |
114 | panic("vap table full" ); |
115 | for (b = ieee80211_vapmap[i]; b & 1; b >>= 1) |
116 | ic->ic_vap++; |
117 | setbit(ieee80211_vapmap, ic->ic_vap); |
118 | SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); |
119 | splx(s); |
120 | #undef N |
121 | } |
122 | |
123 | static void |
124 | ieee80211_remove_vap(struct ieee80211com *ic) |
125 | { |
126 | int s; |
127 | |
128 | s = splnet(); |
129 | SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); |
130 | IASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY, |
131 | ("invalid vap id %d" , ic->ic_vap)); |
132 | IASSERT(isset(ieee80211_vapmap, ic->ic_vap), |
133 | ("vap id %d not allocated" , ic->ic_vap)); |
134 | clrbit(ieee80211_vapmap, ic->ic_vap); |
135 | splx(s); |
136 | } |
137 | |
138 | /* |
139 | * Default reset method for use with the ioctl support. This |
140 | * method is invoked after any state change in the 802.11 |
141 | * layer that should be propagated to the hardware but not |
142 | * require re-initialization of the 802.11 state machine (e.g |
143 | * rescanning for an ap). We always return ENETRESET which |
144 | * should cause the driver to re-initialize the device. Drivers |
145 | * can override this method to implement more optimized support. |
146 | */ |
147 | static int |
148 | ieee80211_default_reset(struct ifnet *ifp) |
149 | { |
150 | return ENETRESET; |
151 | } |
152 | |
153 | void |
154 | ieee80211_ifattach(struct ieee80211com *ic) |
155 | { |
156 | struct ifnet *ifp = ic->ic_ifp; |
157 | struct ieee80211_channel *c; |
158 | int i; |
159 | |
160 | #ifdef __NetBSD__ |
161 | ieee80211_init(); |
162 | #endif /* __NetBSD__ */ |
163 | |
164 | ether_ifattach(ifp, ic->ic_myaddr); |
165 | bpf_attach2(ifp, DLT_IEEE802_11, |
166 | sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); |
167 | |
168 | ieee80211_crypto_attach(ic); |
169 | |
170 | /* |
171 | * Fill in 802.11 available channel set, mark |
172 | * all available channels as active, and pick |
173 | * a default channel if not already specified. |
174 | */ |
175 | memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); |
176 | ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO; |
177 | for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { |
178 | c = &ic->ic_channels[i]; |
179 | if (c->ic_flags) { |
180 | /* |
181 | * Verify driver passed us valid data. |
182 | */ |
183 | if (i != ieee80211_chan2ieee(ic, c)) { |
184 | if_printf(ifp, "bad channel ignored; " |
185 | "freq %u flags %x number %u\n" , |
186 | c->ic_freq, c->ic_flags, i); |
187 | c->ic_flags = 0; /* NB: remove */ |
188 | continue; |
189 | } |
190 | setbit(ic->ic_chan_avail, i); |
191 | /* |
192 | * Identify mode capabilities. |
193 | */ |
194 | if (IEEE80211_IS_CHAN_A(c)) |
195 | ic->ic_modecaps |= 1<<IEEE80211_MODE_11A; |
196 | if (IEEE80211_IS_CHAN_B(c)) |
197 | ic->ic_modecaps |= 1<<IEEE80211_MODE_11B; |
198 | if (IEEE80211_IS_CHAN_PUREG(c)) |
199 | ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; |
200 | if (IEEE80211_IS_CHAN_FHSS(c)) |
201 | ic->ic_modecaps |= 1<<IEEE80211_MODE_FH; |
202 | if (IEEE80211_IS_CHAN_T(c)) |
203 | ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A; |
204 | if (IEEE80211_IS_CHAN_108G(c)) |
205 | ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G; |
206 | if (ic->ic_curchan == NULL) { |
207 | /* arbitrarily pick the first channel */ |
208 | ic->ic_curchan = &ic->ic_channels[i]; |
209 | } |
210 | } |
211 | } |
212 | /* validate ic->ic_curmode */ |
213 | if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0) |
214 | ic->ic_curmode = IEEE80211_MODE_AUTO; |
215 | ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ |
216 | #if 0 |
217 | /* |
218 | * Enable WME by default if we're capable. |
219 | */ |
220 | if (ic->ic_caps & IEEE80211_C_WME) |
221 | ic->ic_flags |= IEEE80211_F_WME; |
222 | #endif |
223 | (void) ieee80211_setmode(ic, ic->ic_curmode); |
224 | |
225 | if (ic->ic_bintval == 0) |
226 | ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; |
227 | ic->ic_bmisstimeout = 7*ic->ic_bintval; /* default 7 beacons */ |
228 | ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; |
229 | IEEE80211_BEACON_LOCK_INIT(ic, "beacon" ); |
230 | |
231 | if (ic->ic_lintval == 0) |
232 | ic->ic_lintval = ic->ic_bintval; |
233 | ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; |
234 | |
235 | LIST_INSERT_HEAD(&ieee80211com_head, ic, ic_list); |
236 | ieee80211_node_attach(ic); |
237 | ieee80211_proto_attach(ic); |
238 | |
239 | ieee80211_add_vap(ic); |
240 | |
241 | ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ |
242 | |
243 | /* |
244 | * Install a default reset method for the ioctl support. |
245 | * The driver is expected to fill this in before calling us. |
246 | */ |
247 | if (ic->ic_reset == NULL) |
248 | ic->ic_reset = ieee80211_default_reset; |
249 | } |
250 | |
251 | void |
252 | ieee80211_ifdetach(struct ieee80211com *ic) |
253 | { |
254 | struct ifnet *ifp = ic->ic_ifp; |
255 | |
256 | ieee80211_remove_vap(ic); |
257 | |
258 | ieee80211_sysctl_detach(ic); |
259 | ieee80211_proto_detach(ic); |
260 | ieee80211_crypto_detach(ic); |
261 | ieee80211_node_detach(ic); |
262 | LIST_REMOVE(ic, ic_list); |
263 | ifmedia_delete_instance(&ic->ic_media, IFM_INST_ANY); |
264 | |
265 | IEEE80211_BEACON_LOCK_DESTROY(ic); |
266 | |
267 | bpf_detach(ifp); |
268 | ether_ifdetach(ifp); |
269 | } |
270 | |
271 | /* |
272 | * Convert MHz frequency to IEEE channel number. |
273 | */ |
274 | u_int |
275 | ieee80211_mhz2ieee(u_int freq, u_int flags) |
276 | { |
277 | if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ |
278 | if (freq == 2484) |
279 | return 14; |
280 | if (freq < 2484) |
281 | return (freq - 2407) / 5; |
282 | else |
283 | return 15 + ((freq - 2512) / 20); |
284 | } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5 GHz band */ |
285 | return (freq - 5000) / 5; |
286 | } else { /* either, guess */ |
287 | if (freq == 2484) |
288 | return 14; |
289 | if (freq < 2484) |
290 | return (freq - 2407) / 5; |
291 | if (freq < 5000) |
292 | return 15 + ((freq - 2512) / 20); |
293 | return (freq - 5000) / 5; |
294 | } |
295 | } |
296 | |
297 | /* |
298 | * Convert channel to IEEE channel number. |
299 | */ |
300 | u_int |
301 | ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) |
302 | { |
303 | if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) |
304 | return c - ic->ic_channels; |
305 | else if (c == IEEE80211_CHAN_ANYC) |
306 | return IEEE80211_CHAN_ANY; |
307 | else if (c != NULL) { |
308 | if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n" , |
309 | c->ic_freq, c->ic_flags); |
310 | return 0; /* XXX */ |
311 | } else { |
312 | if_printf(ic->ic_ifp, "invalid channel (NULL)\n" ); |
313 | return 0; /* XXX */ |
314 | } |
315 | } |
316 | |
317 | /* |
318 | * Convert IEEE channel number to MHz frequency. |
319 | */ |
320 | u_int |
321 | ieee80211_ieee2mhz(u_int chan, u_int flags) |
322 | { |
323 | if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ |
324 | if (chan == 14) |
325 | return 2484; |
326 | if (chan < 14) |
327 | return 2407 + chan*5; |
328 | else |
329 | return 2512 + ((chan-15)*20); |
330 | } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5 GHz band */ |
331 | return 5000 + (chan*5); |
332 | } else { /* either, guess */ |
333 | if (chan == 14) |
334 | return 2484; |
335 | if (chan < 14) /* 0-13 */ |
336 | return 2407 + chan*5; |
337 | if (chan < 27) /* 15-26 */ |
338 | return 2512 + ((chan-15)*20); |
339 | return 5000 + (chan*5); |
340 | } |
341 | } |
342 | |
343 | /* |
344 | * Setup the media data structures according to the channel and |
345 | * rate tables. This must be called by the driver after |
346 | * ieee80211_attach and before most anything else. |
347 | */ |
348 | void |
349 | ieee80211_media_init(struct ieee80211com *ic, |
350 | ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) |
351 | { |
352 | #define ADD(_ic, _s, _o) \ |
353 | ifmedia_add(&(_ic)->ic_media, \ |
354 | IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) |
355 | struct ifnet *ifp = ic->ic_ifp; |
356 | struct ifmediareq imr; |
357 | int i, j, mode, rate, maxrate, mword, mopt, r; |
358 | const struct ieee80211_rateset *rs; |
359 | struct ieee80211_rateset allrates; |
360 | |
361 | /* |
362 | * Do late attach work that must wait for any subclass |
363 | * (i.e. driver) work such as overriding methods. |
364 | */ |
365 | ieee80211_node_lateattach(ic); |
366 | |
367 | #ifdef IEEE80211_NO_HOSTAP |
368 | ic->ic_caps &= ~IEEE80211_C_HOSTAP; |
369 | #endif /* IEEE80211_NO_HOSTAP */ |
370 | |
371 | /* |
372 | * Fill in media characteristics. |
373 | */ |
374 | ifmedia_init(&ic->ic_media, 0, media_change, media_stat); |
375 | maxrate = 0; |
376 | memset(&allrates, 0, sizeof(allrates)); |
377 | for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { |
378 | static const u_int mopts[] = { |
379 | IFM_AUTO, |
380 | IFM_IEEE80211_11A, |
381 | IFM_IEEE80211_11B, |
382 | IFM_IEEE80211_11G, |
383 | IFM_IEEE80211_FH, |
384 | IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, |
385 | IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, |
386 | }; |
387 | if ((ic->ic_modecaps & (1<<mode)) == 0) |
388 | continue; |
389 | mopt = mopts[mode]; |
390 | ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ |
391 | if (ic->ic_caps & IEEE80211_C_IBSS) |
392 | ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); |
393 | if (ic->ic_caps & IEEE80211_C_HOSTAP) |
394 | ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); |
395 | if (ic->ic_caps & IEEE80211_C_AHDEMO) |
396 | ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); |
397 | if (ic->ic_caps & IEEE80211_C_MONITOR) |
398 | ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); |
399 | if (mode == IEEE80211_MODE_AUTO) |
400 | continue; |
401 | rs = &ic->ic_sup_rates[mode]; |
402 | for (i = 0; i < rs->rs_nrates; i++) { |
403 | rate = rs->rs_rates[i]; |
404 | mword = ieee80211_rate2media(ic, rate, mode); |
405 | if (mword == 0) |
406 | continue; |
407 | ADD(ic, mword, mopt); |
408 | if (ic->ic_caps & IEEE80211_C_IBSS) |
409 | ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); |
410 | if (ic->ic_caps & IEEE80211_C_HOSTAP) |
411 | ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); |
412 | if (ic->ic_caps & IEEE80211_C_AHDEMO) |
413 | ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); |
414 | if (ic->ic_caps & IEEE80211_C_MONITOR) |
415 | ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); |
416 | /* |
417 | * Add rate to the collection of all rates. |
418 | */ |
419 | r = rate & IEEE80211_RATE_VAL; |
420 | for (j = 0; j < allrates.rs_nrates; j++) |
421 | if (allrates.rs_rates[j] == r) |
422 | break; |
423 | if (j == allrates.rs_nrates) { |
424 | /* unique, add to the set */ |
425 | allrates.rs_rates[j] = r; |
426 | allrates.rs_nrates++; |
427 | } |
428 | rate = (rate & IEEE80211_RATE_VAL) / 2; |
429 | if (rate > maxrate) |
430 | maxrate = rate; |
431 | } |
432 | } |
433 | for (i = 0; i < allrates.rs_nrates; i++) { |
434 | mword = ieee80211_rate2media(ic, allrates.rs_rates[i], |
435 | IEEE80211_MODE_AUTO); |
436 | if (mword == 0) |
437 | continue; |
438 | mword = IFM_SUBTYPE(mword); /* remove media options */ |
439 | ADD(ic, mword, 0); |
440 | if (ic->ic_caps & IEEE80211_C_IBSS) |
441 | ADD(ic, mword, IFM_IEEE80211_ADHOC); |
442 | if (ic->ic_caps & IEEE80211_C_HOSTAP) |
443 | ADD(ic, mword, IFM_IEEE80211_HOSTAP); |
444 | if (ic->ic_caps & IEEE80211_C_AHDEMO) |
445 | ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); |
446 | if (ic->ic_caps & IEEE80211_C_MONITOR) |
447 | ADD(ic, mword, IFM_IEEE80211_MONITOR); |
448 | } |
449 | ieee80211_media_status(ifp, &imr); |
450 | ifmedia_set(&ic->ic_media, imr.ifm_active); |
451 | |
452 | if (maxrate) |
453 | ifp->if_baudrate = IF_Mbps(maxrate); |
454 | #undef ADD |
455 | } |
456 | |
457 | void |
458 | ieee80211_announce(struct ieee80211com *ic) |
459 | { |
460 | struct ifnet *ifp = ic->ic_ifp; |
461 | int i, mode, rate, mword; |
462 | struct ieee80211_rateset *rs; |
463 | |
464 | for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { |
465 | if ((ic->ic_modecaps & (1<<mode)) == 0) |
466 | continue; |
467 | aprint_normal("%s: %s rates: " , ifp->if_xname, |
468 | ieee80211_phymode_name[mode]); |
469 | rs = &ic->ic_sup_rates[mode]; |
470 | for (i = 0; i < rs->rs_nrates; i++) { |
471 | rate = rs->rs_rates[i]; |
472 | mword = ieee80211_rate2media(ic, rate, mode); |
473 | if (mword == 0) |
474 | continue; |
475 | aprint_normal("%s%d%sMbps" , (i != 0 ? " " : "" ), |
476 | (rate & IEEE80211_RATE_VAL) / 2, |
477 | ((rate & 0x1) != 0 ? ".5" : "" )); |
478 | } |
479 | aprint_normal("\n" ); |
480 | } |
481 | } |
482 | |
483 | static int |
484 | findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) |
485 | { |
486 | #define IEEERATE(_ic,_m,_i) \ |
487 | ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) |
488 | int i, nrates = ic->ic_sup_rates[mode].rs_nrates; |
489 | for (i = 0; i < nrates; i++) |
490 | if (IEEERATE(ic, mode, i) == rate) |
491 | return i; |
492 | return -1; |
493 | #undef IEEERATE |
494 | } |
495 | |
496 | /* |
497 | * Find an instance by its mac address. |
498 | */ |
499 | struct ieee80211com * |
500 | ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) |
501 | { |
502 | int s; |
503 | struct ieee80211com *ic; |
504 | |
505 | s = splnet(); |
506 | SLIST_FOREACH(ic, &ieee80211_list, ic_next) |
507 | if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) |
508 | break; |
509 | splx(s); |
510 | return ic; |
511 | } |
512 | |
513 | static struct ieee80211com * |
514 | ieee80211_find_instance(struct ifnet *ifp) |
515 | { |
516 | int s; |
517 | struct ieee80211com *ic; |
518 | |
519 | s = splnet(); |
520 | /* XXX not right for multiple instances but works for now */ |
521 | SLIST_FOREACH(ic, &ieee80211_list, ic_next) |
522 | if (ic->ic_ifp == ifp) |
523 | break; |
524 | splx(s); |
525 | return ic; |
526 | } |
527 | |
528 | /* |
529 | * Handle a media change request. |
530 | */ |
531 | int |
532 | ieee80211_media_change(struct ifnet *ifp) |
533 | { |
534 | struct ieee80211com *ic; |
535 | struct ifmedia_entry *ime; |
536 | enum ieee80211_opmode newopmode; |
537 | enum ieee80211_phymode newphymode; |
538 | int i, j, newrate, error = 0; |
539 | |
540 | ic = ieee80211_find_instance(ifp); |
541 | if (!ic) { |
542 | if_printf(ifp, "%s: no 802.11 instance!\n" , __func__); |
543 | return EINVAL; |
544 | } |
545 | ime = ic->ic_media.ifm_cur; |
546 | /* |
547 | * First, identify the phy mode. |
548 | */ |
549 | switch (IFM_MODE(ime->ifm_media)) { |
550 | case IFM_IEEE80211_11A: |
551 | newphymode = IEEE80211_MODE_11A; |
552 | break; |
553 | case IFM_IEEE80211_11B: |
554 | newphymode = IEEE80211_MODE_11B; |
555 | break; |
556 | case IFM_IEEE80211_11G: |
557 | newphymode = IEEE80211_MODE_11G; |
558 | break; |
559 | case IFM_IEEE80211_FH: |
560 | newphymode = IEEE80211_MODE_FH; |
561 | break; |
562 | case IFM_AUTO: |
563 | newphymode = IEEE80211_MODE_AUTO; |
564 | break; |
565 | default: |
566 | return EINVAL; |
567 | } |
568 | /* |
569 | * Turbo mode is an ``option''. |
570 | * XXX does not apply to AUTO |
571 | */ |
572 | if (ime->ifm_media & IFM_IEEE80211_TURBO) { |
573 | if (newphymode == IEEE80211_MODE_11A) |
574 | newphymode = IEEE80211_MODE_TURBO_A; |
575 | else if (newphymode == IEEE80211_MODE_11G) |
576 | newphymode = IEEE80211_MODE_TURBO_G; |
577 | else |
578 | return EINVAL; |
579 | } |
580 | /* |
581 | * Validate requested mode is available. |
582 | */ |
583 | if ((ic->ic_modecaps & (1<<newphymode)) == 0) |
584 | return EINVAL; |
585 | |
586 | /* |
587 | * Next, the fixed/variable rate. |
588 | */ |
589 | i = -1; |
590 | if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { |
591 | /* |
592 | * Convert media subtype to rate. |
593 | */ |
594 | newrate = ieee80211_media2rate(ime->ifm_media); |
595 | if (newrate == 0) |
596 | return EINVAL; |
597 | /* |
598 | * Check the rate table for the specified/current phy. |
599 | */ |
600 | if (newphymode == IEEE80211_MODE_AUTO) { |
601 | /* |
602 | * In autoselect mode search for the rate. |
603 | */ |
604 | for (j = IEEE80211_MODE_11A; |
605 | j < IEEE80211_MODE_MAX; j++) { |
606 | if ((ic->ic_modecaps & (1<<j)) == 0) |
607 | continue; |
608 | i = findrate(ic, j, newrate); |
609 | if (i != -1) { |
610 | /* lock mode too */ |
611 | newphymode = j; |
612 | break; |
613 | } |
614 | } |
615 | } else { |
616 | i = findrate(ic, newphymode, newrate); |
617 | } |
618 | if (i == -1) /* mode/rate mismatch */ |
619 | return EINVAL; |
620 | } |
621 | /* NB: defer rate setting to later */ |
622 | |
623 | /* |
624 | * Deduce new operating mode but don't install it just yet. |
625 | */ |
626 | if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == |
627 | (IFM_IEEE80211_ADHOC|IFM_FLAG0)) |
628 | newopmode = IEEE80211_M_AHDEMO; |
629 | else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) |
630 | newopmode = IEEE80211_M_HOSTAP; |
631 | else if (ime->ifm_media & IFM_IEEE80211_ADHOC) |
632 | newopmode = IEEE80211_M_IBSS; |
633 | else if (ime->ifm_media & IFM_IEEE80211_MONITOR) |
634 | newopmode = IEEE80211_M_MONITOR; |
635 | else |
636 | newopmode = IEEE80211_M_STA; |
637 | |
638 | #ifndef IEEE80211_NO_HOSTAP |
639 | /* |
640 | * Autoselect doesn't make sense when operating as an AP. |
641 | * If no phy mode has been selected, pick one and lock it |
642 | * down so rate tables can be used in forming beacon frames |
643 | * and the like. |
644 | */ |
645 | if (newopmode == IEEE80211_M_HOSTAP && |
646 | newphymode == IEEE80211_MODE_AUTO) { |
647 | for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) |
648 | if (ic->ic_modecaps & (1<<j)) { |
649 | newphymode = j; |
650 | break; |
651 | } |
652 | } |
653 | #endif /* !IEEE80211_NO_HOSTAP */ |
654 | |
655 | /* |
656 | * Handle phy mode change. |
657 | */ |
658 | if (ic->ic_curmode != newphymode) { /* change phy mode */ |
659 | error = ieee80211_setmode(ic, newphymode); |
660 | if (error != 0) |
661 | return error; |
662 | error = ENETRESET; |
663 | } |
664 | |
665 | /* |
666 | * Committed to changes, install the rate setting. |
667 | */ |
668 | if (ic->ic_fixed_rate != i) { |
669 | ic->ic_fixed_rate = i; /* set fixed tx rate */ |
670 | error = ENETRESET; |
671 | } |
672 | |
673 | /* |
674 | * Handle operating mode change. |
675 | */ |
676 | if (ic->ic_opmode != newopmode) { |
677 | ic->ic_opmode = newopmode; |
678 | switch (newopmode) { |
679 | case IEEE80211_M_AHDEMO: |
680 | case IEEE80211_M_HOSTAP: |
681 | case IEEE80211_M_STA: |
682 | case IEEE80211_M_MONITOR: |
683 | ic->ic_flags &= ~IEEE80211_F_IBSSON; |
684 | break; |
685 | case IEEE80211_M_IBSS: |
686 | ic->ic_flags |= IEEE80211_F_IBSSON; |
687 | break; |
688 | } |
689 | /* |
690 | * Yech, slot time may change depending on the |
691 | * operating mode so reset it to be sure everything |
692 | * is setup appropriately. |
693 | */ |
694 | ieee80211_reset_erp(ic); |
695 | ieee80211_wme_initparams(ic); /* after opmode change */ |
696 | error = ENETRESET; |
697 | } |
698 | #ifdef notdef |
699 | if (error == 0) |
700 | ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); |
701 | #endif |
702 | return error; |
703 | } |
704 | |
705 | void |
706 | ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) |
707 | { |
708 | struct ieee80211com *ic; |
709 | struct ieee80211_rateset *rs; |
710 | |
711 | ic = ieee80211_find_instance(ifp); |
712 | if (!ic) { |
713 | if_printf(ifp, "%s: no 802.11 instance!\n" , __func__); |
714 | return; |
715 | } |
716 | imr->ifm_status = IFM_AVALID; |
717 | imr->ifm_active = IFM_IEEE80211; |
718 | if (ic->ic_state == IEEE80211_S_RUN) |
719 | imr->ifm_status |= IFM_ACTIVE; |
720 | /* |
721 | * Calculate a current rate if possible. |
722 | */ |
723 | if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { |
724 | /* |
725 | * A fixed rate is set, report that. |
726 | */ |
727 | rs = &ic->ic_sup_rates[ic->ic_curmode]; |
728 | imr->ifm_active |= ieee80211_rate2media(ic, |
729 | rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); |
730 | } else if (ic->ic_opmode == IEEE80211_M_STA) { |
731 | /* |
732 | * In station mode report the current transmit rate. |
733 | */ |
734 | rs = &ic->ic_bss->ni_rates; |
735 | imr->ifm_active |= ieee80211_rate2media(ic, |
736 | rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); |
737 | } else |
738 | imr->ifm_active |= IFM_AUTO; |
739 | switch (ic->ic_opmode) { |
740 | case IEEE80211_M_STA: |
741 | break; |
742 | case IEEE80211_M_IBSS: |
743 | imr->ifm_active |= IFM_IEEE80211_ADHOC; |
744 | break; |
745 | case IEEE80211_M_AHDEMO: |
746 | /* should not come here */ |
747 | break; |
748 | case IEEE80211_M_HOSTAP: |
749 | imr->ifm_active |= IFM_IEEE80211_HOSTAP; |
750 | break; |
751 | case IEEE80211_M_MONITOR: |
752 | imr->ifm_active |= IFM_IEEE80211_MONITOR; |
753 | break; |
754 | } |
755 | switch (ic->ic_curmode) { |
756 | case IEEE80211_MODE_11A: |
757 | imr->ifm_active |= IFM_IEEE80211_11A; |
758 | break; |
759 | case IEEE80211_MODE_11B: |
760 | imr->ifm_active |= IFM_IEEE80211_11B; |
761 | break; |
762 | case IEEE80211_MODE_11G: |
763 | imr->ifm_active |= IFM_IEEE80211_11G; |
764 | break; |
765 | case IEEE80211_MODE_FH: |
766 | imr->ifm_active |= IFM_IEEE80211_FH; |
767 | break; |
768 | case IEEE80211_MODE_TURBO_A: |
769 | imr->ifm_active |= IFM_IEEE80211_11A |
770 | | IFM_IEEE80211_TURBO; |
771 | break; |
772 | case IEEE80211_MODE_TURBO_G: |
773 | imr->ifm_active |= IFM_IEEE80211_11G |
774 | | IFM_IEEE80211_TURBO; |
775 | break; |
776 | } |
777 | } |
778 | |
779 | void |
780 | ieee80211_watchdog(struct ieee80211com *ic) |
781 | { |
782 | struct ieee80211_node_table *nt; |
783 | int need_inact_timer = 0; |
784 | |
785 | if (ic->ic_state != IEEE80211_S_INIT) { |
786 | if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) |
787 | ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); |
788 | nt = &ic->ic_scan; |
789 | if (nt->nt_inact_timer) { |
790 | if (--nt->nt_inact_timer == 0) |
791 | nt->nt_timeout(nt); |
792 | need_inact_timer += nt->nt_inact_timer; |
793 | } |
794 | nt = &ic->ic_sta; |
795 | if (nt->nt_inact_timer) { |
796 | if (--nt->nt_inact_timer == 0) |
797 | nt->nt_timeout(nt); |
798 | need_inact_timer += nt->nt_inact_timer; |
799 | } |
800 | } |
801 | if (ic->ic_mgt_timer != 0 || need_inact_timer) |
802 | ic->ic_ifp->if_timer = 1; |
803 | } |
804 | |
805 | const struct ieee80211_rateset ieee80211_std_rateset_11a = |
806 | { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; |
807 | |
808 | const struct ieee80211_rateset ieee80211_std_rateset_11b = |
809 | { 4, { 2, 4, 11, 22 } }; |
810 | |
811 | const struct ieee80211_rateset ieee80211_std_rateset_11g = |
812 | { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; |
813 | |
814 | /* |
815 | * Set the current phy mode and recalculate the active channel |
816 | * set based on the available channels for this mode. Also |
817 | * select a new default/current channel if the current one is |
818 | * inappropriate for this mode. |
819 | */ |
820 | int |
821 | ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) |
822 | { |
823 | #define N(a) (sizeof(a) / sizeof(a[0])) |
824 | static const u_int chanflags[] = { |
825 | 0, /* IEEE80211_MODE_AUTO */ |
826 | IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ |
827 | IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ |
828 | IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ |
829 | IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ |
830 | IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ |
831 | IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ |
832 | }; |
833 | struct ieee80211_channel *c; |
834 | u_int modeflags; |
835 | int i; |
836 | |
837 | /* validate new mode */ |
838 | if ((ic->ic_modecaps & (1<<mode)) == 0) { |
839 | IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, |
840 | "%s: mode %u not supported (caps 0x%x)\n" , |
841 | __func__, mode, ic->ic_modecaps); |
842 | return EINVAL; |
843 | } |
844 | |
845 | /* |
846 | * Verify at least one channel is present in the available |
847 | * channel list before committing to the new mode. |
848 | */ |
849 | IASSERT(mode < N(chanflags), ("Unexpected mode %u" , mode)); |
850 | modeflags = chanflags[mode]; |
851 | for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { |
852 | c = &ic->ic_channels[i]; |
853 | if (c->ic_flags == 0) |
854 | continue; |
855 | if (mode == IEEE80211_MODE_AUTO) { |
856 | /* ignore turbo channels for autoselect */ |
857 | if ((c->ic_flags & IEEE80211_CHAN_TURBO) == 0) |
858 | break; |
859 | } else { |
860 | if ((c->ic_flags & modeflags) == modeflags) |
861 | break; |
862 | } |
863 | } |
864 | if (i > IEEE80211_CHAN_MAX) { |
865 | IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, |
866 | "%s: no channels found for mode %u\n" , __func__, mode); |
867 | return EINVAL; |
868 | } |
869 | |
870 | /* |
871 | * Calculate the active channel set. |
872 | */ |
873 | memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); |
874 | for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { |
875 | c = &ic->ic_channels[i]; |
876 | if (c->ic_flags == 0) |
877 | continue; |
878 | if (mode == IEEE80211_MODE_AUTO) { |
879 | /* take anything but pure turbo channels */ |
880 | if ((c->ic_flags & IEEE80211_CHAN_TURBO) == 0) |
881 | setbit(ic->ic_chan_active, i); |
882 | } else { |
883 | if ((c->ic_flags & modeflags) == modeflags) |
884 | setbit(ic->ic_chan_active, i); |
885 | } |
886 | } |
887 | /* |
888 | * If no current/default channel is setup or the current |
889 | * channel is wrong for the mode then pick the first |
890 | * available channel from the active list. This is likely |
891 | * not the right one. |
892 | */ |
893 | if (ic->ic_ibss_chan == NULL || |
894 | isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { |
895 | for (i = 0; i <= IEEE80211_CHAN_MAX; i++) |
896 | if (isset(ic->ic_chan_active, i)) { |
897 | ic->ic_ibss_chan = &ic->ic_channels[i]; |
898 | break; |
899 | } |
900 | IASSERT(ic->ic_ibss_chan != NULL && |
901 | isset(ic->ic_chan_active, |
902 | ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), |
903 | ("Bad IBSS channel %u" , |
904 | ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); |
905 | } |
906 | /* |
907 | * If the desired channel is set but no longer valid then reset it. |
908 | */ |
909 | if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && |
910 | isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) |
911 | ic->ic_des_chan = IEEE80211_CHAN_ANYC; |
912 | |
913 | /* |
914 | * Do mode-specific rate setup. |
915 | */ |
916 | if (mode == IEEE80211_MODE_11G) { |
917 | /* |
918 | * Use a mixed 11b/11g rate set. |
919 | */ |
920 | ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], |
921 | IEEE80211_MODE_11G); |
922 | } else if (mode == IEEE80211_MODE_11B) { |
923 | /* |
924 | * Force pure 11b rate set. |
925 | */ |
926 | ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], |
927 | IEEE80211_MODE_11B); |
928 | } |
929 | /* |
930 | * Setup an initial rate set according to the |
931 | * current/default channel selected above. This |
932 | * will be changed when scanning but must exist |
933 | * now so driver have a consistent state of ic_ibss_chan. |
934 | */ |
935 | if (ic->ic_bss) /* NB: can be called before lateattach */ |
936 | ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; |
937 | |
938 | ic->ic_curmode = mode; |
939 | ieee80211_reset_erp(ic); /* reset ERP state */ |
940 | ieee80211_wme_initparams(ic); /* reset WME stat */ |
941 | |
942 | return 0; |
943 | #undef N |
944 | } |
945 | |
946 | /* |
947 | * Return the phy mode for with the specified channel so the |
948 | * caller can select a rate set. This is problematic for channels |
949 | * where multiple operating modes are possible (e.g. 11g+11b). |
950 | * In those cases we defer to the current operating mode when set. |
951 | */ |
952 | enum ieee80211_phymode |
953 | ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) |
954 | { |
955 | if (IEEE80211_IS_CHAN_T(chan)) { |
956 | return IEEE80211_MODE_TURBO_A; |
957 | } else if (IEEE80211_IS_CHAN_5GHZ(chan)) { |
958 | return IEEE80211_MODE_11A; |
959 | } else if (IEEE80211_IS_CHAN_FHSS(chan)) |
960 | return IEEE80211_MODE_FH; |
961 | else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { |
962 | /* |
963 | * This assumes all 11g channels are also usable |
964 | * for 11b, which is currently true. |
965 | */ |
966 | if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) |
967 | return IEEE80211_MODE_TURBO_G; |
968 | if (ic->ic_curmode == IEEE80211_MODE_11B) |
969 | return IEEE80211_MODE_11B; |
970 | return IEEE80211_MODE_11G; |
971 | } else |
972 | return IEEE80211_MODE_11B; |
973 | } |
974 | |
975 | /* |
976 | * convert IEEE80211 rate value to ifmedia subtype. |
977 | * ieee80211 rate is in unit of 0.5Mbps. |
978 | */ |
979 | int |
980 | ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) |
981 | { |
982 | #define N(a) (sizeof(a) / sizeof(a[0])) |
983 | static const struct { |
984 | u_int m; /* rate + mode */ |
985 | u_int r; /* if_media rate */ |
986 | } rates[] = { |
987 | { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, |
988 | { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, |
989 | { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, |
990 | { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, |
991 | { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, |
992 | { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, |
993 | { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, |
994 | { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, |
995 | { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, |
996 | { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, |
997 | { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, |
998 | { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, |
999 | { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, |
1000 | { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, |
1001 | { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, |
1002 | { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, |
1003 | { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, |
1004 | { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, |
1005 | { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, |
1006 | { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, |
1007 | { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, |
1008 | { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, |
1009 | { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, |
1010 | { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, |
1011 | { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, |
1012 | { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, |
1013 | { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, |
1014 | /* NB: OFDM72 doesn't realy exist so we don't handle it */ |
1015 | }; |
1016 | u_int mask, i; |
1017 | |
1018 | mask = rate & IEEE80211_RATE_VAL; |
1019 | switch (mode) { |
1020 | case IEEE80211_MODE_11A: |
1021 | case IEEE80211_MODE_TURBO_A: |
1022 | mask |= IFM_IEEE80211_11A; |
1023 | break; |
1024 | case IEEE80211_MODE_11B: |
1025 | mask |= IFM_IEEE80211_11B; |
1026 | break; |
1027 | case IEEE80211_MODE_FH: |
1028 | mask |= IFM_IEEE80211_FH; |
1029 | break; |
1030 | case IEEE80211_MODE_AUTO: |
1031 | /* NB: ic may be NULL for some drivers */ |
1032 | if (ic && ic->ic_phytype == IEEE80211_T_FH) { |
1033 | mask |= IFM_IEEE80211_FH; |
1034 | break; |
1035 | } |
1036 | /* NB: hack, 11g matches both 11b+11a rates */ |
1037 | /* fall thru... */ |
1038 | case IEEE80211_MODE_11G: |
1039 | case IEEE80211_MODE_TURBO_G: |
1040 | mask |= IFM_IEEE80211_11G; |
1041 | break; |
1042 | } |
1043 | for (i = 0; i < N(rates); i++) |
1044 | if (rates[i].m == mask) |
1045 | return rates[i].r; |
1046 | return IFM_AUTO; |
1047 | #undef N |
1048 | } |
1049 | |
1050 | int |
1051 | ieee80211_media2rate(int mword) |
1052 | { |
1053 | #define N(a) (sizeof(a) / sizeof(a[0])) |
1054 | static const int ieeerates[] = { |
1055 | -1, /* IFM_AUTO */ |
1056 | 0, /* IFM_MANUAL */ |
1057 | 0, /* IFM_NONE */ |
1058 | 2, /* IFM_IEEE80211_FH1 */ |
1059 | 4, /* IFM_IEEE80211_FH2 */ |
1060 | 4, /* IFM_IEEE80211_DS2 */ |
1061 | 11, /* IFM_IEEE80211_DS5 */ |
1062 | 22, /* IFM_IEEE80211_DS11 */ |
1063 | 2, /* IFM_IEEE80211_DS1 */ |
1064 | 44, /* IFM_IEEE80211_DS22 */ |
1065 | 12, /* IFM_IEEE80211_OFDM6 */ |
1066 | 18, /* IFM_IEEE80211_OFDM9 */ |
1067 | 24, /* IFM_IEEE80211_OFDM12 */ |
1068 | 36, /* IFM_IEEE80211_OFDM18 */ |
1069 | 48, /* IFM_IEEE80211_OFDM24 */ |
1070 | 72, /* IFM_IEEE80211_OFDM36 */ |
1071 | 96, /* IFM_IEEE80211_OFDM48 */ |
1072 | 108, /* IFM_IEEE80211_OFDM54 */ |
1073 | 144, /* IFM_IEEE80211_OFDM72 */ |
1074 | }; |
1075 | return IFM_SUBTYPE(mword) < N(ieeerates) ? |
1076 | ieeerates[IFM_SUBTYPE(mword)] : 0; |
1077 | #undef N |
1078 | } |
1079 | |