ieee80211.c revision 173861
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3170360Ssam * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116742Ssam *
15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25116742Ssam */
26116742Ssam
27116742Ssam#include <sys/cdefs.h>
28116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211.c 173861 2007-11-23 05:57:20Z sam $");
29116742Ssam
30116742Ssam/*
31116742Ssam * IEEE 802.11 generic handler
32116742Ssam */
33116742Ssam
34116742Ssam#include <sys/param.h>
35116742Ssam#include <sys/systm.h>
36116742Ssam#include <sys/kernel.h>
37138568Ssam
38116742Ssam#include <sys/socket.h>
39116742Ssam
40116742Ssam#include <net/if.h>
41116742Ssam#include <net/if_media.h>
42116742Ssam#include <net/ethernet.h>
43116742Ssam
44116742Ssam#include <net80211/ieee80211_var.h>
45116742Ssam
46116742Ssam#include <net/bpf.h>
47116742Ssam
48139502Ssamconst char *ieee80211_phymode_name[] = {
49116742Ssam	"auto",		/* IEEE80211_MODE_AUTO */
50116742Ssam	"11a",		/* IEEE80211_MODE_11A */
51116742Ssam	"11b",		/* IEEE80211_MODE_11B */
52116742Ssam	"11g",		/* IEEE80211_MODE_11G */
53124543Sonoe	"FH",		/* IEEE80211_MODE_FH */
54138568Ssam	"turboA",	/* IEEE80211_MODE_TURBO_A */
55138568Ssam	"turboG",	/* IEEE80211_MODE_TURBO_G */
56170530Ssam	"sturboA",	/* IEEE80211_MODE_STURBO_A */
57170530Ssam	"11na",		/* IEEE80211_MODE_11NA */
58170530Ssam	"11ng",		/* IEEE80211_MODE_11NG */
59116742Ssam};
60116742Ssam
61164645Ssam/*
62164645Ssam * Default supported rates for 802.11 operation (in IEEE .5Mb units).
63164645Ssam */
64164645Ssam#define	B(r)	((r) | IEEE80211_RATE_BASIC)
65164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11a =
66164645Ssam	{ 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } };
67165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_half =
68165569Ssam	{ 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } };
69165569Ssamstatic const struct ieee80211_rateset ieee80211_rateset_quarter =
70165569Ssam	{ 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } };
71164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11b =
72164645Ssam	{ 4, { B(2), B(4), B(11), B(22) } };
73164645Ssam/* NB: OFDM rates are handled specially based on mode */
74164645Ssamstatic const struct ieee80211_rateset ieee80211_rateset_11g =
75164645Ssam	{ 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
76164645Ssam#undef B
77164645Ssam
78170530Ssamstatic	int media_status(enum ieee80211_opmode ,
79170530Ssam		const struct ieee80211_channel *);
80170530Ssam
81138568Ssam/* list of all instances */
82138568SsamSLIST_HEAD(ieee80211_list, ieee80211com);
83138568Ssamstatic struct ieee80211_list ieee80211_list =
84138568Ssam	SLIST_HEAD_INITIALIZER(ieee80211_list);
85170530Ssamstatic uint8_t ieee80211_vapmap[32];		/* enough for 256 */
86138568Ssamstatic struct mtx ieee80211_vap_mtx;
87138568SsamMTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
88138568Ssam
89138568Ssamstatic void
90138568Ssamieee80211_add_vap(struct ieee80211com *ic)
91138568Ssam{
92138568Ssam#define	N(a)	(sizeof(a)/sizeof(a[0]))
93138568Ssam	int i;
94170530Ssam	uint8_t b;
95138568Ssam
96138568Ssam	mtx_lock(&ieee80211_vap_mtx);
97138568Ssam	ic->ic_vap = 0;
98138568Ssam	for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++)
99138568Ssam		ic->ic_vap += NBBY;
100138568Ssam	if (i == N(ieee80211_vapmap))
101138568Ssam		panic("vap table full");
102138568Ssam	for (b = ieee80211_vapmap[i]; b & 1; b >>= 1)
103138568Ssam		ic->ic_vap++;
104138568Ssam	setbit(ieee80211_vapmap, ic->ic_vap);
105138568Ssam	SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next);
106138568Ssam	mtx_unlock(&ieee80211_vap_mtx);
107138568Ssam#undef N
108138568Ssam}
109138568Ssam
110138568Ssamstatic void
111138568Ssamieee80211_remove_vap(struct ieee80211com *ic)
112138568Ssam{
113138568Ssam	mtx_lock(&ieee80211_vap_mtx);
114138568Ssam	SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next);
115138568Ssam	KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY,
116138568Ssam		("invalid vap id %d", ic->ic_vap));
117138568Ssam	KASSERT(isset(ieee80211_vapmap, ic->ic_vap),
118138568Ssam		("vap id %d not allocated", ic->ic_vap));
119138568Ssam	clrbit(ieee80211_vapmap, ic->ic_vap);
120138568Ssam	mtx_unlock(&ieee80211_vap_mtx);
121138568Ssam}
122138568Ssam
123140915Ssam/*
124140915Ssam * Default reset method for use with the ioctl support.  This
125140915Ssam * method is invoked after any state change in the 802.11
126140915Ssam * layer that should be propagated to the hardware but not
127140915Ssam * require re-initialization of the 802.11 state machine (e.g
128140915Ssam * rescanning for an ap).  We always return ENETRESET which
129140915Ssam * should cause the driver to re-initialize the device. Drivers
130140915Ssam * can override this method to implement more optimized support.
131140915Ssam */
132140915Ssamstatic int
133140915Ssamieee80211_default_reset(struct ifnet *ifp)
134140915Ssam{
135140915Ssam	return ENETRESET;
136140915Ssam}
137140915Ssam
138165569Ssam/*
139165569Ssam * Fill in 802.11 available channel set, mark
140165569Ssam * all available channels as active, and pick
141165569Ssam * a default channel if not already specified.
142165569Ssam */
143165569Ssamstatic void
144165569Ssamieee80211_chan_init(struct ieee80211com *ic)
145116742Ssam{
146165569Ssam#define	DEFAULTRATES(m, def) do { \
147167468Ssam	if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \
148165574Ssam		ic->ic_sup_rates[m] = def; \
149165569Ssam} while (0)
150116742Ssam	struct ieee80211_channel *c;
151116742Ssam	int i;
152116742Ssam
153170530Ssam	KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
154170530Ssam		("invalid number of channels specified: %u", ic->ic_nchans));
155116742Ssam	memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
156167468Ssam	setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
157170530Ssam	for (i = 0; i < ic->ic_nchans; i++) {
158116742Ssam		c = &ic->ic_channels[i];
159170530Ssam		KASSERT(c->ic_flags != 0, ("channel with no flags"));
160170530Ssam		KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX,
161170530Ssam			("channel with bogus ieee number %u", c->ic_ieee));
162170530Ssam		setbit(ic->ic_chan_avail, c->ic_ieee);
163170530Ssam		/*
164170530Ssam		 * Identify mode capabilities.
165170530Ssam		 */
166170530Ssam		if (IEEE80211_IS_CHAN_A(c))
167170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_11A);
168170530Ssam		if (IEEE80211_IS_CHAN_B(c))
169170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_11B);
170170530Ssam		if (IEEE80211_IS_CHAN_ANYG(c))
171170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_11G);
172170530Ssam		if (IEEE80211_IS_CHAN_FHSS(c))
173170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_FH);
174170530Ssam		if (IEEE80211_IS_CHAN_108A(c))
175170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A);
176170530Ssam		if (IEEE80211_IS_CHAN_108G(c))
177170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G);
178170530Ssam		if (IEEE80211_IS_CHAN_ST(c))
179170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A);
180170530Ssam		if (IEEE80211_IS_CHAN_HTA(c))
181170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_11NA);
182170530Ssam		if (IEEE80211_IS_CHAN_HTG(c))
183170530Ssam			setbit(ic->ic_modecaps, IEEE80211_MODE_11NG);
184116742Ssam	}
185170530Ssam	/* initialize candidate channels to all available */
186170530Ssam	memcpy(ic->ic_chan_active, ic->ic_chan_avail,
187170530Ssam		sizeof(ic->ic_chan_avail));
188164645Ssam
189170530Ssam	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
190170530Ssam	ic->ic_bsschan = IEEE80211_CHAN_ANYC;
191172233Ssam	ic->ic_prevchan = NULL;
192170530Ssam	/* arbitrarily pick the first channel */
193170530Ssam	ic->ic_curchan = &ic->ic_channels[0];
194170530Ssam
195164645Ssam	/* fillin well-known rate sets if driver has not specified */
196165569Ssam	DEFAULTRATES(IEEE80211_MODE_11B,	 ieee80211_rateset_11b);
197165569Ssam	DEFAULTRATES(IEEE80211_MODE_11G,	 ieee80211_rateset_11g);
198165569Ssam	DEFAULTRATES(IEEE80211_MODE_11A,	 ieee80211_rateset_11a);
199165569Ssam	DEFAULTRATES(IEEE80211_MODE_TURBO_A,	 ieee80211_rateset_11a);
200165569Ssam	DEFAULTRATES(IEEE80211_MODE_TURBO_G,	 ieee80211_rateset_11g);
201165569Ssam
202165569Ssam	/*
203165569Ssam	 * Set auto mode to reset active channel state and any desired channel.
204165569Ssam	 */
205165569Ssam	(void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO);
206165569Ssam#undef DEFAULTRATES
207165569Ssam}
208165569Ssam
209165569Ssamvoid
210165569Ssamieee80211_ifattach(struct ieee80211com *ic)
211165569Ssam{
212165569Ssam	struct ifnet *ifp = ic->ic_ifp;
213165569Ssam
214165569Ssam	ether_ifattach(ifp, ic->ic_myaddr);
215165569Ssam	ifp->if_output = ieee80211_output;
216165569Ssam
217165569Ssam	bpfattach2(ifp, DLT_IEEE802_11,
218165569Ssam	    sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
219165569Ssam
220170530Ssam	/* override the 802.3 setting */
221170530Ssam	ifp->if_hdrlen = ic->ic_headroom
222170530Ssam		+ sizeof(struct ieee80211_qosframe_addr4)
223170530Ssam		+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
224170530Ssam		+ IEEE80211_WEP_EXTIVLEN;
225170530Ssam	/* XXX no way to recalculate on ifdetach */
226170530Ssam	if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
227170530Ssam		/* XXX sanity check... */
228170530Ssam		max_linkhdr = ALIGN(ifp->if_hdrlen);
229170530Ssam		max_hdr = max_linkhdr + max_protohdr;
230170530Ssam		max_datalen = MHLEN - max_hdr;
231170530Ssam	}
232165569Ssam
233165569Ssam	/*
234165569Ssam	 * Fill in 802.11 available channel set, mark all
235165569Ssam	 * available channels as active, and pick a default
236165569Ssam	 * channel if not already specified.
237165569Ssam	 */
238165569Ssam	ieee80211_chan_init(ic);
239170530Ssam
240170530Ssam	if (ic->ic_caps & IEEE80211_C_BGSCAN)	/* enable if capable */
241170530Ssam		ic->ic_flags |= IEEE80211_F_BGSCAN;
242139526Ssam#if 0
243170530Ssam	/* XXX not until WME+WPA issues resolved */
244170530Ssam	if (ic->ic_caps & IEEE80211_C_WME)	/* enable if capable */
245138568Ssam		ic->ic_flags |= IEEE80211_F_WME;
246139526Ssam#endif
247153421Ssam	if (ic->ic_caps & IEEE80211_C_BURST)
248153421Ssam		ic->ic_flags |= IEEE80211_F_BURST;
249170530Ssam	ic->ic_flags |= IEEE80211_F_DOTH;	/* XXX out of caps, just ena */
250116742Ssam
251155688Ssam	ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
252155688Ssam	ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
253138568Ssam	ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
254170530Ssam	IEEE80211_LOCK_INIT(ic, "ieee80211com");
255138568Ssam	IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
256116742Ssam
257155688Ssam	ic->ic_lintval = ic->ic_bintval;
258138568Ssam	ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
259138568Ssam
260170530Ssam	ieee80211_crypto_attach(ic);
261138568Ssam	ieee80211_node_attach(ic);
262170530Ssam	ieee80211_power_attach(ic);
263138568Ssam	ieee80211_proto_attach(ic);
264170530Ssam	ieee80211_ht_attach(ic);
265170530Ssam	ieee80211_scan_attach(ic);
266138568Ssam
267138568Ssam	ieee80211_add_vap(ic);
268138568Ssam
269138568Ssam	ieee80211_sysctl_attach(ic);		/* NB: requires ic_vap */
270140915Ssam
271140915Ssam	/*
272140915Ssam	 * Install a default reset method for the ioctl support.
273140915Ssam	 * The driver is expected to fill this in before calling us.
274140915Ssam	 */
275140915Ssam	if (ic->ic_reset == NULL)
276140915Ssam		ic->ic_reset = ieee80211_default_reset;
277160690Ssam
278160690Ssam	KASSERT(ifp->if_spare2 == NULL, ("oops, hosed"));
279160690Ssam	ifp->if_spare2 = ic;			/* XXX temp backpointer */
280116742Ssam}
281116742Ssam
282116742Ssamvoid
283138568Ssamieee80211_ifdetach(struct ieee80211com *ic)
284116742Ssam{
285138568Ssam	struct ifnet *ifp = ic->ic_ifp;
286116742Ssam
287138568Ssam	ieee80211_remove_vap(ic);
288138568Ssam
289138568Ssam	ieee80211_sysctl_detach(ic);
290170530Ssam	ieee80211_scan_detach(ic);
291170530Ssam	ieee80211_ht_detach(ic);
292166012Ssam	/* NB: must be called before ieee80211_node_detach */
293138568Ssam	ieee80211_proto_detach(ic);
294138568Ssam	ieee80211_crypto_detach(ic);
295170530Ssam	ieee80211_power_detach(ic);
296138568Ssam	ieee80211_node_detach(ic);
297116742Ssam	ifmedia_removeall(&ic->ic_media);
298138568Ssam
299170530Ssam	IEEE80211_LOCK_DESTROY(ic);
300138568Ssam	IEEE80211_BEACON_LOCK_DESTROY(ic);
301138568Ssam
302116742Ssam	bpfdetach(ifp);
303116742Ssam	ether_ifdetach(ifp);
304116742Ssam}
305116742Ssam
306166012Ssamstatic __inline int
307166012Ssammapgsm(u_int freq, u_int flags)
308166012Ssam{
309166012Ssam	freq *= 10;
310166012Ssam	if (flags & IEEE80211_CHAN_QUARTER)
311166012Ssam		freq += 5;
312166012Ssam	else if (flags & IEEE80211_CHAN_HALF)
313166012Ssam		freq += 10;
314166012Ssam	else
315166012Ssam		freq += 20;
316166012Ssam	/* NB: there is no 907/20 wide but leave room */
317166012Ssam	return (freq - 906*10) / 5;
318166012Ssam}
319166012Ssam
320166012Ssamstatic __inline int
321166012Ssammappsb(u_int freq, u_int flags)
322166012Ssam{
323166012Ssam	return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5;
324166012Ssam}
325166012Ssam
326116742Ssam/*
327116742Ssam * Convert MHz frequency to IEEE channel number.
328116742Ssam */
329152450Ssamint
330116742Ssamieee80211_mhz2ieee(u_int freq, u_int flags)
331116742Ssam{
332167430Ssam#define	IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990)
333166012Ssam	if (flags & IEEE80211_CHAN_GSM)
334166012Ssam		return mapgsm(freq, flags);
335116742Ssam	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
336116742Ssam		if (freq == 2484)
337116742Ssam			return 14;
338116742Ssam		if (freq < 2484)
339152450Ssam			return ((int) freq - 2407) / 5;
340116742Ssam		else
341116742Ssam			return 15 + ((freq - 2512) / 20);
342116899Ssam	} else if (flags & IEEE80211_CHAN_5GHZ) {	/* 5Ghz band */
343165569Ssam		if (freq <= 5000) {
344170530Ssam			/* XXX check regdomain? */
345167430Ssam			if (IS_FREQ_IN_PSB(freq))
346166012Ssam				return mappsb(freq, flags);
347152450Ssam			return (freq - 4000) / 5;
348165569Ssam		} else
349152450Ssam			return (freq - 5000) / 5;
350116742Ssam	} else {				/* either, guess */
351116742Ssam		if (freq == 2484)
352116742Ssam			return 14;
353166012Ssam		if (freq < 2484) {
354166012Ssam			if (907 <= freq && freq <= 922)
355166012Ssam				return mapgsm(freq, flags);
356152450Ssam			return ((int) freq - 2407) / 5;
357166012Ssam		}
358152450Ssam		if (freq < 5000) {
359167430Ssam			if (IS_FREQ_IN_PSB(freq))
360166012Ssam				return mappsb(freq, flags);
361165569Ssam			else if (freq > 4900)
362152450Ssam				return (freq - 4000) / 5;
363152450Ssam			else
364152450Ssam				return 15 + ((freq - 2512) / 20);
365152450Ssam		}
366116742Ssam		return (freq - 5000) / 5;
367116742Ssam	}
368167430Ssam#undef IS_FREQ_IN_PSB
369116742Ssam}
370116742Ssam
371116742Ssam/*
372116742Ssam * Convert channel to IEEE channel number.
373116742Ssam */
374152450Ssamint
375165825Smjacobieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c)
376116742Ssam{
377170530Ssam	if (c == NULL) {
378138568Ssam		if_printf(ic->ic_ifp, "invalid channel (NULL)\n");
379117039Ssam		return 0;		/* XXX */
380116742Ssam	}
381170530Ssam	return (c == IEEE80211_CHAN_ANYC ?  IEEE80211_CHAN_ANY : c->ic_ieee);
382116742Ssam}
383116742Ssam
384116742Ssam/*
385116742Ssam * Convert IEEE channel number to MHz frequency.
386116742Ssam */
387116742Ssamu_int
388116742Ssamieee80211_ieee2mhz(u_int chan, u_int flags)
389116742Ssam{
390166012Ssam	if (flags & IEEE80211_CHAN_GSM)
391166012Ssam		return 907 + 5 * (chan / 10);
392116742Ssam	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
393116742Ssam		if (chan == 14)
394116742Ssam			return 2484;
395116742Ssam		if (chan < 14)
396116742Ssam			return 2407 + chan*5;
397116742Ssam		else
398116742Ssam			return 2512 + ((chan-15)*20);
399116742Ssam	} else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */
400165569Ssam		if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) {
401165569Ssam			chan -= 37;
402165569Ssam			return 4940 + chan*5 + (chan % 5 ? 2 : 0);
403165569Ssam		}
404116742Ssam		return 5000 + (chan*5);
405116742Ssam	} else {				/* either, guess */
406166012Ssam		/* XXX can't distinguish PSB+GSM channels */
407116742Ssam		if (chan == 14)
408116742Ssam			return 2484;
409116742Ssam		if (chan < 14)			/* 0-13 */
410116742Ssam			return 2407 + chan*5;
411116742Ssam		if (chan < 27)			/* 15-26 */
412116742Ssam			return 2512 + ((chan-15)*20);
413116742Ssam		return 5000 + (chan*5);
414116742Ssam	}
415116742Ssam}
416116742Ssam
417116742Ssam/*
418170530Ssam * Locate a channel given a frequency+flags.  We cache
419170530Ssam * the previous lookup to optimize swithing between two
420170530Ssam * channels--as happens with dynamic turbo.
421170530Ssam */
422170530Ssamstruct ieee80211_channel *
423170530Ssamieee80211_find_channel(struct ieee80211com *ic, int freq, int flags)
424170530Ssam{
425170530Ssam	struct ieee80211_channel *c;
426170530Ssam	int i;
427170530Ssam
428170530Ssam	flags &= IEEE80211_CHAN_ALLTURBO;
429170530Ssam	c = ic->ic_prevchan;
430170530Ssam	if (c != NULL && c->ic_freq == freq &&
431170530Ssam	    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
432170530Ssam		return c;
433170530Ssam	/* brute force search */
434170530Ssam	for (i = 0; i < ic->ic_nchans; i++) {
435170530Ssam		c = &ic->ic_channels[i];
436170530Ssam		if (c->ic_freq == freq &&
437170530Ssam		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
438170530Ssam			return c;
439170530Ssam	}
440170530Ssam	return NULL;
441170530Ssam}
442170530Ssam
443173861Ssam/*
444173861Ssam * Locate a channel given a channel number+flags.  We cache
445173861Ssam * the previous lookup to optimize switching between two
446173861Ssam * channels--as happens with dynamic turbo.
447173861Ssam */
448173861Ssamstruct ieee80211_channel *
449173861Ssamieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags)
450173861Ssam{
451173861Ssam	struct ieee80211_channel *c;
452173861Ssam	int i;
453173861Ssam
454173861Ssam	flags &= IEEE80211_CHAN_ALLTURBO;
455173861Ssam	c = ic->ic_prevchan;
456173861Ssam	if (c != NULL && c->ic_ieee == ieee &&
457173861Ssam	    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
458173861Ssam		return c;
459173861Ssam	/* brute force search */
460173861Ssam	for (i = 0; i < ic->ic_nchans; i++) {
461173861Ssam		c = &ic->ic_channels[i];
462173861Ssam		if (c->ic_ieee == ieee &&
463173861Ssam		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
464173861Ssam			return c;
465173861Ssam	}
466173861Ssam	return NULL;
467173861Ssam}
468173861Ssam
469170530Ssamstatic void
470170530Ssamaddmedia(struct ieee80211com *ic, int mode, int mword)
471170530Ssam{
472170530Ssam#define	TURBO(m)	((m) | IFM_IEEE80211_TURBO)
473170530Ssam#define	ADD(_ic, _s, _o) \
474170530Ssam	ifmedia_add(&(_ic)->ic_media, \
475170530Ssam		IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
476170530Ssam	static const u_int mopts[IEEE80211_MODE_MAX] = {
477170530Ssam		IFM_AUTO,			/* IEEE80211_MODE_AUTO */
478170530Ssam		IFM_IEEE80211_11A,		/* IEEE80211_MODE_11A */
479170530Ssam		IFM_IEEE80211_11B,		/* IEEE80211_MODE_11B */
480170530Ssam		IFM_IEEE80211_11G,		/* IEEE80211_MODE_11G */
481170530Ssam		IFM_IEEE80211_FH,		/* IEEE80211_MODE_FH */
482170530Ssam		TURBO(IFM_IEEE80211_11A),	/* IEEE80211_MODE_TURBO_A */
483170530Ssam		TURBO(IFM_IEEE80211_11G),	/* IEEE80211_MODE_TURBO_G */
484170530Ssam		TURBO(IFM_IEEE80211_11A),	/* IEEE80211_MODE_STURBO_A */
485170530Ssam		IFM_IEEE80211_11NA,		/* IEEE80211_MODE_11NA */
486170530Ssam		IFM_IEEE80211_11NG,		/* IEEE80211_MODE_11NG */
487170530Ssam	};
488170530Ssam	u_int mopt;
489170530Ssam
490170530Ssam	KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
491170530Ssam	mopt = mopts[mode];
492170530Ssam	KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO,
493170530Ssam	    ("no media mapping for mode %u", mode));
494170530Ssam
495170530Ssam	ADD(ic, mword, mopt);	/* e.g. 11a auto */
496170530Ssam	if (ic->ic_caps & IEEE80211_C_IBSS)
497170530Ssam		ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
498170530Ssam	if (ic->ic_caps & IEEE80211_C_HOSTAP)
499170530Ssam		ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
500170530Ssam	if (ic->ic_caps & IEEE80211_C_AHDEMO)
501170530Ssam		ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
502170530Ssam	if (ic->ic_caps & IEEE80211_C_MONITOR)
503170530Ssam		ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
504170530Ssam#undef ADD
505170530Ssam#undef TURBO
506170530Ssam}
507170530Ssam
508170530Ssam/*
509116742Ssam * Setup the media data structures according to the channel and
510116742Ssam * rate tables.  This must be called by the driver after
511116742Ssam * ieee80211_attach and before most anything else.
512116742Ssam */
513116742Ssamvoid
514138568Ssamieee80211_media_init(struct ieee80211com *ic,
515116742Ssam	ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
516116742Ssam{
517138568Ssam	struct ifnet *ifp = ic->ic_ifp;
518170530Ssam	int i, j, mode, rate, maxrate, mword, r;
519170530Ssam	const struct ieee80211_rateset *rs;
520116742Ssam	struct ieee80211_rateset allrates;
521116742Ssam
522165569Ssam	/* NB: this works because the structure is initialized to zero */
523165569Ssam	if (LIST_EMPTY(&ic->ic_media.ifm_list)) {
524165569Ssam		/*
525165569Ssam		 * Do late attach work that must wait for any subclass
526165569Ssam		 * (i.e. driver) work such as overriding methods.
527165569Ssam		 */
528165569Ssam		ieee80211_node_lateattach(ic);
529165569Ssam	} else {
530165569Ssam		/*
531165569Ssam		 * We are re-initializing the channel list; clear
532165569Ssam		 * the existing media state as the media routines
533165569Ssam		 * don't suppress duplicates.
534165569Ssam		 */
535165569Ssam		ifmedia_removeall(&ic->ic_media);
536165569Ssam		ieee80211_chan_init(ic);
537165569Ssam	}
538170530Ssam	ieee80211_power_lateattach(ic);
539118887Ssam
540118887Ssam	/*
541116742Ssam	 * Fill in media characteristics.
542116742Ssam	 */
543116742Ssam	ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
544116742Ssam	maxrate = 0;
545170530Ssam	/*
546170530Ssam	 * Add media for legacy operating modes.
547170530Ssam	 */
548116742Ssam	memset(&allrates, 0, sizeof(allrates));
549170530Ssam	for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
550167468Ssam		if (isclr(ic->ic_modecaps, mode))
551116742Ssam			continue;
552170530Ssam		addmedia(ic, mode, IFM_AUTO);
553116742Ssam		if (mode == IEEE80211_MODE_AUTO)
554116742Ssam			continue;
555116742Ssam		rs = &ic->ic_sup_rates[mode];
556116742Ssam		for (i = 0; i < rs->rs_nrates; i++) {
557116742Ssam			rate = rs->rs_rates[i];
558116742Ssam			mword = ieee80211_rate2media(ic, rate, mode);
559116742Ssam			if (mword == 0)
560116742Ssam				continue;
561170530Ssam			addmedia(ic, mode, mword);
562116742Ssam			/*
563170530Ssam			 * Add legacy rate to the collection of all rates.
564116742Ssam			 */
565116742Ssam			r = rate & IEEE80211_RATE_VAL;
566116742Ssam			for (j = 0; j < allrates.rs_nrates; j++)
567116742Ssam				if (allrates.rs_rates[j] == r)
568116742Ssam					break;
569116742Ssam			if (j == allrates.rs_nrates) {
570116742Ssam				/* unique, add to the set */
571116742Ssam				allrates.rs_rates[j] = r;
572116742Ssam				allrates.rs_nrates++;
573116742Ssam			}
574116742Ssam			rate = (rate & IEEE80211_RATE_VAL) / 2;
575116742Ssam			if (rate > maxrate)
576116742Ssam				maxrate = rate;
577116742Ssam		}
578116742Ssam	}
579116742Ssam	for (i = 0; i < allrates.rs_nrates; i++) {
580116742Ssam		mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
581116742Ssam				IEEE80211_MODE_AUTO);
582116742Ssam		if (mword == 0)
583116742Ssam			continue;
584170530Ssam		/* NB: remove media options from mword */
585170530Ssam		addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
586116742Ssam	}
587170530Ssam	/*
588170530Ssam	 * Add HT/11n media.  Note that we do not have enough
589170530Ssam	 * bits in the media subtype to express the MCS so we
590170530Ssam	 * use a "placeholder" media subtype and any fixed MCS
591170530Ssam	 * must be specified with a different mechanism.
592170530Ssam	 */
593170530Ssam	for (; mode < IEEE80211_MODE_MAX; mode++) {
594170530Ssam		if (isclr(ic->ic_modecaps, mode))
595170530Ssam			continue;
596170530Ssam		addmedia(ic, mode, IFM_AUTO);
597170530Ssam		addmedia(ic, mode, IFM_IEEE80211_MCS);
598170530Ssam	}
599170530Ssam	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
600170530Ssam	    isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
601170530Ssam		addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
602170530Ssam		/* XXX could walk htrates */
603170530Ssam		/* XXX known array size */
604170530Ssam		if (ieee80211_htrates[15] > maxrate)
605170530Ssam			maxrate = ieee80211_htrates[15];
606170530Ssam	}
607116742Ssam
608170530Ssam	/* NB: strip explicit mode; we're actually in autoselect */
609170530Ssam	ifmedia_set(&ic->ic_media,
610170530Ssam		media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK);
611170530Ssam
612116742Ssam	if (maxrate)
613116742Ssam		ifp->if_baudrate = IF_Mbps(maxrate);
614116742Ssam}
615116742Ssam
616165569Ssamconst struct ieee80211_rateset *
617165569Ssamieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c)
618165569Ssam{
619166012Ssam	if (IEEE80211_IS_CHAN_HALF(c))
620166012Ssam		return &ieee80211_rateset_half;
621166012Ssam	if (IEEE80211_IS_CHAN_QUARTER(c))
622166012Ssam		return &ieee80211_rateset_quarter;
623170530Ssam	if (IEEE80211_IS_CHAN_HTA(c))
624170530Ssam		return &ic->ic_sup_rates[IEEE80211_MODE_11A];
625170530Ssam	if (IEEE80211_IS_CHAN_HTG(c)) {
626170530Ssam		/* XXX does this work for basic rates? */
627170530Ssam		return &ic->ic_sup_rates[IEEE80211_MODE_11G];
628170530Ssam	}
629170530Ssam	return &ic->ic_sup_rates[ieee80211_chan2mode(c)];
630165569Ssam}
631165569Ssam
632138568Ssamvoid
633138568Ssamieee80211_announce(struct ieee80211com *ic)
634138568Ssam{
635138568Ssam	struct ifnet *ifp = ic->ic_ifp;
636138568Ssam	int i, mode, rate, mword;
637170530Ssam	const struct ieee80211_rateset *rs;
638138568Ssam
639172227Ssam	/* NB: skip AUTO since it has no rates */
640172227Ssam	for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) {
641167468Ssam		if (isclr(ic->ic_modecaps, mode))
642138568Ssam			continue;
643138568Ssam		if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
644138568Ssam		rs = &ic->ic_sup_rates[mode];
645138568Ssam		for (i = 0; i < rs->rs_nrates; i++) {
646170530Ssam			mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode);
647138568Ssam			if (mword == 0)
648138568Ssam				continue;
649170530Ssam			rate = ieee80211_media2rate(mword);
650138568Ssam			printf("%s%d%sMbps", (i != 0 ? " " : ""),
651170530Ssam			    rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
652138568Ssam		}
653138568Ssam		printf("\n");
654138568Ssam	}
655170530Ssam	ieee80211_ht_announce(ic);
656138568Ssam}
657138568Ssam
658170530Ssamvoid
659170530Ssamieee80211_announce_channels(struct ieee80211com *ic)
660116742Ssam{
661170530Ssam	const struct ieee80211_channel *c;
662170530Ssam	char type;
663170530Ssam	int i, cw;
664170530Ssam
665170530Ssam	printf("Chan  Freq  CW  RegPwr  MinPwr  MaxPwr\n");
666170530Ssam	for (i = 0; i < ic->ic_nchans; i++) {
667170530Ssam		c = &ic->ic_channels[i];
668170530Ssam		if (IEEE80211_IS_CHAN_ST(c))
669170530Ssam			type = 'S';
670170530Ssam		else if (IEEE80211_IS_CHAN_108A(c))
671170530Ssam			type = 'T';
672170530Ssam		else if (IEEE80211_IS_CHAN_108G(c))
673170530Ssam			type = 'G';
674170530Ssam		else if (IEEE80211_IS_CHAN_HT(c))
675170530Ssam			type = 'n';
676170530Ssam		else if (IEEE80211_IS_CHAN_A(c))
677170530Ssam			type = 'a';
678170530Ssam		else if (IEEE80211_IS_CHAN_ANYG(c))
679170530Ssam			type = 'g';
680170530Ssam		else if (IEEE80211_IS_CHAN_B(c))
681170530Ssam			type = 'b';
682170530Ssam		else
683170530Ssam			type = 'f';
684170530Ssam		if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c))
685170530Ssam			cw = 40;
686170530Ssam		else if (IEEE80211_IS_CHAN_HALF(c))
687170530Ssam			cw = 10;
688170530Ssam		else if (IEEE80211_IS_CHAN_QUARTER(c))
689170530Ssam			cw = 5;
690170530Ssam		else
691170530Ssam			cw = 20;
692170530Ssam		printf("%4d  %4d%c %2d%c %6d  %4d.%d  %4d.%d\n"
693170530Ssam			, c->ic_ieee, c->ic_freq, type
694170530Ssam			, cw
695170530Ssam			, IEEE80211_IS_CHAN_HT40U(c) ? '+' :
696170530Ssam			  IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' '
697170530Ssam			, c->ic_maxregpower
698170530Ssam			, c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0
699170530Ssam			, c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0
700170530Ssam		);
701170530Ssam	}
702116742Ssam}
703116742Ssam
704116742Ssam/*
705138568Ssam * Find an instance by it's mac address.
706138568Ssam */
707138568Ssamstruct ieee80211com *
708170530Ssamieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN])
709138568Ssam{
710138568Ssam	struct ieee80211com *ic;
711138568Ssam
712138568Ssam	/* XXX lock */
713138568Ssam	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
714138568Ssam		if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr))
715138568Ssam			return ic;
716138568Ssam	return NULL;
717138568Ssam}
718138568Ssam
719138568Ssamstatic struct ieee80211com *
720138568Ssamieee80211_find_instance(struct ifnet *ifp)
721138568Ssam{
722138568Ssam	struct ieee80211com *ic;
723138568Ssam
724138568Ssam	/* XXX lock */
725138568Ssam	/* XXX not right for multiple instances but works for now */
726138568Ssam	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
727138568Ssam		if (ic->ic_ifp == ifp)
728138568Ssam			return ic;
729138568Ssam	return NULL;
730138568Ssam}
731138568Ssam
732170530Ssamstatic int
733170530Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
734170530Ssam{
735170530Ssam#define	IEEERATE(_ic,_m,_i) \
736170530Ssam	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
737170530Ssam	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
738170530Ssam	for (i = 0; i < nrates; i++)
739170530Ssam		if (IEEERATE(ic, mode, i) == rate)
740170530Ssam			return i;
741170530Ssam	return -1;
742170530Ssam#undef IEEERATE
743170530Ssam}
744170530Ssam
745138568Ssam/*
746170530Ssam * Convert a media specification to a rate index and possibly a mode
747170530Ssam * (if the rate is fixed and the mode is specified as ``auto'' then
748170530Ssam * we need to lock down the mode so the index is meanginful).
749170530Ssam */
750170530Ssamstatic int
751170530Ssamcheckrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
752170530Ssam{
753170530Ssam
754170530Ssam	/*
755170530Ssam	 * Check the rate table for the specified/current phy.
756170530Ssam	 */
757170530Ssam	if (mode == IEEE80211_MODE_AUTO) {
758170530Ssam		int i;
759170530Ssam		/*
760170530Ssam		 * In autoselect mode search for the rate.
761170530Ssam		 */
762170530Ssam		for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
763170530Ssam			if (isset(ic->ic_modecaps, i) &&
764170530Ssam			    findrate(ic, i, rate) != -1)
765170530Ssam				return 1;
766170530Ssam		}
767170530Ssam		return 0;
768170530Ssam	} else {
769170530Ssam		/*
770170530Ssam		 * Mode is fixed, check for rate.
771170530Ssam		 */
772170530Ssam		return (findrate(ic, mode, rate) != -1);
773170530Ssam	}
774170530Ssam}
775170530Ssam
776170530Ssam/*
777116742Ssam * Handle a media change request.
778116742Ssam */
779116742Ssamint
780116742Ssamieee80211_media_change(struct ifnet *ifp)
781116742Ssam{
782138568Ssam	struct ieee80211com *ic;
783116742Ssam	struct ifmedia_entry *ime;
784116742Ssam	enum ieee80211_opmode newopmode;
785116742Ssam	enum ieee80211_phymode newphymode;
786170530Ssam	int newrate, error = 0;
787116742Ssam
788138568Ssam	ic = ieee80211_find_instance(ifp);
789138568Ssam	if (!ic) {
790138568Ssam		if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
791138568Ssam		return EINVAL;
792138568Ssam	}
793116742Ssam	ime = ic->ic_media.ifm_cur;
794116742Ssam	/*
795116742Ssam	 * First, identify the phy mode.
796116742Ssam	 */
797116742Ssam	switch (IFM_MODE(ime->ifm_media)) {
798116742Ssam	case IFM_IEEE80211_11A:
799116742Ssam		newphymode = IEEE80211_MODE_11A;
800116742Ssam		break;
801116742Ssam	case IFM_IEEE80211_11B:
802116742Ssam		newphymode = IEEE80211_MODE_11B;
803116742Ssam		break;
804116742Ssam	case IFM_IEEE80211_11G:
805116742Ssam		newphymode = IEEE80211_MODE_11G;
806116742Ssam		break;
807124543Sonoe	case IFM_IEEE80211_FH:
808124543Sonoe		newphymode = IEEE80211_MODE_FH;
809124543Sonoe		break;
810170530Ssam	case IFM_IEEE80211_11NA:
811170530Ssam		newphymode = IEEE80211_MODE_11NA;
812170530Ssam		break;
813170530Ssam	case IFM_IEEE80211_11NG:
814170530Ssam		newphymode = IEEE80211_MODE_11NG;
815170530Ssam		break;
816116742Ssam	case IFM_AUTO:
817116742Ssam		newphymode = IEEE80211_MODE_AUTO;
818116742Ssam		break;
819116742Ssam	default:
820116742Ssam		return EINVAL;
821116742Ssam	}
822116742Ssam	/*
823138568Ssam	 * Turbo mode is an ``option''.
824138568Ssam	 * XXX does not apply to AUTO
825116742Ssam	 */
826116742Ssam	if (ime->ifm_media & IFM_IEEE80211_TURBO) {
827170530Ssam		if (newphymode == IEEE80211_MODE_11A) {
828170530Ssam			if (ic->ic_flags & IEEE80211_F_TURBOP)
829170530Ssam				newphymode = IEEE80211_MODE_TURBO_A;
830170530Ssam			else
831170530Ssam				newphymode = IEEE80211_MODE_STURBO_A;
832170530Ssam		} else if (newphymode == IEEE80211_MODE_11G)
833138568Ssam			newphymode = IEEE80211_MODE_TURBO_G;
834138568Ssam		else
835116742Ssam			return EINVAL;
836116742Ssam	}
837170530Ssam	/* XXX HT40 +/- */
838116742Ssam	/*
839116742Ssam	 * Next, the fixed/variable rate.
840116742Ssam	 */
841170530Ssam	newrate = ic->ic_fixed_rate;
842116742Ssam	if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
843116742Ssam		/*
844116742Ssam		 * Convert media subtype to rate.
845116742Ssam		 */
846116742Ssam		newrate = ieee80211_media2rate(ime->ifm_media);
847170530Ssam		if (newrate == 0 || !checkrate(ic, newphymode, newrate))
848116742Ssam			return EINVAL;
849170530Ssam	} else
850170530Ssam		newrate = IEEE80211_FIXED_RATE_NONE;
851116742Ssam
852116742Ssam	/*
853116742Ssam	 * Deduce new operating mode but don't install it just yet.
854116742Ssam	 */
855116742Ssam	if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
856116742Ssam	    (IFM_IEEE80211_ADHOC|IFM_FLAG0))
857116742Ssam		newopmode = IEEE80211_M_AHDEMO;
858116742Ssam	else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
859116742Ssam		newopmode = IEEE80211_M_HOSTAP;
860116742Ssam	else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
861116742Ssam		newopmode = IEEE80211_M_IBSS;
862117817Ssam	else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
863117817Ssam		newopmode = IEEE80211_M_MONITOR;
864116742Ssam	else
865116742Ssam		newopmode = IEEE80211_M_STA;
866116742Ssam
867116742Ssam	/*
868116742Ssam	 * Handle phy mode change.
869116742Ssam	 */
870170530Ssam	if (ic->ic_des_mode != newphymode) {		/* change phy mode */
871170530Ssam		ic->ic_des_mode = newphymode;
872116742Ssam		error = ENETRESET;
873116742Ssam	}
874116742Ssam
875116742Ssam	/*
876116742Ssam	 * Committed to changes, install the rate setting.
877116742Ssam	 */
878170530Ssam	if (ic->ic_fixed_rate != newrate) {
879170530Ssam		ic->ic_fixed_rate = newrate;		/* set fixed tx rate */
880116742Ssam		error = ENETRESET;
881116742Ssam	}
882116742Ssam
883116742Ssam	/*
884116742Ssam	 * Handle operating mode change.
885116742Ssam	 */
886116742Ssam	if (ic->ic_opmode != newopmode) {
887116742Ssam		ic->ic_opmode = newopmode;
888116742Ssam		switch (newopmode) {
889116742Ssam		case IEEE80211_M_AHDEMO:
890116742Ssam		case IEEE80211_M_HOSTAP:
891116742Ssam		case IEEE80211_M_STA:
892117817Ssam		case IEEE80211_M_MONITOR:
893170530Ssam		case IEEE80211_M_WDS:
894116742Ssam			ic->ic_flags &= ~IEEE80211_F_IBSSON;
895116742Ssam			break;
896116742Ssam		case IEEE80211_M_IBSS:
897116742Ssam			ic->ic_flags |= IEEE80211_F_IBSSON;
898116742Ssam			break;
899116742Ssam		}
900138568Ssam		/*
901138568Ssam		 * Yech, slot time may change depending on the
902138568Ssam		 * operating mode so reset it to be sure everything
903138568Ssam		 * is setup appropriately.
904138568Ssam		 */
905138568Ssam		ieee80211_reset_erp(ic);
906138568Ssam		ieee80211_wme_initparams(ic);	/* after opmode change */
907116742Ssam		error = ENETRESET;
908116742Ssam	}
909116742Ssam#ifdef notdef
910116742Ssam	if (error == 0)
911116742Ssam		ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
912116742Ssam#endif
913116742Ssam	return error;
914116742Ssam}
915116742Ssam
916170530Ssam/*
917170530Ssam * Common code to calculate the media status word
918170530Ssam * from the operating mode and channel state.
919170530Ssam */
920170530Ssamstatic int
921170530Ssammedia_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
922170530Ssam{
923170530Ssam	int status;
924170530Ssam
925170530Ssam	status = IFM_IEEE80211;
926170530Ssam	switch (opmode) {
927170530Ssam	case IEEE80211_M_STA:
928170530Ssam		break;
929170530Ssam	case IEEE80211_M_IBSS:
930170530Ssam		status |= IFM_IEEE80211_ADHOC;
931170530Ssam		break;
932170530Ssam	case IEEE80211_M_HOSTAP:
933170530Ssam		status |= IFM_IEEE80211_HOSTAP;
934170530Ssam		break;
935170530Ssam	case IEEE80211_M_MONITOR:
936170530Ssam		status |= IFM_IEEE80211_MONITOR;
937170530Ssam		break;
938170530Ssam	case IEEE80211_M_AHDEMO:
939170530Ssam		status |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
940170530Ssam		break;
941170530Ssam	case IEEE80211_M_WDS:
942170530Ssam		/* should not come here */
943170530Ssam		break;
944170530Ssam	}
945170530Ssam	if (IEEE80211_IS_CHAN_HTA(chan)) {
946170530Ssam		status |= IFM_IEEE80211_11NA;
947170530Ssam	} else if (IEEE80211_IS_CHAN_HTG(chan)) {
948170530Ssam		status |= IFM_IEEE80211_11NG;
949170530Ssam	} else if (IEEE80211_IS_CHAN_A(chan)) {
950170530Ssam		status |= IFM_IEEE80211_11A;
951170530Ssam	} else if (IEEE80211_IS_CHAN_B(chan)) {
952170530Ssam		status |= IFM_IEEE80211_11B;
953170530Ssam	} else if (IEEE80211_IS_CHAN_ANYG(chan)) {
954170530Ssam		status |= IFM_IEEE80211_11G;
955170530Ssam	} else if (IEEE80211_IS_CHAN_FHSS(chan)) {
956170530Ssam		status |= IFM_IEEE80211_FH;
957170530Ssam	}
958170530Ssam	/* XXX else complain? */
959170530Ssam
960170530Ssam	if (IEEE80211_IS_CHAN_TURBO(chan))
961170530Ssam		status |= IFM_IEEE80211_TURBO;
962170530Ssam
963170530Ssam	return status;
964170530Ssam}
965170530Ssam
966116742Ssamvoid
967116742Ssamieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
968116742Ssam{
969138568Ssam	struct ieee80211com *ic;
970170530Ssam	enum ieee80211_phymode mode;
971165569Ssam	const struct ieee80211_rateset *rs;
972116742Ssam
973138568Ssam	ic = ieee80211_find_instance(ifp);
974138568Ssam	if (!ic) {
975138568Ssam		if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
976138568Ssam		return;
977138568Ssam	}
978116742Ssam	imr->ifm_status = IFM_AVALID;
979170530Ssam	/*
980170530Ssam	 * NB: use the current channel's mode to lock down a xmit
981170530Ssam	 * rate only when running; otherwise we may have a mismatch
982170530Ssam	 * in which case the rate will not be convertible.
983170530Ssam	 */
984170530Ssam	if (ic->ic_state == IEEE80211_S_RUN) {
985116742Ssam		imr->ifm_status |= IFM_ACTIVE;
986170530Ssam		mode = ieee80211_chan2mode(ic->ic_curchan);
987170530Ssam	} else
988170530Ssam		mode = IEEE80211_MODE_AUTO;
989170530Ssam	imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
990138568Ssam	/*
991138568Ssam	 * Calculate a current rate if possible.
992138568Ssam	 */
993148290Ssam	if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
994138568Ssam		/*
995138568Ssam		 * A fixed rate is set, report that.
996138568Ssam		 */
997138568Ssam		imr->ifm_active |= ieee80211_rate2media(ic,
998170530Ssam			ic->ic_fixed_rate, mode);
999138568Ssam	} else if (ic->ic_opmode == IEEE80211_M_STA) {
1000138568Ssam		/*
1001138568Ssam		 * In station mode report the current transmit rate.
1002170530Ssam		 * XXX HT rate
1003138568Ssam		 */
1004138568Ssam		rs = &ic->ic_bss->ni_rates;
1005138568Ssam		imr->ifm_active |= ieee80211_rate2media(ic,
1006170530Ssam			rs->rs_rates[ic->ic_bss->ni_txrate], mode);
1007128966Sandre	} else
1008138568Ssam		imr->ifm_active |= IFM_AUTO;
1009116742Ssam}
1010116742Ssam
1011116742Ssam/*
1012116742Ssam * Set the current phy mode and recalculate the active channel
1013116742Ssam * set based on the available channels for this mode.  Also
1014116742Ssam * select a new default/current channel if the current one is
1015116742Ssam * inappropriate for this mode.
1016116742Ssam */
1017116742Ssamint
1018116742Ssamieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
1019116742Ssam{
1020116742Ssam	/*
1021166012Ssam	 * Adjust basic rates in 11b/11g supported rate set.
1022166012Ssam	 * Note that if operating on a hal/quarter rate channel
1023166012Ssam	 * this is a noop as those rates sets are different
1024166012Ssam	 * and used instead.
1025116742Ssam	 */
1026166012Ssam	if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
1027166012Ssam		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode);
1028166012Ssam
1029116742Ssam	ic->ic_curmode = mode;
1030138568Ssam	ieee80211_reset_erp(ic);	/* reset ERP state */
1031138568Ssam	ieee80211_wme_initparams(ic);	/* reset WME stat */
1032138568Ssam
1033116742Ssam	return 0;
1034116742Ssam}
1035116742Ssam
1036116742Ssam/*
1037170530Ssam * Return the phy mode for with the specified channel.
1038116742Ssam */
1039116742Ssamenum ieee80211_phymode
1040170530Ssamieee80211_chan2mode(const struct ieee80211_channel *chan)
1041116742Ssam{
1042170530Ssam
1043170530Ssam	if (IEEE80211_IS_CHAN_HTA(chan))
1044170530Ssam		return IEEE80211_MODE_11NA;
1045170530Ssam	else if (IEEE80211_IS_CHAN_HTG(chan))
1046170530Ssam		return IEEE80211_MODE_11NG;
1047170530Ssam	else if (IEEE80211_IS_CHAN_108G(chan))
1048170530Ssam		return IEEE80211_MODE_TURBO_G;
1049170530Ssam	else if (IEEE80211_IS_CHAN_ST(chan))
1050170530Ssam		return IEEE80211_MODE_STURBO_A;
1051170530Ssam	else if (IEEE80211_IS_CHAN_TURBO(chan))
1052153350Ssam		return IEEE80211_MODE_TURBO_A;
1053170530Ssam	else if (IEEE80211_IS_CHAN_A(chan))
1054116742Ssam		return IEEE80211_MODE_11A;
1055170530Ssam	else if (IEEE80211_IS_CHAN_ANYG(chan))
1056116742Ssam		return IEEE80211_MODE_11G;
1057170530Ssam	else if (IEEE80211_IS_CHAN_B(chan))
1058116742Ssam		return IEEE80211_MODE_11B;
1059170530Ssam	else if (IEEE80211_IS_CHAN_FHSS(chan))
1060170530Ssam		return IEEE80211_MODE_FH;
1061170530Ssam
1062170530Ssam	/* NB: should not get here */
1063170530Ssam	printf("%s: cannot map channel to mode; freq %u flags 0x%x\n",
1064170530Ssam		__func__, chan->ic_freq, chan->ic_flags);
1065170530Ssam	return IEEE80211_MODE_11B;
1066116742Ssam}
1067116742Ssam
1068170530Ssamstruct ratemedia {
1069170530Ssam	u_int	match;	/* rate + mode */
1070170530Ssam	u_int	media;	/* if_media rate */
1071170530Ssam};
1072170530Ssam
1073170530Ssamstatic int
1074170530Ssamfindmedia(const struct ratemedia rates[], int n, u_int match)
1075170530Ssam{
1076170530Ssam	int i;
1077170530Ssam
1078170530Ssam	for (i = 0; i < n; i++)
1079170530Ssam		if (rates[i].match == match)
1080170530Ssam			return rates[i].media;
1081170530Ssam	return IFM_AUTO;
1082170530Ssam}
1083170530Ssam
1084116742Ssam/*
1085170530Ssam * Convert IEEE80211 rate value to ifmedia subtype.
1086170530Ssam * Rate is either a legacy rate in units of 0.5Mbps
1087170530Ssam * or an MCS index.
1088116742Ssam */
1089116742Ssamint
1090116742Ssamieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
1091116742Ssam{
1092116742Ssam#define	N(a)	(sizeof(a) / sizeof(a[0]))
1093170530Ssam	static const struct ratemedia rates[] = {
1094124543Sonoe		{   2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
1095124543Sonoe		{   4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
1096124543Sonoe		{   2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 },
1097124543Sonoe		{   4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 },
1098124543Sonoe		{  11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 },
1099124543Sonoe		{  22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 },
1100124543Sonoe		{  44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 },
1101124543Sonoe		{  12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 },
1102124543Sonoe		{  18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 },
1103124543Sonoe		{  24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 },
1104124543Sonoe		{  36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 },
1105124543Sonoe		{  48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 },
1106124543Sonoe		{  72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 },
1107124543Sonoe		{  96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 },
1108124543Sonoe		{ 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 },
1109124543Sonoe		{   2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 },
1110124543Sonoe		{   4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 },
1111124543Sonoe		{  11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 },
1112124543Sonoe		{  22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 },
1113124543Sonoe		{  12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 },
1114124543Sonoe		{  18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 },
1115124543Sonoe		{  24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 },
1116124543Sonoe		{  36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 },
1117124543Sonoe		{  48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 },
1118124543Sonoe		{  72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 },
1119124543Sonoe		{  96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 },
1120124543Sonoe		{ 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 },
1121165569Ssam		{   6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 },
1122165569Ssam		{   9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 },
1123165569Ssam		{  54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 },
1124116742Ssam		/* NB: OFDM72 doesn't realy exist so we don't handle it */
1125116742Ssam	};
1126170530Ssam	static const struct ratemedia htrates[] = {
1127170530Ssam		{   0, IFM_IEEE80211_MCS },
1128170530Ssam		{   1, IFM_IEEE80211_MCS },
1129170530Ssam		{   2, IFM_IEEE80211_MCS },
1130170530Ssam		{   3, IFM_IEEE80211_MCS },
1131170530Ssam		{   4, IFM_IEEE80211_MCS },
1132170530Ssam		{   5, IFM_IEEE80211_MCS },
1133170530Ssam		{   6, IFM_IEEE80211_MCS },
1134170530Ssam		{   7, IFM_IEEE80211_MCS },
1135170530Ssam		{   8, IFM_IEEE80211_MCS },
1136170530Ssam		{   9, IFM_IEEE80211_MCS },
1137170530Ssam		{  10, IFM_IEEE80211_MCS },
1138170530Ssam		{  11, IFM_IEEE80211_MCS },
1139170530Ssam		{  12, IFM_IEEE80211_MCS },
1140170530Ssam		{  13, IFM_IEEE80211_MCS },
1141170530Ssam		{  14, IFM_IEEE80211_MCS },
1142170530Ssam		{  15, IFM_IEEE80211_MCS },
1143170530Ssam	};
1144170530Ssam	int m;
1145116742Ssam
1146170530Ssam	/*
1147170530Ssam	 * Check 11n rates first for match as an MCS.
1148170530Ssam	 */
1149170530Ssam	if (mode == IEEE80211_MODE_11NA) {
1150172226Ssam		if (rate & IEEE80211_RATE_MCS) {
1151172226Ssam			rate &= ~IEEE80211_RATE_MCS;
1152170530Ssam			m = findmedia(htrates, N(htrates), rate);
1153170530Ssam			if (m != IFM_AUTO)
1154170530Ssam				return m | IFM_IEEE80211_11NA;
1155170530Ssam		}
1156170530Ssam	} else if (mode == IEEE80211_MODE_11NG) {
1157170530Ssam		/* NB: 12 is ambiguous, it will be treated as an MCS */
1158172226Ssam		if (rate & IEEE80211_RATE_MCS) {
1159172226Ssam			rate &= ~IEEE80211_RATE_MCS;
1160170530Ssam			m = findmedia(htrates, N(htrates), rate);
1161170530Ssam			if (m != IFM_AUTO)
1162170530Ssam				return m | IFM_IEEE80211_11NG;
1163170530Ssam		}
1164170530Ssam	}
1165170530Ssam	rate &= IEEE80211_RATE_VAL;
1166116742Ssam	switch (mode) {
1167116742Ssam	case IEEE80211_MODE_11A:
1168170530Ssam	case IEEE80211_MODE_11NA:
1169138568Ssam	case IEEE80211_MODE_TURBO_A:
1170170530Ssam	case IEEE80211_MODE_STURBO_A:
1171170530Ssam		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A);
1172116742Ssam	case IEEE80211_MODE_11B:
1173170530Ssam		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B);
1174124543Sonoe	case IEEE80211_MODE_FH:
1175170530Ssam		return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH);
1176116742Ssam	case IEEE80211_MODE_AUTO:
1177116742Ssam		/* NB: ic may be NULL for some drivers */
1178170530Ssam		if (ic && ic->ic_phytype == IEEE80211_T_FH)
1179170530Ssam			return findmedia(rates, N(rates),
1180170530Ssam			    rate | IFM_IEEE80211_FH);
1181116742Ssam		/* NB: hack, 11g matches both 11b+11a rates */
1182116742Ssam		/* fall thru... */
1183116742Ssam	case IEEE80211_MODE_11G:
1184170530Ssam	case IEEE80211_MODE_11NG:
1185138568Ssam	case IEEE80211_MODE_TURBO_G:
1186170530Ssam		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G);
1187116742Ssam	}
1188116742Ssam	return IFM_AUTO;
1189116742Ssam#undef N
1190116742Ssam}
1191116742Ssam
1192116742Ssamint
1193116742Ssamieee80211_media2rate(int mword)
1194116742Ssam{
1195116742Ssam#define	N(a)	(sizeof(a) / sizeof(a[0]))
1196116742Ssam	static const int ieeerates[] = {
1197116742Ssam		-1,		/* IFM_AUTO */
1198116742Ssam		0,		/* IFM_MANUAL */
1199116742Ssam		0,		/* IFM_NONE */
1200116742Ssam		2,		/* IFM_IEEE80211_FH1 */
1201116742Ssam		4,		/* IFM_IEEE80211_FH2 */
1202116742Ssam		2,		/* IFM_IEEE80211_DS1 */
1203116742Ssam		4,		/* IFM_IEEE80211_DS2 */
1204116742Ssam		11,		/* IFM_IEEE80211_DS5 */
1205116742Ssam		22,		/* IFM_IEEE80211_DS11 */
1206116742Ssam		44,		/* IFM_IEEE80211_DS22 */
1207116742Ssam		12,		/* IFM_IEEE80211_OFDM6 */
1208116742Ssam		18,		/* IFM_IEEE80211_OFDM9 */
1209116742Ssam		24,		/* IFM_IEEE80211_OFDM12 */
1210116742Ssam		36,		/* IFM_IEEE80211_OFDM18 */
1211116742Ssam		48,		/* IFM_IEEE80211_OFDM24 */
1212116742Ssam		72,		/* IFM_IEEE80211_OFDM36 */
1213116742Ssam		96,		/* IFM_IEEE80211_OFDM48 */
1214116742Ssam		108,		/* IFM_IEEE80211_OFDM54 */
1215116742Ssam		144,		/* IFM_IEEE80211_OFDM72 */
1216165569Ssam		0,		/* IFM_IEEE80211_DS354k */
1217165569Ssam		0,		/* IFM_IEEE80211_DS512k */
1218165569Ssam		6,		/* IFM_IEEE80211_OFDM3 */
1219165569Ssam		9,		/* IFM_IEEE80211_OFDM4 */
1220165569Ssam		54,		/* IFM_IEEE80211_OFDM27 */
1221170530Ssam		-1,		/* IFM_IEEE80211_MCS */
1222116742Ssam	};
1223116742Ssam	return IFM_SUBTYPE(mword) < N(ieeerates) ?
1224116742Ssam		ieeerates[IFM_SUBTYPE(mword)] : 0;
1225116742Ssam#undef N
1226116742Ssam}
1227