xref: /freebsd/sys/net80211/ieee80211.c (revision 8be0d5701039c9f72d4efc1cc8477add90315662)
11a1e1d21SSam Leffler /*-
27535e66aSSam Leffler  * Copyright (c) 2001 Atsushi Onoe
31a1e1d21SSam Leffler  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
41a1e1d21SSam Leffler  * All rights reserved.
51a1e1d21SSam Leffler  *
61a1e1d21SSam Leffler  * Redistribution and use in source and binary forms, with or without
71a1e1d21SSam Leffler  * modification, are permitted provided that the following conditions
81a1e1d21SSam Leffler  * are met:
91a1e1d21SSam Leffler  * 1. Redistributions of source code must retain the above copyright
107535e66aSSam Leffler  *    notice, this list of conditions and the following disclaimer.
117535e66aSSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
127535e66aSSam Leffler  *    notice, this list of conditions and the following disclaimer in the
137535e66aSSam Leffler  *    documentation and/or other materials provided with the distribution.
147535e66aSSam Leffler  * 3. The name of the author may not be used to endorse or promote products
157535e66aSSam Leffler  *    derived from this software without specific prior written permission.
161a1e1d21SSam Leffler  *
171a1e1d21SSam Leffler  * Alternatively, this software may be distributed under the terms of the
181a1e1d21SSam Leffler  * GNU General Public License ("GPL") version 2 as published by the Free
191a1e1d21SSam Leffler  * Software Foundation.
201a1e1d21SSam Leffler  *
217535e66aSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
227535e66aSSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
237535e66aSSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
247535e66aSSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
257535e66aSSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
267535e66aSSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
277535e66aSSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
287535e66aSSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
297535e66aSSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
307535e66aSSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
311a1e1d21SSam Leffler  */
321a1e1d21SSam Leffler 
331a1e1d21SSam Leffler #include <sys/cdefs.h>
341a1e1d21SSam Leffler __FBSDID("$FreeBSD$");
351a1e1d21SSam Leffler 
361a1e1d21SSam Leffler /*
371a1e1d21SSam Leffler  * IEEE 802.11 generic handler
381a1e1d21SSam Leffler  */
391a1e1d21SSam Leffler 
401a1e1d21SSam Leffler #include "opt_inet.h"
411a1e1d21SSam Leffler 
421a1e1d21SSam Leffler #include <sys/param.h>
431a1e1d21SSam Leffler #include <sys/systm.h>
441a1e1d21SSam Leffler #include <sys/mbuf.h>
451a1e1d21SSam Leffler #include <sys/malloc.h>
461a1e1d21SSam Leffler #include <sys/kernel.h>
471a1e1d21SSam Leffler #include <sys/socket.h>
481a1e1d21SSam Leffler #include <sys/sockio.h>
491a1e1d21SSam Leffler #include <sys/endian.h>
501a1e1d21SSam Leffler #include <sys/errno.h>
511a1e1d21SSam Leffler #include <sys/bus.h>
521a1e1d21SSam Leffler #include <sys/proc.h>
531a1e1d21SSam Leffler #include <sys/sysctl.h>
541a1e1d21SSam Leffler 
551a1e1d21SSam Leffler #include <machine/atomic.h>
561a1e1d21SSam Leffler 
571a1e1d21SSam Leffler #include <net/if.h>
581a1e1d21SSam Leffler #include <net/if_dl.h>
591a1e1d21SSam Leffler #include <net/if_media.h>
601a1e1d21SSam Leffler #include <net/if_arp.h>
611a1e1d21SSam Leffler #include <net/ethernet.h>
621a1e1d21SSam Leffler #include <net/if_llc.h>
631a1e1d21SSam Leffler 
641a1e1d21SSam Leffler #include <net80211/ieee80211_var.h>
651a1e1d21SSam Leffler 
661a1e1d21SSam Leffler #include <net/bpf.h>
671a1e1d21SSam Leffler 
681a1e1d21SSam Leffler #ifdef INET
691a1e1d21SSam Leffler #include <netinet/in.h>
701a1e1d21SSam Leffler #include <netinet/if_ether.h>
711a1e1d21SSam Leffler #endif
721a1e1d21SSam Leffler 
731a1e1d21SSam Leffler #ifdef IEEE80211_DEBUG
741a1e1d21SSam Leffler int	ieee80211_debug = 0;
751a1e1d21SSam Leffler SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug,
761a1e1d21SSam Leffler 	    0, "IEEE 802.11 media debugging printfs");
771a1e1d21SSam Leffler #endif
781a1e1d21SSam Leffler 
791a1e1d21SSam Leffler static void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
801a1e1d21SSam Leffler 		enum ieee80211_phymode);
811a1e1d21SSam Leffler 
821a1e1d21SSam Leffler static const char *ieee80211_phymode_name[] = {
831a1e1d21SSam Leffler 	"auto",		/* IEEE80211_MODE_AUTO */
841a1e1d21SSam Leffler 	"11a",		/* IEEE80211_MODE_11A */
851a1e1d21SSam Leffler 	"11b",		/* IEEE80211_MODE_11B */
861a1e1d21SSam Leffler 	"11g",		/* IEEE80211_MODE_11G */
871a1e1d21SSam Leffler 	"turbo",	/* IEEE80211_MODE_TURBO	*/
881a1e1d21SSam Leffler };
891a1e1d21SSam Leffler 
901a1e1d21SSam Leffler void
911a1e1d21SSam Leffler ieee80211_ifattach(struct ifnet *ifp)
921a1e1d21SSam Leffler {
931a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
941a1e1d21SSam Leffler 	struct ieee80211_channel *c;
951a1e1d21SSam Leffler 	int i;
961a1e1d21SSam Leffler 
971a1e1d21SSam Leffler 	ether_ifattach(ifp, ic->ic_myaddr);
981a1e1d21SSam Leffler 	bpfattach2(ifp, DLT_IEEE802_11,
991a1e1d21SSam Leffler 	    sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
1001a1e1d21SSam Leffler 	ieee80211_crypto_attach(ifp);
1011a1e1d21SSam Leffler 
1021a1e1d21SSam Leffler 	/*
1031a1e1d21SSam Leffler 	 * Fill in 802.11 available channel set, mark
1041a1e1d21SSam Leffler 	 * all available channels as active, and pick
1051a1e1d21SSam Leffler 	 * a default channel if not already specified.
1061a1e1d21SSam Leffler 	 */
1071a1e1d21SSam Leffler 	memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
1081a1e1d21SSam Leffler 	ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO;
1091a1e1d21SSam Leffler 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1101a1e1d21SSam Leffler 		c = &ic->ic_channels[i];
1111a1e1d21SSam Leffler 		if (c->ic_flags) {
1121a1e1d21SSam Leffler 			/*
1131a1e1d21SSam Leffler 			 * Verify driver passed us valid data.
1141a1e1d21SSam Leffler 			 */
1151a1e1d21SSam Leffler 			if (i != ieee80211_chan2ieee(ic, c)) {
1161a1e1d21SSam Leffler 				if_printf(ifp, "bad channel ignored; "
1171a1e1d21SSam Leffler 					"freq %u flags %x number %u\n",
1181a1e1d21SSam Leffler 					c->ic_freq, c->ic_flags, i);
1191a1e1d21SSam Leffler 				c->ic_flags = 0;	/* NB: remove */
1201a1e1d21SSam Leffler 				continue;
1211a1e1d21SSam Leffler 			}
1221a1e1d21SSam Leffler 			setbit(ic->ic_chan_avail, i);
1231a1e1d21SSam Leffler 			/*
1241a1e1d21SSam Leffler 			 * Identify mode capabilities.
1251a1e1d21SSam Leffler 			 */
1261a1e1d21SSam Leffler 			if (IEEE80211_IS_CHAN_A(c))
1271a1e1d21SSam Leffler 				ic->ic_modecaps |= 1<<IEEE80211_MODE_11A;
1281a1e1d21SSam Leffler 			if (IEEE80211_IS_CHAN_B(c))
1291a1e1d21SSam Leffler 				ic->ic_modecaps |= 1<<IEEE80211_MODE_11B;
1301a1e1d21SSam Leffler 			if (IEEE80211_IS_CHAN_PUREG(c))
1311a1e1d21SSam Leffler 				ic->ic_modecaps |= 1<<IEEE80211_MODE_11G;
1321a1e1d21SSam Leffler 			if (IEEE80211_IS_CHAN_T(c))
1331a1e1d21SSam Leffler 				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO;
1341a1e1d21SSam Leffler 		}
1351a1e1d21SSam Leffler 	}
1361a1e1d21SSam Leffler 	/* validate ic->ic_curmode */
1371a1e1d21SSam Leffler 	if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0)
1381a1e1d21SSam Leffler 		ic->ic_curmode = IEEE80211_MODE_AUTO;
1391a1e1d21SSam Leffler 
1401a1e1d21SSam Leffler 	(void) ieee80211_setmode(ic, ic->ic_curmode);
1411a1e1d21SSam Leffler 
1421a1e1d21SSam Leffler 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
1431a1e1d21SSam Leffler 	if (ic->ic_lintval == 0)
1441a1e1d21SSam Leffler 		ic->ic_lintval = 100;		/* default sleep */
1451a1e1d21SSam Leffler 	ic->ic_bmisstimeout = 7*ic->ic_lintval;	/* default 7 beacons */
1461a1e1d21SSam Leffler 
1471a1e1d21SSam Leffler 	ieee80211_node_attach(ifp);
1481a1e1d21SSam Leffler 	ieee80211_proto_attach(ifp);
1491a1e1d21SSam Leffler }
1501a1e1d21SSam Leffler 
1511a1e1d21SSam Leffler void
1521a1e1d21SSam Leffler ieee80211_ifdetach(struct ifnet *ifp)
1531a1e1d21SSam Leffler {
1541a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
1551a1e1d21SSam Leffler 
1561a1e1d21SSam Leffler 	ieee80211_proto_detach(ifp);
1571a1e1d21SSam Leffler 	ieee80211_crypto_detach(ifp);
1581a1e1d21SSam Leffler 	ieee80211_node_detach(ifp);
1591a1e1d21SSam Leffler 	ifmedia_removeall(&ic->ic_media);
1601a1e1d21SSam Leffler 	bpfdetach(ifp);
1611a1e1d21SSam Leffler 	ether_ifdetach(ifp);
1621a1e1d21SSam Leffler }
1631a1e1d21SSam Leffler 
1641a1e1d21SSam Leffler /*
1651a1e1d21SSam Leffler  * Convert MHz frequency to IEEE channel number.
1661a1e1d21SSam Leffler  */
1671a1e1d21SSam Leffler u_int
1681a1e1d21SSam Leffler ieee80211_mhz2ieee(u_int freq, u_int flags)
1691a1e1d21SSam Leffler {
1701a1e1d21SSam Leffler 	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
1711a1e1d21SSam Leffler 		if (freq == 2484)
1721a1e1d21SSam Leffler 			return 14;
1731a1e1d21SSam Leffler 		if (freq < 2484)
1741a1e1d21SSam Leffler 			return (freq - 2407) / 5;
1751a1e1d21SSam Leffler 		else
1761a1e1d21SSam Leffler 			return 15 + ((freq - 2512) / 20);
177c032abb5SSam Leffler 	} else if (flags & IEEE80211_CHAN_5GHZ) {	/* 5Ghz band */
1781a1e1d21SSam Leffler 		return (freq - 5000) / 5;
1791a1e1d21SSam Leffler 	} else {				/* either, guess */
1801a1e1d21SSam Leffler 		if (freq == 2484)
1811a1e1d21SSam Leffler 			return 14;
1821a1e1d21SSam Leffler 		if (freq < 2484)
1831a1e1d21SSam Leffler 			return (freq - 2407) / 5;
1841a1e1d21SSam Leffler 		if (freq < 5000)
1851a1e1d21SSam Leffler 			return 15 + ((freq - 2512) / 20);
1861a1e1d21SSam Leffler 		return (freq - 5000) / 5;
1871a1e1d21SSam Leffler 	}
1881a1e1d21SSam Leffler }
1891a1e1d21SSam Leffler 
1901a1e1d21SSam Leffler /*
1911a1e1d21SSam Leffler  * Convert channel to IEEE channel number.
1921a1e1d21SSam Leffler  */
1931a1e1d21SSam Leffler u_int
1941a1e1d21SSam Leffler ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c)
1951a1e1d21SSam Leffler {
1961a1e1d21SSam Leffler 	if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX])
1971a1e1d21SSam Leffler 		return c - ic->ic_channels;
1981a1e1d21SSam Leffler 	else if (c == IEEE80211_CHAN_ANYC)
1991a1e1d21SSam Leffler 		return IEEE80211_CHAN_ANY;
2008be0d570SSam Leffler 	else if (c != NULL) {
2011a1e1d21SSam Leffler 		if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n",
2021a1e1d21SSam Leffler 			c->ic_freq, c->ic_flags);
2031a1e1d21SSam Leffler 		return 0;		/* XXX */
2048be0d570SSam Leffler 	} else {
2058be0d570SSam Leffler 		if_printf(&ic->ic_if, "invalid channel (NULL)\n");
2068be0d570SSam Leffler 		return 0;		/* XXX */
2071a1e1d21SSam Leffler 	}
2081a1e1d21SSam Leffler }
2091a1e1d21SSam Leffler 
2101a1e1d21SSam Leffler /*
2111a1e1d21SSam Leffler  * Convert IEEE channel number to MHz frequency.
2121a1e1d21SSam Leffler  */
2131a1e1d21SSam Leffler u_int
2141a1e1d21SSam Leffler ieee80211_ieee2mhz(u_int chan, u_int flags)
2151a1e1d21SSam Leffler {
2161a1e1d21SSam Leffler 	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
2171a1e1d21SSam Leffler 		if (chan == 14)
2181a1e1d21SSam Leffler 			return 2484;
2191a1e1d21SSam Leffler 		if (chan < 14)
2201a1e1d21SSam Leffler 			return 2407 + chan*5;
2211a1e1d21SSam Leffler 		else
2221a1e1d21SSam Leffler 			return 2512 + ((chan-15)*20);
2231a1e1d21SSam Leffler 	} else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */
2241a1e1d21SSam Leffler 		return 5000 + (chan*5);
2251a1e1d21SSam Leffler 	} else {				/* either, guess */
2261a1e1d21SSam Leffler 		if (chan == 14)
2271a1e1d21SSam Leffler 			return 2484;
2281a1e1d21SSam Leffler 		if (chan < 14)			/* 0-13 */
2291a1e1d21SSam Leffler 			return 2407 + chan*5;
2301a1e1d21SSam Leffler 		if (chan < 27)			/* 15-26 */
2311a1e1d21SSam Leffler 			return 2512 + ((chan-15)*20);
2321a1e1d21SSam Leffler 		return 5000 + (chan*5);
2331a1e1d21SSam Leffler 	}
2341a1e1d21SSam Leffler }
2351a1e1d21SSam Leffler 
2361a1e1d21SSam Leffler /*
2371a1e1d21SSam Leffler  * Setup the media data structures according to the channel and
2381a1e1d21SSam Leffler  * rate tables.  This must be called by the driver after
2391a1e1d21SSam Leffler  * ieee80211_attach and before most anything else.
2401a1e1d21SSam Leffler  */
2411a1e1d21SSam Leffler void
2421a1e1d21SSam Leffler ieee80211_media_init(struct ifnet *ifp,
2431a1e1d21SSam Leffler 	ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
2441a1e1d21SSam Leffler {
2451a1e1d21SSam Leffler #define	ADD(_ic, _s, _o) \
2461a1e1d21SSam Leffler 	ifmedia_add(&(_ic)->ic_media, \
2471a1e1d21SSam Leffler 		IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
2481a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
2491a1e1d21SSam Leffler 	struct ifmediareq imr;
2501a1e1d21SSam Leffler 	int i, j, mode, rate, maxrate, mword, mopt, r;
2511a1e1d21SSam Leffler 	struct ieee80211_rateset *rs;
2521a1e1d21SSam Leffler 	struct ieee80211_rateset allrates;
2531a1e1d21SSam Leffler 
2541a1e1d21SSam Leffler 	/*
2551a1e1d21SSam Leffler 	 * Fill in media characteristics.
2561a1e1d21SSam Leffler 	 */
2571a1e1d21SSam Leffler 	ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
2581a1e1d21SSam Leffler 	maxrate = 0;
2591a1e1d21SSam Leffler 	memset(&allrates, 0, sizeof(allrates));
2601a1e1d21SSam Leffler 	for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
2611a1e1d21SSam Leffler 		static const u_int mopts[] = {
2621a1e1d21SSam Leffler 			IFM_AUTO,
2631a1e1d21SSam Leffler 			IFM_MAKEMODE(IFM_IEEE80211_11A),
2641a1e1d21SSam Leffler 			IFM_MAKEMODE(IFM_IEEE80211_11B),
2651a1e1d21SSam Leffler 			IFM_MAKEMODE(IFM_IEEE80211_11G),
2661a1e1d21SSam Leffler 			IFM_MAKEMODE(IFM_IEEE80211_11A) | IFM_IEEE80211_TURBO,
2671a1e1d21SSam Leffler 		};
2681a1e1d21SSam Leffler 		if ((ic->ic_modecaps & (1<<mode)) == 0)
2691a1e1d21SSam Leffler 			continue;
2701a1e1d21SSam Leffler 		mopt = mopts[mode];
2711a1e1d21SSam Leffler 		ADD(ic, IFM_AUTO, mopt);	/* e.g. 11a auto */
2721a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_IBSS)
2731a1e1d21SSam Leffler 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC);
2741a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_HOSTAP)
2751a1e1d21SSam Leffler 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP);
2761a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_AHDEMO)
2771a1e1d21SSam Leffler 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
2781a1e1d21SSam Leffler 		if (mode == IEEE80211_MODE_AUTO)
2791a1e1d21SSam Leffler 			continue;
2801a1e1d21SSam Leffler 		if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
2811a1e1d21SSam Leffler 		rs = &ic->ic_sup_rates[mode];
2821a1e1d21SSam Leffler 		for (i = 0; i < rs->rs_nrates; i++) {
2831a1e1d21SSam Leffler 			rate = rs->rs_rates[i];
2841a1e1d21SSam Leffler 			mword = ieee80211_rate2media(ic, rate, mode);
2851a1e1d21SSam Leffler 			if (mword == 0)
2861a1e1d21SSam Leffler 				continue;
2871a1e1d21SSam Leffler 			printf("%s%d%sMbps", (i != 0 ? " " : ""),
2881a1e1d21SSam Leffler 			    (rate & IEEE80211_RATE_VAL) / 2,
2891a1e1d21SSam Leffler 			    ((rate & 0x1) != 0 ? ".5" : ""));
2901a1e1d21SSam Leffler 			ADD(ic, mword, mopt);
2911a1e1d21SSam Leffler 			if (ic->ic_caps & IEEE80211_C_IBSS)
2921a1e1d21SSam Leffler 				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
2931a1e1d21SSam Leffler 			if (ic->ic_caps & IEEE80211_C_HOSTAP)
2941a1e1d21SSam Leffler 				ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
2951a1e1d21SSam Leffler 			if (ic->ic_caps & IEEE80211_C_AHDEMO)
2961a1e1d21SSam Leffler 				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
2971a1e1d21SSam Leffler 			/*
2981a1e1d21SSam Leffler 			 * Add rate to the collection of all rates.
2991a1e1d21SSam Leffler 			 */
3001a1e1d21SSam Leffler 			r = rate & IEEE80211_RATE_VAL;
3011a1e1d21SSam Leffler 			for (j = 0; j < allrates.rs_nrates; j++)
3021a1e1d21SSam Leffler 				if (allrates.rs_rates[j] == r)
3031a1e1d21SSam Leffler 					break;
3041a1e1d21SSam Leffler 			if (j == allrates.rs_nrates) {
3051a1e1d21SSam Leffler 				/* unique, add to the set */
3061a1e1d21SSam Leffler 				allrates.rs_rates[j] = r;
3071a1e1d21SSam Leffler 				allrates.rs_nrates++;
3081a1e1d21SSam Leffler 			}
3091a1e1d21SSam Leffler 			rate = (rate & IEEE80211_RATE_VAL) / 2;
3101a1e1d21SSam Leffler 			if (rate > maxrate)
3111a1e1d21SSam Leffler 				maxrate = rate;
3121a1e1d21SSam Leffler 		}
3131a1e1d21SSam Leffler 		printf("\n");
3141a1e1d21SSam Leffler 	}
3151a1e1d21SSam Leffler 	for (i = 0; i < allrates.rs_nrates; i++) {
3161a1e1d21SSam Leffler 		mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
3171a1e1d21SSam Leffler 				IEEE80211_MODE_AUTO);
3181a1e1d21SSam Leffler 		if (mword == 0)
3191a1e1d21SSam Leffler 			continue;
3201a1e1d21SSam Leffler 		mword = IFM_SUBTYPE(mword);	/* remove media options */
3211a1e1d21SSam Leffler 		ADD(ic, mword, 0);
3221a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_IBSS)
3231a1e1d21SSam Leffler 			ADD(ic, mword, IFM_IEEE80211_ADHOC);
3241a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_HOSTAP)
3251a1e1d21SSam Leffler 			ADD(ic, mword, IFM_IEEE80211_HOSTAP);
3261a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_AHDEMO)
3271a1e1d21SSam Leffler 			ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
3281a1e1d21SSam Leffler 	}
3291a1e1d21SSam Leffler 	ieee80211_media_status(ifp, &imr);
3301a1e1d21SSam Leffler 	ifmedia_set(&ic->ic_media, imr.ifm_active);
3311a1e1d21SSam Leffler 
3321a1e1d21SSam Leffler 	if (maxrate)
3331a1e1d21SSam Leffler 		ifp->if_baudrate = IF_Mbps(maxrate);
3341a1e1d21SSam Leffler #undef ADD
3351a1e1d21SSam Leffler }
3361a1e1d21SSam Leffler 
3371a1e1d21SSam Leffler static int
3381a1e1d21SSam Leffler findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
3391a1e1d21SSam Leffler {
3401a1e1d21SSam Leffler #define	IEEERATE(_ic,_m,_i) \
3411a1e1d21SSam Leffler 	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
3421a1e1d21SSam Leffler 	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
3431a1e1d21SSam Leffler 	for (i = 0; i < nrates; i++)
3441a1e1d21SSam Leffler 		if (IEEERATE(ic, mode, i) == rate)
3451a1e1d21SSam Leffler 			return i;
3461a1e1d21SSam Leffler 	return -1;
3471a1e1d21SSam Leffler #undef IEEERATE
3481a1e1d21SSam Leffler }
3491a1e1d21SSam Leffler 
3501a1e1d21SSam Leffler /*
3511a1e1d21SSam Leffler  * Handle a media change request.
3521a1e1d21SSam Leffler  */
3531a1e1d21SSam Leffler int
3541a1e1d21SSam Leffler ieee80211_media_change(struct ifnet *ifp)
3551a1e1d21SSam Leffler {
3561a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
3571a1e1d21SSam Leffler 	struct ifmedia_entry *ime;
3581a1e1d21SSam Leffler 	enum ieee80211_opmode newopmode;
3591a1e1d21SSam Leffler 	enum ieee80211_phymode newphymode;
3601a1e1d21SSam Leffler 	int i, j, newrate, error = 0;
3611a1e1d21SSam Leffler 
3621a1e1d21SSam Leffler 	ime = ic->ic_media.ifm_cur;
3631a1e1d21SSam Leffler 	/*
3641a1e1d21SSam Leffler 	 * First, identify the phy mode.
3651a1e1d21SSam Leffler 	 */
3661a1e1d21SSam Leffler 	switch (IFM_MODE(ime->ifm_media)) {
3671a1e1d21SSam Leffler 	case IFM_IEEE80211_11A:
3681a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_11A;
3691a1e1d21SSam Leffler 		break;
3701a1e1d21SSam Leffler 	case IFM_IEEE80211_11B:
3711a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_11B;
3721a1e1d21SSam Leffler 		break;
3731a1e1d21SSam Leffler 	case IFM_IEEE80211_11G:
3741a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_11G;
3751a1e1d21SSam Leffler 		break;
3761a1e1d21SSam Leffler 	case IFM_AUTO:
3771a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_AUTO;
3781a1e1d21SSam Leffler 		break;
3791a1e1d21SSam Leffler 	default:
3801a1e1d21SSam Leffler 		return EINVAL;
3811a1e1d21SSam Leffler 	}
3821a1e1d21SSam Leffler 	/*
3831a1e1d21SSam Leffler 	 * Turbo mode is an ``option''.  Eventually it
3841a1e1d21SSam Leffler 	 * needs to be applied to 11g too.
3851a1e1d21SSam Leffler 	 */
3861a1e1d21SSam Leffler 	if (ime->ifm_media & IFM_IEEE80211_TURBO) {
3871a1e1d21SSam Leffler 		if (newphymode != IEEE80211_MODE_11A)
3881a1e1d21SSam Leffler 			return EINVAL;
3891a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_TURBO;
3901a1e1d21SSam Leffler 	}
3911a1e1d21SSam Leffler 	/*
3921a1e1d21SSam Leffler 	 * Validate requested mode is available.
3931a1e1d21SSam Leffler 	 */
3941a1e1d21SSam Leffler 	if ((ic->ic_modecaps & (1<<newphymode)) == 0)
3951a1e1d21SSam Leffler 		return EINVAL;
3961a1e1d21SSam Leffler 
3971a1e1d21SSam Leffler 	/*
3981a1e1d21SSam Leffler 	 * Next, the fixed/variable rate.
3991a1e1d21SSam Leffler 	 */
4001a1e1d21SSam Leffler 	i = -1;
4011a1e1d21SSam Leffler 	if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
4021a1e1d21SSam Leffler 		/*
4031a1e1d21SSam Leffler 		 * Convert media subtype to rate.
4041a1e1d21SSam Leffler 		 */
4051a1e1d21SSam Leffler 		newrate = ieee80211_media2rate(ime->ifm_media);
4061a1e1d21SSam Leffler 		if (newrate == 0)
4071a1e1d21SSam Leffler 			return EINVAL;
4081a1e1d21SSam Leffler 		/*
4091a1e1d21SSam Leffler 		 * Check the rate table for the specified/current phy.
4101a1e1d21SSam Leffler 		 */
4111a1e1d21SSam Leffler 		if (newphymode == IEEE80211_MODE_AUTO) {
4121a1e1d21SSam Leffler 			/*
4131a1e1d21SSam Leffler 			 * In autoselect mode search for the rate.
4141a1e1d21SSam Leffler 			 */
4151a1e1d21SSam Leffler 			for (j = IEEE80211_MODE_11A;
4161a1e1d21SSam Leffler 			     j < IEEE80211_MODE_MAX; j++) {
4171a1e1d21SSam Leffler 				if ((ic->ic_modecaps & (1<<j)) == 0)
4181a1e1d21SSam Leffler 					continue;
4191a1e1d21SSam Leffler 				i = findrate(ic, j, newrate);
4201a1e1d21SSam Leffler 				if (i != -1) {
4211a1e1d21SSam Leffler 					/* lock mode too */
4221a1e1d21SSam Leffler 					newphymode = j;
4231a1e1d21SSam Leffler 					break;
4241a1e1d21SSam Leffler 				}
4251a1e1d21SSam Leffler 			}
4261a1e1d21SSam Leffler 		} else {
4271a1e1d21SSam Leffler 			i = findrate(ic, newphymode, newrate);
4281a1e1d21SSam Leffler 		}
4291a1e1d21SSam Leffler 		if (i == -1)			/* mode/rate mismatch */
4301a1e1d21SSam Leffler 			return EINVAL;
4311a1e1d21SSam Leffler 	}
4321a1e1d21SSam Leffler 	/* NB: defer rate setting to later */
4331a1e1d21SSam Leffler 
4341a1e1d21SSam Leffler 	/*
4351a1e1d21SSam Leffler 	 * Deduce new operating mode but don't install it just yet.
4361a1e1d21SSam Leffler 	 */
4371a1e1d21SSam Leffler 	if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
4381a1e1d21SSam Leffler 	    (IFM_IEEE80211_ADHOC|IFM_FLAG0))
4391a1e1d21SSam Leffler 		newopmode = IEEE80211_M_AHDEMO;
4401a1e1d21SSam Leffler 	else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
4411a1e1d21SSam Leffler 		newopmode = IEEE80211_M_HOSTAP;
4421a1e1d21SSam Leffler 	else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
4431a1e1d21SSam Leffler 		newopmode = IEEE80211_M_IBSS;
4441a1e1d21SSam Leffler 	else
4451a1e1d21SSam Leffler 		newopmode = IEEE80211_M_STA;
4461a1e1d21SSam Leffler 
4471a1e1d21SSam Leffler 	/*
4481a1e1d21SSam Leffler 	 * Autoselect doesn't make sense when operating as an AP.
4491a1e1d21SSam Leffler 	 * If no phy mode has been selected, pick one and lock it
4501a1e1d21SSam Leffler 	 * down so rate tables can be used in forming beacon frames
4511a1e1d21SSam Leffler 	 * and the like.
4521a1e1d21SSam Leffler 	 */
4531a1e1d21SSam Leffler 	if (newopmode == IEEE80211_M_HOSTAP &&
4541a1e1d21SSam Leffler 	    newphymode == IEEE80211_MODE_AUTO) {
4551a1e1d21SSam Leffler 		for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++)
4561a1e1d21SSam Leffler 			if (ic->ic_modecaps & (1<<j)) {
4571a1e1d21SSam Leffler 				newphymode = j;
4581a1e1d21SSam Leffler 				break;
4591a1e1d21SSam Leffler 			}
4601a1e1d21SSam Leffler 	}
4611a1e1d21SSam Leffler 
4621a1e1d21SSam Leffler 	/*
4631a1e1d21SSam Leffler 	 * Handle phy mode change.
4641a1e1d21SSam Leffler 	 */
4651a1e1d21SSam Leffler 	if (ic->ic_curmode != newphymode) {		/* change phy mode */
4661a1e1d21SSam Leffler 		error = ieee80211_setmode(ic, newphymode);
4671a1e1d21SSam Leffler 		if (error != 0)
4681a1e1d21SSam Leffler 			return error;
4691a1e1d21SSam Leffler 		error = ENETRESET;
4701a1e1d21SSam Leffler 	}
4711a1e1d21SSam Leffler 
4721a1e1d21SSam Leffler 	/*
4731a1e1d21SSam Leffler 	 * Committed to changes, install the rate setting.
4741a1e1d21SSam Leffler 	 */
4751a1e1d21SSam Leffler 	if (ic->ic_fixed_rate != i) {
4761a1e1d21SSam Leffler 		ic->ic_fixed_rate = i;			/* set fixed tx rate */
4771a1e1d21SSam Leffler 		error = ENETRESET;
4781a1e1d21SSam Leffler 	}
4791a1e1d21SSam Leffler 
4801a1e1d21SSam Leffler 	/*
4811a1e1d21SSam Leffler 	 * Handle operating mode change.
4821a1e1d21SSam Leffler 	 */
4831a1e1d21SSam Leffler 	if (ic->ic_opmode != newopmode) {
4841a1e1d21SSam Leffler 		ic->ic_opmode = newopmode;
4851a1e1d21SSam Leffler 		switch (newopmode) {
4861a1e1d21SSam Leffler 		case IEEE80211_M_AHDEMO:
4871a1e1d21SSam Leffler 		case IEEE80211_M_HOSTAP:
4881a1e1d21SSam Leffler 		case IEEE80211_M_STA:
4891a1e1d21SSam Leffler 			ic->ic_flags &= ~IEEE80211_F_IBSSON;
4901a1e1d21SSam Leffler 			break;
4911a1e1d21SSam Leffler 		case IEEE80211_M_IBSS:
4921a1e1d21SSam Leffler 			ic->ic_flags |= IEEE80211_F_IBSSON;
4931a1e1d21SSam Leffler #ifdef notdef
4941a1e1d21SSam Leffler 			if (ic->ic_curmode == IEEE80211_MODE_11G)
4951a1e1d21SSam Leffler 				ieee80211_set11gbasicrates(
4961a1e1d21SSam Leffler 					&ic->ic_suprates[newphymode],
4971a1e1d21SSam Leffler 					IEEE80211_MODE_11B);
4981a1e1d21SSam Leffler #endif
4991a1e1d21SSam Leffler 			break;
5001a1e1d21SSam Leffler 		}
5011a1e1d21SSam Leffler 		error = ENETRESET;
5021a1e1d21SSam Leffler 	}
5031a1e1d21SSam Leffler #ifdef notdef
5041a1e1d21SSam Leffler 	if (error == 0)
5051a1e1d21SSam Leffler 		ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
5061a1e1d21SSam Leffler #endif
5071a1e1d21SSam Leffler 	return error;
5081a1e1d21SSam Leffler }
5091a1e1d21SSam Leffler 
5101a1e1d21SSam Leffler void
5111a1e1d21SSam Leffler ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
5121a1e1d21SSam Leffler {
5131a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
5141a1e1d21SSam Leffler 	struct ieee80211_node *ni = NULL;
5151a1e1d21SSam Leffler 
5161a1e1d21SSam Leffler 	imr->ifm_status = IFM_AVALID;
5171a1e1d21SSam Leffler 	imr->ifm_active = IFM_IEEE80211;
5181a1e1d21SSam Leffler 	if (ic->ic_state == IEEE80211_S_RUN)
5191a1e1d21SSam Leffler 		imr->ifm_status |= IFM_ACTIVE;
5201a1e1d21SSam Leffler 	imr->ifm_active |= IFM_AUTO;
5211a1e1d21SSam Leffler 	switch (ic->ic_opmode) {
5221a1e1d21SSam Leffler 	case IEEE80211_M_STA:
5231a1e1d21SSam Leffler 		ni = ic->ic_bss;
5241a1e1d21SSam Leffler 		/* calculate rate subtype */
5251a1e1d21SSam Leffler 		imr->ifm_active |= ieee80211_rate2media(ic,
5261a1e1d21SSam Leffler 			ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
5271a1e1d21SSam Leffler 		break;
5281a1e1d21SSam Leffler 	case IEEE80211_M_IBSS:
5291a1e1d21SSam Leffler 		imr->ifm_active |= IFM_IEEE80211_ADHOC;
5301a1e1d21SSam Leffler 		break;
5311a1e1d21SSam Leffler 	case IEEE80211_M_AHDEMO:
5321a1e1d21SSam Leffler 		/* should not come here */
5331a1e1d21SSam Leffler 		break;
5341a1e1d21SSam Leffler 	case IEEE80211_M_HOSTAP:
5351a1e1d21SSam Leffler 		imr->ifm_active |= IFM_IEEE80211_HOSTAP;
5361a1e1d21SSam Leffler 		break;
5371a1e1d21SSam Leffler 	}
5381a1e1d21SSam Leffler 	switch (ic->ic_curmode) {
5391a1e1d21SSam Leffler 	case IEEE80211_MODE_11A:
5401a1e1d21SSam Leffler 		imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A);
5411a1e1d21SSam Leffler 		break;
5421a1e1d21SSam Leffler 	case IEEE80211_MODE_11B:
5431a1e1d21SSam Leffler 		imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B);
5441a1e1d21SSam Leffler 		break;
5451a1e1d21SSam Leffler 	case IEEE80211_MODE_11G:
5461a1e1d21SSam Leffler 		imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G);
5471a1e1d21SSam Leffler 		break;
5481a1e1d21SSam Leffler 	case IEEE80211_MODE_TURBO:
5491a1e1d21SSam Leffler 		imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A)
5501a1e1d21SSam Leffler 				|  IFM_IEEE80211_TURBO;
5511a1e1d21SSam Leffler 		break;
5521a1e1d21SSam Leffler 	}
5531a1e1d21SSam Leffler }
5541a1e1d21SSam Leffler 
5551a1e1d21SSam Leffler void
5561a1e1d21SSam Leffler ieee80211_watchdog(struct ifnet *ifp)
5571a1e1d21SSam Leffler {
5581a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
5591a1e1d21SSam Leffler 
5601a1e1d21SSam Leffler 	if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
5611a1e1d21SSam Leffler 		ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
5621a1e1d21SSam Leffler 	if (ic->ic_inact_timer && --ic->ic_inact_timer == 0)
5631a1e1d21SSam Leffler 		ieee80211_timeout_nodes(ic);
5641a1e1d21SSam Leffler 
5651a1e1d21SSam Leffler 	if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0)
5661a1e1d21SSam Leffler 		ifp->if_timer = 1;
5671a1e1d21SSam Leffler }
5681a1e1d21SSam Leffler 
5691a1e1d21SSam Leffler /*
5701a1e1d21SSam Leffler  * Mark the basic rates for the 11g rate table based on the
5711a1e1d21SSam Leffler  * operating mode.  For real 11g we mark all the 11b rates
5721a1e1d21SSam Leffler  * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
5731a1e1d21SSam Leffler  * 11b rates.  There's also a pseudo 11a-mode used to mark only
5741a1e1d21SSam Leffler  * the basic OFDM rates.
5751a1e1d21SSam Leffler  */
5761a1e1d21SSam Leffler static void
5771a1e1d21SSam Leffler ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
5781a1e1d21SSam Leffler {
5791a1e1d21SSam Leffler 	static const struct ieee80211_rateset basic[] = {
5801a1e1d21SSam Leffler 	    { 3, { 12, 24, 48 } },		/* IEEE80211_MODE_11A */
5811a1e1d21SSam Leffler 	    { 4, { 2, 4, 11, 22 } },		/* IEEE80211_MODE_11B */
5821a1e1d21SSam Leffler 	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */
5831a1e1d21SSam Leffler 	    { 0 },				/* IEEE80211_MODE_TURBO	*/
5841a1e1d21SSam Leffler 	};
5851a1e1d21SSam Leffler 	int i, j;
5861a1e1d21SSam Leffler 
5871a1e1d21SSam Leffler 	for (i = 0; i < rs->rs_nrates; i++) {
5881a1e1d21SSam Leffler 		rs->rs_rates[i] &= IEEE80211_RATE_VAL;
5891a1e1d21SSam Leffler 		for (j = 0; j < basic[mode].rs_nrates; j++)
5901a1e1d21SSam Leffler 			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
5911a1e1d21SSam Leffler 				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
5921a1e1d21SSam Leffler 				break;
5931a1e1d21SSam Leffler 			}
5941a1e1d21SSam Leffler 	}
5951a1e1d21SSam Leffler }
5961a1e1d21SSam Leffler 
5971a1e1d21SSam Leffler /*
5981a1e1d21SSam Leffler  * Set the current phy mode and recalculate the active channel
5991a1e1d21SSam Leffler  * set based on the available channels for this mode.  Also
6001a1e1d21SSam Leffler  * select a new default/current channel if the current one is
6011a1e1d21SSam Leffler  * inappropriate for this mode.
6021a1e1d21SSam Leffler  */
6031a1e1d21SSam Leffler int
6041a1e1d21SSam Leffler ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
6051a1e1d21SSam Leffler {
6061a1e1d21SSam Leffler #define	N(a)	(sizeof(a) / sizeof(a[0]))
6071a1e1d21SSam Leffler 	static const u_int chanflags[] = {
6081a1e1d21SSam Leffler 		0,			/* IEEE80211_MODE_AUTO */
6091a1e1d21SSam Leffler 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
6101a1e1d21SSam Leffler 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
6111a1e1d21SSam Leffler 		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
6121a1e1d21SSam Leffler 		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO	*/
6131a1e1d21SSam Leffler 	};
6141a1e1d21SSam Leffler 	struct ieee80211_channel *c;
6151a1e1d21SSam Leffler 	u_int modeflags;
6161a1e1d21SSam Leffler 	int i;
6171a1e1d21SSam Leffler 
6181a1e1d21SSam Leffler 	/* validate new mode */
6191a1e1d21SSam Leffler 	if ((ic->ic_modecaps & (1<<mode)) == 0) {
6201a1e1d21SSam Leffler 		IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n",
6211a1e1d21SSam Leffler 			__func__, mode, ic->ic_modecaps));
6221a1e1d21SSam Leffler 		return EINVAL;
6231a1e1d21SSam Leffler 	}
6241a1e1d21SSam Leffler 
6251a1e1d21SSam Leffler 	/*
6261a1e1d21SSam Leffler 	 * Verify at least one channel is present in the available
6271a1e1d21SSam Leffler 	 * channel list before committing to the new mode.
6281a1e1d21SSam Leffler 	 */
6291a1e1d21SSam Leffler 	KASSERT(mode < N(chanflags), ("Unexpected mode %u\n", mode));
6301a1e1d21SSam Leffler 	modeflags = chanflags[mode];
6311a1e1d21SSam Leffler 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
6321a1e1d21SSam Leffler 		c = &ic->ic_channels[i];
6331a1e1d21SSam Leffler 		if (mode == IEEE80211_MODE_AUTO) {
6341a1e1d21SSam Leffler 			/* ignore turbo channels for autoselect */
6351a1e1d21SSam Leffler 			if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
6361a1e1d21SSam Leffler 				break;
6371a1e1d21SSam Leffler 		} else {
6381a1e1d21SSam Leffler 			if ((c->ic_flags & modeflags) == modeflags)
6391a1e1d21SSam Leffler 				break;
6401a1e1d21SSam Leffler 		}
6411a1e1d21SSam Leffler 	}
6421a1e1d21SSam Leffler 	if (i > IEEE80211_CHAN_MAX) {
6431a1e1d21SSam Leffler 		IEEE80211_DPRINTF(("%s: no channels found for mode %u\n",
6441a1e1d21SSam Leffler 			__func__, mode));
6451a1e1d21SSam Leffler 		return EINVAL;
6461a1e1d21SSam Leffler 	}
6471a1e1d21SSam Leffler 
6481a1e1d21SSam Leffler 	/*
6491a1e1d21SSam Leffler 	 * Calculate the active channel set.
6501a1e1d21SSam Leffler 	 */
6511a1e1d21SSam Leffler 	memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active));
6521a1e1d21SSam Leffler 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
6531a1e1d21SSam Leffler 		c = &ic->ic_channels[i];
6541a1e1d21SSam Leffler 		if (mode == IEEE80211_MODE_AUTO) {
6551a1e1d21SSam Leffler 			/* take anything but pure turbo channels */
6561a1e1d21SSam Leffler 			if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
6571a1e1d21SSam Leffler 				setbit(ic->ic_chan_active, i);
6581a1e1d21SSam Leffler 		} else {
6591a1e1d21SSam Leffler 			if ((c->ic_flags & modeflags) == modeflags)
6601a1e1d21SSam Leffler 				setbit(ic->ic_chan_active, i);
6611a1e1d21SSam Leffler 		}
6621a1e1d21SSam Leffler 	}
6631a1e1d21SSam Leffler 	/*
6641a1e1d21SSam Leffler 	 * If no current/default channel is setup or the current
6651a1e1d21SSam Leffler 	 * channel is wrong for the mode then pick the first
6661a1e1d21SSam Leffler 	 * available channel from the active list.  This is likely
6671a1e1d21SSam Leffler 	 * not the right one.
6681a1e1d21SSam Leffler 	 */
6691a1e1d21SSam Leffler 	if (ic->ic_ibss_chan == NULL ||
6701a1e1d21SSam Leffler 	    isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
6711a1e1d21SSam Leffler 		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
6721a1e1d21SSam Leffler 			if (isset(ic->ic_chan_active, i)) {
6731a1e1d21SSam Leffler 				ic->ic_ibss_chan = &ic->ic_channels[i];
6741a1e1d21SSam Leffler 				break;
6751a1e1d21SSam Leffler 			}
6761a1e1d21SSam Leffler 	}
6771a1e1d21SSam Leffler 
6781a1e1d21SSam Leffler 	/*
6791a1e1d21SSam Leffler 	 * Set/reset state flags that influence beacon contents, etc.
6801a1e1d21SSam Leffler 	 *
6811a1e1d21SSam Leffler 	 * XXX what if we have stations already associated???
6821a1e1d21SSam Leffler 	 * XXX probably not right for autoselect?
6831a1e1d21SSam Leffler 	 */
6841a1e1d21SSam Leffler 	if (mode == IEEE80211_MODE_11G) {
6851a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_SHSLOT)
6861a1e1d21SSam Leffler 			ic->ic_flags |= IEEE80211_F_SHSLOT;
6871a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_SHPREAMBLE)
6881a1e1d21SSam Leffler 			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
6891a1e1d21SSam Leffler 		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
6901a1e1d21SSam Leffler 			IEEE80211_MODE_11G);
6911a1e1d21SSam Leffler 	} else {
6921a1e1d21SSam Leffler 		ic->ic_flags &= ~(IEEE80211_F_SHSLOT | IEEE80211_F_SHPREAMBLE);
6931a1e1d21SSam Leffler 	}
6941a1e1d21SSam Leffler 
6951a1e1d21SSam Leffler 	ic->ic_curmode = mode;
6961a1e1d21SSam Leffler 	return 0;
6971a1e1d21SSam Leffler #undef N
6981a1e1d21SSam Leffler }
6991a1e1d21SSam Leffler 
7001a1e1d21SSam Leffler /*
7011a1e1d21SSam Leffler  * Return the phy mode for with the specified channel so the
7021a1e1d21SSam Leffler  * caller can select a rate set.  This is problematic and the
7031a1e1d21SSam Leffler  * work here assumes how things work elsewhere in this code.
7041a1e1d21SSam Leffler  */
7051a1e1d21SSam Leffler enum ieee80211_phymode
7061a1e1d21SSam Leffler ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
7071a1e1d21SSam Leffler {
7081a1e1d21SSam Leffler 	/*
7091a1e1d21SSam Leffler 	 * NB: this assumes the channel would not be supplied to us
7101a1e1d21SSam Leffler 	 *     unless it was already compatible with the current mode.
7111a1e1d21SSam Leffler 	 */
7121a1e1d21SSam Leffler 	if (ic->ic_curmode != IEEE80211_MODE_AUTO)
7131a1e1d21SSam Leffler 		return ic->ic_curmode;
7141a1e1d21SSam Leffler 	/*
7151a1e1d21SSam Leffler 	 * In autoselect mode; deduce a mode based on the channel
7161a1e1d21SSam Leffler 	 * characteristics.  We assume that turbo-only channels
7171a1e1d21SSam Leffler 	 * are not considered when the channel set is constructed.
7181a1e1d21SSam Leffler 	 */
7191a1e1d21SSam Leffler 	if (IEEE80211_IS_CHAN_5GHZ(chan))
7201a1e1d21SSam Leffler 		return IEEE80211_MODE_11A;
7211a1e1d21SSam Leffler 	else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN))
7221a1e1d21SSam Leffler 		return IEEE80211_MODE_11G;
7231a1e1d21SSam Leffler 	else
7241a1e1d21SSam Leffler 		return IEEE80211_MODE_11B;
7251a1e1d21SSam Leffler }
7261a1e1d21SSam Leffler 
7271a1e1d21SSam Leffler /*
7281a1e1d21SSam Leffler  * convert IEEE80211 rate value to ifmedia subtype.
7291a1e1d21SSam Leffler  * ieee80211 rate is in unit of 0.5Mbps.
7301a1e1d21SSam Leffler  */
7311a1e1d21SSam Leffler int
7321a1e1d21SSam Leffler ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
7331a1e1d21SSam Leffler {
7341a1e1d21SSam Leffler #define	N(a)	(sizeof(a) / sizeof(a[0]))
7351a1e1d21SSam Leffler 	static const struct {
7361a1e1d21SSam Leffler 		u_int	m;	/* rate + mode */
7371a1e1d21SSam Leffler 		u_int	r;	/* if_media rate */
7381a1e1d21SSam Leffler 	} rates[] = {
7391a1e1d21SSam Leffler 		{   2 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS1 },
7401a1e1d21SSam Leffler 		{   4 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS2 },
7411a1e1d21SSam Leffler 		{  11 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS5 },
7421a1e1d21SSam Leffler 		{  22 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS11 },
7431a1e1d21SSam Leffler 		{  44 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS22 },
7441a1e1d21SSam Leffler 		{  12 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM6 },
7451a1e1d21SSam Leffler 		{  18 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM9 },
7461a1e1d21SSam Leffler 		{  24 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM12 },
7471a1e1d21SSam Leffler 		{  36 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM18 },
7481a1e1d21SSam Leffler 		{  48 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM24 },
7491a1e1d21SSam Leffler 		{  72 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM36 },
7501a1e1d21SSam Leffler 		{  96 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM48 },
7511a1e1d21SSam Leffler 		{ 108 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM54 },
7521a1e1d21SSam Leffler 		{   2 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS1 },
7531a1e1d21SSam Leffler 		{   4 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS2 },
7541a1e1d21SSam Leffler 		{  11 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS5 },
7551a1e1d21SSam Leffler 		{  22 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS11 },
7561a1e1d21SSam Leffler 		{  12 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM6 },
7571a1e1d21SSam Leffler 		{  18 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM9 },
7581a1e1d21SSam Leffler 		{  24 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM12 },
7591a1e1d21SSam Leffler 		{  36 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM18 },
7601a1e1d21SSam Leffler 		{  48 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM24 },
7611a1e1d21SSam Leffler 		{  72 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM36 },
7621a1e1d21SSam Leffler 		{  96 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM48 },
7631a1e1d21SSam Leffler 		{ 108 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM54 },
7641a1e1d21SSam Leffler 		/* NB: OFDM72 doesn't realy exist so we don't handle it */
7651a1e1d21SSam Leffler 	};
7661a1e1d21SSam Leffler 	u_int mask, i;
7671a1e1d21SSam Leffler 
7681a1e1d21SSam Leffler 	mask = rate & IEEE80211_RATE_VAL;
7691a1e1d21SSam Leffler 	switch (mode) {
7701a1e1d21SSam Leffler 	case IEEE80211_MODE_11A:
7711a1e1d21SSam Leffler 	case IEEE80211_MODE_TURBO:
7721a1e1d21SSam Leffler 		mask |= IFM_MAKEMODE(IFM_IEEE80211_11A);
7731a1e1d21SSam Leffler 		break;
7741a1e1d21SSam Leffler 	case IEEE80211_MODE_11B:
7751a1e1d21SSam Leffler 		mask |= IFM_MAKEMODE(IFM_IEEE80211_11B);
7761a1e1d21SSam Leffler 		break;
7771a1e1d21SSam Leffler 	case IEEE80211_MODE_AUTO:
7781a1e1d21SSam Leffler 		/* NB: ic may be NULL for some drivers */
7791a1e1d21SSam Leffler 		if (ic && ic->ic_phytype == IEEE80211_T_FH) {
7801a1e1d21SSam Leffler 			/* must handle these specially */
7811a1e1d21SSam Leffler 			switch (mask) {
7821a1e1d21SSam Leffler 			case 2:		return IFM_IEEE80211_FH1;
7831a1e1d21SSam Leffler 			case 4:		return IFM_IEEE80211_FH2;
7841a1e1d21SSam Leffler 			}
7851a1e1d21SSam Leffler 			return IFM_AUTO;
7861a1e1d21SSam Leffler 		}
7871a1e1d21SSam Leffler 		/* NB: hack, 11g matches both 11b+11a rates */
7881a1e1d21SSam Leffler 		/* fall thru... */
7891a1e1d21SSam Leffler 	case IEEE80211_MODE_11G:
7901a1e1d21SSam Leffler 		mask |= IFM_MAKEMODE(IFM_IEEE80211_11G);
7911a1e1d21SSam Leffler 		break;
7921a1e1d21SSam Leffler 	}
7931a1e1d21SSam Leffler 	for (i = 0; i < N(rates); i++)
7941a1e1d21SSam Leffler 		if (rates[i].m == mask)
7951a1e1d21SSam Leffler 			return rates[i].r;
7961a1e1d21SSam Leffler 	return IFM_AUTO;
7971a1e1d21SSam Leffler #undef N
7981a1e1d21SSam Leffler }
7991a1e1d21SSam Leffler 
8001a1e1d21SSam Leffler int
8011a1e1d21SSam Leffler ieee80211_media2rate(int mword)
8021a1e1d21SSam Leffler {
8031a1e1d21SSam Leffler #define	N(a)	(sizeof(a) / sizeof(a[0]))
8041a1e1d21SSam Leffler 	static const int ieeerates[] = {
8051a1e1d21SSam Leffler 		-1,		/* IFM_AUTO */
8061a1e1d21SSam Leffler 		0,		/* IFM_MANUAL */
8071a1e1d21SSam Leffler 		0,		/* IFM_NONE */
8081a1e1d21SSam Leffler 		2,		/* IFM_IEEE80211_FH1 */
8091a1e1d21SSam Leffler 		4,		/* IFM_IEEE80211_FH2 */
8101a1e1d21SSam Leffler 		2,		/* IFM_IEEE80211_DS1 */
8111a1e1d21SSam Leffler 		4,		/* IFM_IEEE80211_DS2 */
8121a1e1d21SSam Leffler 		11,		/* IFM_IEEE80211_DS5 */
8131a1e1d21SSam Leffler 		22,		/* IFM_IEEE80211_DS11 */
8141a1e1d21SSam Leffler 		44,		/* IFM_IEEE80211_DS22 */
8151a1e1d21SSam Leffler 		12,		/* IFM_IEEE80211_OFDM6 */
8161a1e1d21SSam Leffler 		18,		/* IFM_IEEE80211_OFDM9 */
8171a1e1d21SSam Leffler 		24,		/* IFM_IEEE80211_OFDM12 */
8181a1e1d21SSam Leffler 		36,		/* IFM_IEEE80211_OFDM18 */
8191a1e1d21SSam Leffler 		48,		/* IFM_IEEE80211_OFDM24 */
8201a1e1d21SSam Leffler 		72,		/* IFM_IEEE80211_OFDM36 */
8211a1e1d21SSam Leffler 		96,		/* IFM_IEEE80211_OFDM48 */
8221a1e1d21SSam Leffler 		108,		/* IFM_IEEE80211_OFDM54 */
8231a1e1d21SSam Leffler 		144,		/* IFM_IEEE80211_OFDM72 */
8241a1e1d21SSam Leffler 	};
8251a1e1d21SSam Leffler 	return IFM_SUBTYPE(mword) < N(ieeerates) ?
8261a1e1d21SSam Leffler 		ieeerates[IFM_SUBTYPE(mword)] : 0;
8271a1e1d21SSam Leffler #undef N
8281a1e1d21SSam Leffler }
8291a1e1d21SSam Leffler 
8301a1e1d21SSam Leffler /*
8311a1e1d21SSam Leffler  * Module glue.
8321a1e1d21SSam Leffler  *
8331a1e1d21SSam Leffler  * NB: the module name is "wlan" for compatibility with NetBSD.
8341a1e1d21SSam Leffler  */
8351a1e1d21SSam Leffler 
8361a1e1d21SSam Leffler static int
8371a1e1d21SSam Leffler ieee80211_modevent(module_t mod, int type, void *unused)
8381a1e1d21SSam Leffler {
8391a1e1d21SSam Leffler 	switch (type) {
8401a1e1d21SSam Leffler 	case MOD_LOAD:
8411a1e1d21SSam Leffler 		if (bootverbose)
8421a1e1d21SSam Leffler 			printf("wlan: <802.11 Link Layer>\n");
8431a1e1d21SSam Leffler 		return 0;
8441a1e1d21SSam Leffler 	case MOD_UNLOAD:
8451a1e1d21SSam Leffler 		return 0;
8461a1e1d21SSam Leffler 	}
8471a1e1d21SSam Leffler 	return EINVAL;
8481a1e1d21SSam Leffler }
8491a1e1d21SSam Leffler 
8501a1e1d21SSam Leffler static moduledata_t ieee80211_mod = {
8511a1e1d21SSam Leffler 	"wlan",
8521a1e1d21SSam Leffler 	ieee80211_modevent,
8531a1e1d21SSam Leffler 	0
8541a1e1d21SSam Leffler };
8551a1e1d21SSam Leffler DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
8561a1e1d21SSam Leffler MODULE_VERSION(wlan, 1);
8571a1e1d21SSam Leffler MODULE_DEPEND(wlan, rc4, 1, 1, 1);
858