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