xref: /freebsd/sys/net80211/ieee80211.c (revision ba99a9b120451a33e24ff2853e7e0235c4fb270d)
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>
63ba99a9b1SAndre Oppermann #include <net/route.h>
641a1e1d21SSam Leffler 
651a1e1d21SSam Leffler #include <net80211/ieee80211_var.h>
661a1e1d21SSam Leffler 
671a1e1d21SSam Leffler #include <net/bpf.h>
681a1e1d21SSam Leffler 
691a1e1d21SSam Leffler #ifdef INET
701a1e1d21SSam Leffler #include <netinet/in.h>
711a1e1d21SSam Leffler #include <netinet/if_ether.h>
721a1e1d21SSam Leffler #endif
731a1e1d21SSam Leffler 
741a1e1d21SSam Leffler #ifdef IEEE80211_DEBUG
751a1e1d21SSam Leffler int	ieee80211_debug = 0;
761a1e1d21SSam Leffler SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug,
771a1e1d21SSam Leffler 	    0, "IEEE 802.11 media debugging printfs");
781a1e1d21SSam Leffler #endif
791a1e1d21SSam Leffler 
801a1e1d21SSam Leffler static void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
811a1e1d21SSam Leffler 		enum ieee80211_phymode);
821a1e1d21SSam Leffler 
831a1e1d21SSam Leffler static const char *ieee80211_phymode_name[] = {
841a1e1d21SSam Leffler 	"auto",		/* IEEE80211_MODE_AUTO */
851a1e1d21SSam Leffler 	"11a",		/* IEEE80211_MODE_11A */
861a1e1d21SSam Leffler 	"11b",		/* IEEE80211_MODE_11B */
871a1e1d21SSam Leffler 	"11g",		/* IEEE80211_MODE_11G */
884844aa7dSAtsushi Onoe 	"FH",		/* IEEE80211_MODE_FH */
891a1e1d21SSam Leffler 	"turbo",	/* IEEE80211_MODE_TURBO */
901a1e1d21SSam Leffler };
911a1e1d21SSam Leffler 
921a1e1d21SSam Leffler void
931a1e1d21SSam Leffler ieee80211_ifattach(struct ifnet *ifp)
941a1e1d21SSam Leffler {
951a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
961a1e1d21SSam Leffler 	struct ieee80211_channel *c;
971a1e1d21SSam Leffler 	int i;
981a1e1d21SSam Leffler 
991a1e1d21SSam Leffler 	ether_ifattach(ifp, ic->ic_myaddr);
1001a1e1d21SSam Leffler 	bpfattach2(ifp, DLT_IEEE802_11,
1011a1e1d21SSam Leffler 	    sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
1021a1e1d21SSam Leffler 	ieee80211_crypto_attach(ifp);
1031a1e1d21SSam Leffler 
1041a1e1d21SSam Leffler 	/*
1051a1e1d21SSam Leffler 	 * Fill in 802.11 available channel set, mark
1061a1e1d21SSam Leffler 	 * all available channels as active, and pick
1071a1e1d21SSam Leffler 	 * a default channel if not already specified.
1081a1e1d21SSam Leffler 	 */
1091a1e1d21SSam Leffler 	memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
1101a1e1d21SSam Leffler 	ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO;
1111a1e1d21SSam Leffler 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1121a1e1d21SSam Leffler 		c = &ic->ic_channels[i];
1131a1e1d21SSam Leffler 		if (c->ic_flags) {
1141a1e1d21SSam Leffler 			/*
1151a1e1d21SSam Leffler 			 * Verify driver passed us valid data.
1161a1e1d21SSam Leffler 			 */
1171a1e1d21SSam Leffler 			if (i != ieee80211_chan2ieee(ic, c)) {
1181a1e1d21SSam Leffler 				if_printf(ifp, "bad channel ignored; "
1191a1e1d21SSam Leffler 					"freq %u flags %x number %u\n",
1201a1e1d21SSam Leffler 					c->ic_freq, c->ic_flags, i);
1211a1e1d21SSam Leffler 				c->ic_flags = 0;	/* NB: remove */
1221a1e1d21SSam Leffler 				continue;
1231a1e1d21SSam Leffler 			}
1241a1e1d21SSam Leffler 			setbit(ic->ic_chan_avail, i);
1251a1e1d21SSam Leffler 			/*
1261a1e1d21SSam Leffler 			 * Identify mode capabilities.
1271a1e1d21SSam Leffler 			 */
1281a1e1d21SSam Leffler 			if (IEEE80211_IS_CHAN_A(c))
1291a1e1d21SSam Leffler 				ic->ic_modecaps |= 1<<IEEE80211_MODE_11A;
1301a1e1d21SSam Leffler 			if (IEEE80211_IS_CHAN_B(c))
1311a1e1d21SSam Leffler 				ic->ic_modecaps |= 1<<IEEE80211_MODE_11B;
1321a1e1d21SSam Leffler 			if (IEEE80211_IS_CHAN_PUREG(c))
1331a1e1d21SSam Leffler 				ic->ic_modecaps |= 1<<IEEE80211_MODE_11G;
1344844aa7dSAtsushi Onoe 			if (IEEE80211_IS_CHAN_FHSS(c))
1354844aa7dSAtsushi Onoe 				ic->ic_modecaps |= 1<<IEEE80211_MODE_FH;
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;
14363beab83SSam Leffler 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
1441a1e1d21SSam Leffler 
1451a1e1d21SSam Leffler 	(void) ieee80211_setmode(ic, ic->ic_curmode);
1461a1e1d21SSam Leffler 
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;
2048be0d570SSam Leffler 	else if (c != NULL) {
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 */
2088be0d570SSam Leffler 	} else {
2098be0d570SSam Leffler 		if_printf(&ic->ic_if, "invalid channel (NULL)\n");
2108be0d570SSam Leffler 		return 0;		/* XXX */
2111a1e1d21SSam Leffler 	}
2121a1e1d21SSam Leffler }
2131a1e1d21SSam Leffler 
2141a1e1d21SSam Leffler /*
2151a1e1d21SSam Leffler  * Convert IEEE channel number to MHz frequency.
2161a1e1d21SSam Leffler  */
2171a1e1d21SSam Leffler u_int
2181a1e1d21SSam Leffler ieee80211_ieee2mhz(u_int chan, u_int flags)
2191a1e1d21SSam Leffler {
2201a1e1d21SSam Leffler 	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
2211a1e1d21SSam Leffler 		if (chan == 14)
2221a1e1d21SSam Leffler 			return 2484;
2231a1e1d21SSam Leffler 		if (chan < 14)
2241a1e1d21SSam Leffler 			return 2407 + chan*5;
2251a1e1d21SSam Leffler 		else
2261a1e1d21SSam Leffler 			return 2512 + ((chan-15)*20);
2271a1e1d21SSam Leffler 	} else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */
2281a1e1d21SSam Leffler 		return 5000 + (chan*5);
2291a1e1d21SSam Leffler 	} else {				/* either, guess */
2301a1e1d21SSam Leffler 		if (chan == 14)
2311a1e1d21SSam Leffler 			return 2484;
2321a1e1d21SSam Leffler 		if (chan < 14)			/* 0-13 */
2331a1e1d21SSam Leffler 			return 2407 + chan*5;
2341a1e1d21SSam Leffler 		if (chan < 27)			/* 15-26 */
2351a1e1d21SSam Leffler 			return 2512 + ((chan-15)*20);
2361a1e1d21SSam Leffler 		return 5000 + (chan*5);
2371a1e1d21SSam Leffler 	}
2381a1e1d21SSam Leffler }
2391a1e1d21SSam Leffler 
2401a1e1d21SSam Leffler /*
2411a1e1d21SSam Leffler  * Setup the media data structures according to the channel and
2421a1e1d21SSam Leffler  * rate tables.  This must be called by the driver after
2431a1e1d21SSam Leffler  * ieee80211_attach and before most anything else.
2441a1e1d21SSam Leffler  */
2451a1e1d21SSam Leffler void
2461a1e1d21SSam Leffler ieee80211_media_init(struct ifnet *ifp,
2471a1e1d21SSam Leffler 	ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
2481a1e1d21SSam Leffler {
2491a1e1d21SSam Leffler #define	ADD(_ic, _s, _o) \
2501a1e1d21SSam Leffler 	ifmedia_add(&(_ic)->ic_media, \
2511a1e1d21SSam Leffler 		IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
2521a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
2531a1e1d21SSam Leffler 	struct ifmediareq imr;
2541a1e1d21SSam Leffler 	int i, j, mode, rate, maxrate, mword, mopt, r;
2551a1e1d21SSam Leffler 	struct ieee80211_rateset *rs;
2561a1e1d21SSam Leffler 	struct ieee80211_rateset allrates;
2571a1e1d21SSam Leffler 
2581a1e1d21SSam Leffler 	/*
2592692bb26SSam Leffler 	 * Do late attach work that must wait for any subclass
2602692bb26SSam Leffler 	 * (i.e. driver) work such as overriding methods.
2612692bb26SSam Leffler 	 */
2622692bb26SSam Leffler 	ieee80211_node_lateattach(ifp);
2632692bb26SSam Leffler 
2642692bb26SSam Leffler 	/*
2651a1e1d21SSam Leffler 	 * Fill in media characteristics.
2661a1e1d21SSam Leffler 	 */
2671a1e1d21SSam Leffler 	ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
2681a1e1d21SSam Leffler 	maxrate = 0;
2691a1e1d21SSam Leffler 	memset(&allrates, 0, sizeof(allrates));
2701a1e1d21SSam Leffler 	for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
2711a1e1d21SSam Leffler 		static const u_int mopts[] = {
2721a1e1d21SSam Leffler 			IFM_AUTO,
2734844aa7dSAtsushi Onoe 			IFM_IEEE80211_11A,
2744844aa7dSAtsushi Onoe 			IFM_IEEE80211_11B,
2754844aa7dSAtsushi Onoe 			IFM_IEEE80211_11G,
2764844aa7dSAtsushi Onoe 			IFM_IEEE80211_FH,
2774844aa7dSAtsushi Onoe 			IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
2781a1e1d21SSam Leffler 		};
2791a1e1d21SSam Leffler 		if ((ic->ic_modecaps & (1<<mode)) == 0)
2801a1e1d21SSam Leffler 			continue;
2811a1e1d21SSam Leffler 		mopt = mopts[mode];
2821a1e1d21SSam Leffler 		ADD(ic, IFM_AUTO, mopt);	/* e.g. 11a auto */
2831a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_IBSS)
2841a1e1d21SSam Leffler 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC);
2851a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_HOSTAP)
2861a1e1d21SSam Leffler 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP);
2871a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_AHDEMO)
2881a1e1d21SSam Leffler 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
2892bbe529dSSam Leffler 		if (ic->ic_caps & IEEE80211_C_MONITOR)
2902bbe529dSSam Leffler 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
2911a1e1d21SSam Leffler 		if (mode == IEEE80211_MODE_AUTO)
2921a1e1d21SSam Leffler 			continue;
2931a1e1d21SSam Leffler 		if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
2941a1e1d21SSam Leffler 		rs = &ic->ic_sup_rates[mode];
2951a1e1d21SSam Leffler 		for (i = 0; i < rs->rs_nrates; i++) {
2961a1e1d21SSam Leffler 			rate = rs->rs_rates[i];
2971a1e1d21SSam Leffler 			mword = ieee80211_rate2media(ic, rate, mode);
2981a1e1d21SSam Leffler 			if (mword == 0)
2991a1e1d21SSam Leffler 				continue;
3001a1e1d21SSam Leffler 			printf("%s%d%sMbps", (i != 0 ? " " : ""),
3011a1e1d21SSam Leffler 			    (rate & IEEE80211_RATE_VAL) / 2,
3021a1e1d21SSam Leffler 			    ((rate & 0x1) != 0 ? ".5" : ""));
3031a1e1d21SSam Leffler 			ADD(ic, mword, mopt);
3041a1e1d21SSam Leffler 			if (ic->ic_caps & IEEE80211_C_IBSS)
3051a1e1d21SSam Leffler 				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
3061a1e1d21SSam Leffler 			if (ic->ic_caps & IEEE80211_C_HOSTAP)
3071a1e1d21SSam Leffler 				ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
3081a1e1d21SSam Leffler 			if (ic->ic_caps & IEEE80211_C_AHDEMO)
3091a1e1d21SSam Leffler 				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
3102bbe529dSSam Leffler 			if (ic->ic_caps & IEEE80211_C_MONITOR)
3112bbe529dSSam Leffler 				ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
3121a1e1d21SSam Leffler 			/*
3131a1e1d21SSam Leffler 			 * Add rate to the collection of all rates.
3141a1e1d21SSam Leffler 			 */
3151a1e1d21SSam Leffler 			r = rate & IEEE80211_RATE_VAL;
3161a1e1d21SSam Leffler 			for (j = 0; j < allrates.rs_nrates; j++)
3171a1e1d21SSam Leffler 				if (allrates.rs_rates[j] == r)
3181a1e1d21SSam Leffler 					break;
3191a1e1d21SSam Leffler 			if (j == allrates.rs_nrates) {
3201a1e1d21SSam Leffler 				/* unique, add to the set */
3211a1e1d21SSam Leffler 				allrates.rs_rates[j] = r;
3221a1e1d21SSam Leffler 				allrates.rs_nrates++;
3231a1e1d21SSam Leffler 			}
3241a1e1d21SSam Leffler 			rate = (rate & IEEE80211_RATE_VAL) / 2;
3251a1e1d21SSam Leffler 			if (rate > maxrate)
3261a1e1d21SSam Leffler 				maxrate = rate;
3271a1e1d21SSam Leffler 		}
3281a1e1d21SSam Leffler 		printf("\n");
3291a1e1d21SSam Leffler 	}
3301a1e1d21SSam Leffler 	for (i = 0; i < allrates.rs_nrates; i++) {
3311a1e1d21SSam Leffler 		mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
3321a1e1d21SSam Leffler 				IEEE80211_MODE_AUTO);
3331a1e1d21SSam Leffler 		if (mword == 0)
3341a1e1d21SSam Leffler 			continue;
3351a1e1d21SSam Leffler 		mword = IFM_SUBTYPE(mword);	/* remove media options */
3361a1e1d21SSam Leffler 		ADD(ic, mword, 0);
3371a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_IBSS)
3381a1e1d21SSam Leffler 			ADD(ic, mword, IFM_IEEE80211_ADHOC);
3391a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_HOSTAP)
3401a1e1d21SSam Leffler 			ADD(ic, mword, IFM_IEEE80211_HOSTAP);
3411a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_AHDEMO)
3421a1e1d21SSam Leffler 			ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
3432bbe529dSSam Leffler 		if (ic->ic_caps & IEEE80211_C_MONITOR)
3442bbe529dSSam Leffler 			ADD(ic, mword, IFM_IEEE80211_MONITOR);
3451a1e1d21SSam Leffler 	}
3461a1e1d21SSam Leffler 	ieee80211_media_status(ifp, &imr);
3471a1e1d21SSam Leffler 	ifmedia_set(&ic->ic_media, imr.ifm_active);
3481a1e1d21SSam Leffler 
3491a1e1d21SSam Leffler 	if (maxrate)
3501a1e1d21SSam Leffler 		ifp->if_baudrate = IF_Mbps(maxrate);
3511a1e1d21SSam Leffler #undef ADD
3521a1e1d21SSam Leffler }
3531a1e1d21SSam Leffler 
3541a1e1d21SSam Leffler static int
3551a1e1d21SSam Leffler findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
3561a1e1d21SSam Leffler {
3571a1e1d21SSam Leffler #define	IEEERATE(_ic,_m,_i) \
3581a1e1d21SSam Leffler 	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
3591a1e1d21SSam Leffler 	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
3601a1e1d21SSam Leffler 	for (i = 0; i < nrates; i++)
3611a1e1d21SSam Leffler 		if (IEEERATE(ic, mode, i) == rate)
3621a1e1d21SSam Leffler 			return i;
3631a1e1d21SSam Leffler 	return -1;
3641a1e1d21SSam Leffler #undef IEEERATE
3651a1e1d21SSam Leffler }
3661a1e1d21SSam Leffler 
3671a1e1d21SSam Leffler /*
3681a1e1d21SSam Leffler  * Handle a media change request.
3691a1e1d21SSam Leffler  */
3701a1e1d21SSam Leffler int
3711a1e1d21SSam Leffler ieee80211_media_change(struct ifnet *ifp)
3721a1e1d21SSam Leffler {
3731a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
3741a1e1d21SSam Leffler 	struct ifmedia_entry *ime;
3751a1e1d21SSam Leffler 	enum ieee80211_opmode newopmode;
3761a1e1d21SSam Leffler 	enum ieee80211_phymode newphymode;
3771a1e1d21SSam Leffler 	int i, j, newrate, error = 0;
3781a1e1d21SSam Leffler 
3791a1e1d21SSam Leffler 	ime = ic->ic_media.ifm_cur;
3801a1e1d21SSam Leffler 	/*
3811a1e1d21SSam Leffler 	 * First, identify the phy mode.
3821a1e1d21SSam Leffler 	 */
3831a1e1d21SSam Leffler 	switch (IFM_MODE(ime->ifm_media)) {
3841a1e1d21SSam Leffler 	case IFM_IEEE80211_11A:
3851a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_11A;
3861a1e1d21SSam Leffler 		break;
3871a1e1d21SSam Leffler 	case IFM_IEEE80211_11B:
3881a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_11B;
3891a1e1d21SSam Leffler 		break;
3901a1e1d21SSam Leffler 	case IFM_IEEE80211_11G:
3911a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_11G;
3921a1e1d21SSam Leffler 		break;
3934844aa7dSAtsushi Onoe 	case IFM_IEEE80211_FH:
3944844aa7dSAtsushi Onoe 		newphymode = IEEE80211_MODE_FH;
3954844aa7dSAtsushi Onoe 		break;
3961a1e1d21SSam Leffler 	case IFM_AUTO:
3971a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_AUTO;
3981a1e1d21SSam Leffler 		break;
3991a1e1d21SSam Leffler 	default:
4001a1e1d21SSam Leffler 		return EINVAL;
4011a1e1d21SSam Leffler 	}
4021a1e1d21SSam Leffler 	/*
4031a1e1d21SSam Leffler 	 * Turbo mode is an ``option''.  Eventually it
4041a1e1d21SSam Leffler 	 * needs to be applied to 11g too.
4051a1e1d21SSam Leffler 	 */
4061a1e1d21SSam Leffler 	if (ime->ifm_media & IFM_IEEE80211_TURBO) {
4071a1e1d21SSam Leffler 		if (newphymode != IEEE80211_MODE_11A)
4081a1e1d21SSam Leffler 			return EINVAL;
4091a1e1d21SSam Leffler 		newphymode = IEEE80211_MODE_TURBO;
4101a1e1d21SSam Leffler 	}
4111a1e1d21SSam Leffler 	/*
4121a1e1d21SSam Leffler 	 * Validate requested mode is available.
4131a1e1d21SSam Leffler 	 */
4141a1e1d21SSam Leffler 	if ((ic->ic_modecaps & (1<<newphymode)) == 0)
4151a1e1d21SSam Leffler 		return EINVAL;
4161a1e1d21SSam Leffler 
4171a1e1d21SSam Leffler 	/*
4181a1e1d21SSam Leffler 	 * Next, the fixed/variable rate.
4191a1e1d21SSam Leffler 	 */
4201a1e1d21SSam Leffler 	i = -1;
4211a1e1d21SSam Leffler 	if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
4221a1e1d21SSam Leffler 		/*
4231a1e1d21SSam Leffler 		 * Convert media subtype to rate.
4241a1e1d21SSam Leffler 		 */
4251a1e1d21SSam Leffler 		newrate = ieee80211_media2rate(ime->ifm_media);
4261a1e1d21SSam Leffler 		if (newrate == 0)
4271a1e1d21SSam Leffler 			return EINVAL;
4281a1e1d21SSam Leffler 		/*
4291a1e1d21SSam Leffler 		 * Check the rate table for the specified/current phy.
4301a1e1d21SSam Leffler 		 */
4311a1e1d21SSam Leffler 		if (newphymode == IEEE80211_MODE_AUTO) {
4321a1e1d21SSam Leffler 			/*
4331a1e1d21SSam Leffler 			 * In autoselect mode search for the rate.
4341a1e1d21SSam Leffler 			 */
4351a1e1d21SSam Leffler 			for (j = IEEE80211_MODE_11A;
4361a1e1d21SSam Leffler 			     j < IEEE80211_MODE_MAX; j++) {
4371a1e1d21SSam Leffler 				if ((ic->ic_modecaps & (1<<j)) == 0)
4381a1e1d21SSam Leffler 					continue;
4391a1e1d21SSam Leffler 				i = findrate(ic, j, newrate);
4401a1e1d21SSam Leffler 				if (i != -1) {
4411a1e1d21SSam Leffler 					/* lock mode too */
4421a1e1d21SSam Leffler 					newphymode = j;
4431a1e1d21SSam Leffler 					break;
4441a1e1d21SSam Leffler 				}
4451a1e1d21SSam Leffler 			}
4461a1e1d21SSam Leffler 		} else {
4471a1e1d21SSam Leffler 			i = findrate(ic, newphymode, newrate);
4481a1e1d21SSam Leffler 		}
4491a1e1d21SSam Leffler 		if (i == -1)			/* mode/rate mismatch */
4501a1e1d21SSam Leffler 			return EINVAL;
4511a1e1d21SSam Leffler 	}
4521a1e1d21SSam Leffler 	/* NB: defer rate setting to later */
4531a1e1d21SSam Leffler 
4541a1e1d21SSam Leffler 	/*
4551a1e1d21SSam Leffler 	 * Deduce new operating mode but don't install it just yet.
4561a1e1d21SSam Leffler 	 */
4571a1e1d21SSam Leffler 	if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
4581a1e1d21SSam Leffler 	    (IFM_IEEE80211_ADHOC|IFM_FLAG0))
4591a1e1d21SSam Leffler 		newopmode = IEEE80211_M_AHDEMO;
4601a1e1d21SSam Leffler 	else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
4611a1e1d21SSam Leffler 		newopmode = IEEE80211_M_HOSTAP;
4621a1e1d21SSam Leffler 	else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
4631a1e1d21SSam Leffler 		newopmode = IEEE80211_M_IBSS;
4642bbe529dSSam Leffler 	else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
4652bbe529dSSam Leffler 		newopmode = IEEE80211_M_MONITOR;
4661a1e1d21SSam Leffler 	else
4671a1e1d21SSam Leffler 		newopmode = IEEE80211_M_STA;
4681a1e1d21SSam Leffler 
4691a1e1d21SSam Leffler 	/*
4701a1e1d21SSam Leffler 	 * Autoselect doesn't make sense when operating as an AP.
4711a1e1d21SSam Leffler 	 * If no phy mode has been selected, pick one and lock it
4721a1e1d21SSam Leffler 	 * down so rate tables can be used in forming beacon frames
4731a1e1d21SSam Leffler 	 * and the like.
4741a1e1d21SSam Leffler 	 */
4751a1e1d21SSam Leffler 	if (newopmode == IEEE80211_M_HOSTAP &&
4761a1e1d21SSam Leffler 	    newphymode == IEEE80211_MODE_AUTO) {
4771a1e1d21SSam Leffler 		for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++)
4781a1e1d21SSam Leffler 			if (ic->ic_modecaps & (1<<j)) {
4791a1e1d21SSam Leffler 				newphymode = j;
4801a1e1d21SSam Leffler 				break;
4811a1e1d21SSam Leffler 			}
4821a1e1d21SSam Leffler 	}
4831a1e1d21SSam Leffler 
4841a1e1d21SSam Leffler 	/*
4851a1e1d21SSam Leffler 	 * Handle phy mode change.
4861a1e1d21SSam Leffler 	 */
4871a1e1d21SSam Leffler 	if (ic->ic_curmode != newphymode) {		/* change phy mode */
4881a1e1d21SSam Leffler 		error = ieee80211_setmode(ic, newphymode);
4891a1e1d21SSam Leffler 		if (error != 0)
4901a1e1d21SSam Leffler 			return error;
4911a1e1d21SSam Leffler 		error = ENETRESET;
4921a1e1d21SSam Leffler 	}
4931a1e1d21SSam Leffler 
4941a1e1d21SSam Leffler 	/*
4951a1e1d21SSam Leffler 	 * Committed to changes, install the rate setting.
4961a1e1d21SSam Leffler 	 */
4971a1e1d21SSam Leffler 	if (ic->ic_fixed_rate != i) {
4981a1e1d21SSam Leffler 		ic->ic_fixed_rate = i;			/* set fixed tx rate */
4991a1e1d21SSam Leffler 		error = ENETRESET;
5001a1e1d21SSam Leffler 	}
5011a1e1d21SSam Leffler 
5021a1e1d21SSam Leffler 	/*
5031a1e1d21SSam Leffler 	 * Handle operating mode change.
5041a1e1d21SSam Leffler 	 */
5051a1e1d21SSam Leffler 	if (ic->ic_opmode != newopmode) {
5061a1e1d21SSam Leffler 		ic->ic_opmode = newopmode;
5071a1e1d21SSam Leffler 		switch (newopmode) {
5081a1e1d21SSam Leffler 		case IEEE80211_M_AHDEMO:
5091a1e1d21SSam Leffler 		case IEEE80211_M_HOSTAP:
5101a1e1d21SSam Leffler 		case IEEE80211_M_STA:
5112bbe529dSSam Leffler 		case IEEE80211_M_MONITOR:
5121a1e1d21SSam Leffler 			ic->ic_flags &= ~IEEE80211_F_IBSSON;
5131a1e1d21SSam Leffler 			break;
5141a1e1d21SSam Leffler 		case IEEE80211_M_IBSS:
5151a1e1d21SSam Leffler 			ic->ic_flags |= IEEE80211_F_IBSSON;
5161a1e1d21SSam Leffler #ifdef notdef
5171a1e1d21SSam Leffler 			if (ic->ic_curmode == IEEE80211_MODE_11G)
5181a1e1d21SSam Leffler 				ieee80211_set11gbasicrates(
5191a1e1d21SSam Leffler 					&ic->ic_suprates[newphymode],
5201a1e1d21SSam Leffler 					IEEE80211_MODE_11B);
5211a1e1d21SSam Leffler #endif
5221a1e1d21SSam Leffler 			break;
5231a1e1d21SSam Leffler 		}
5241a1e1d21SSam Leffler 		error = ENETRESET;
5251a1e1d21SSam Leffler 	}
5261a1e1d21SSam Leffler #ifdef notdef
5271a1e1d21SSam Leffler 	if (error == 0)
5281a1e1d21SSam Leffler 		ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
5291a1e1d21SSam Leffler #endif
5301a1e1d21SSam Leffler 	return error;
5311a1e1d21SSam Leffler }
5321a1e1d21SSam Leffler 
5331a1e1d21SSam Leffler void
5341a1e1d21SSam Leffler ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
5351a1e1d21SSam Leffler {
5361a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
5371a1e1d21SSam Leffler 	struct ieee80211_node *ni = NULL;
538ba99a9b1SAndre Oppermann 	int old_status = imr->ifm_status;
5391a1e1d21SSam Leffler 
5401a1e1d21SSam Leffler 	imr->ifm_status = IFM_AVALID;
5411a1e1d21SSam Leffler 	imr->ifm_active = IFM_IEEE80211;
542ba99a9b1SAndre Oppermann 	if (ic->ic_state == IEEE80211_S_RUN) {
5431a1e1d21SSam Leffler 		imr->ifm_status |= IFM_ACTIVE;
544ba99a9b1SAndre Oppermann 		ifp->if_link_state = LINK_STATE_UP;
545ba99a9b1SAndre Oppermann 	} else
546ba99a9b1SAndre Oppermann 		ifp->if_link_state = LINK_STATE_DOWN;
5471a1e1d21SSam Leffler 	imr->ifm_active |= IFM_AUTO;
5481a1e1d21SSam Leffler 	switch (ic->ic_opmode) {
5491a1e1d21SSam Leffler 	case IEEE80211_M_STA:
5501a1e1d21SSam Leffler 		ni = ic->ic_bss;
5511a1e1d21SSam Leffler 		/* calculate rate subtype */
5521a1e1d21SSam Leffler 		imr->ifm_active |= ieee80211_rate2media(ic,
5531a1e1d21SSam Leffler 			ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
5541a1e1d21SSam Leffler 		break;
5551a1e1d21SSam Leffler 	case IEEE80211_M_IBSS:
5561a1e1d21SSam Leffler 		imr->ifm_active |= IFM_IEEE80211_ADHOC;
5571a1e1d21SSam Leffler 		break;
5581a1e1d21SSam Leffler 	case IEEE80211_M_AHDEMO:
5591a1e1d21SSam Leffler 		/* should not come here */
5601a1e1d21SSam Leffler 		break;
5611a1e1d21SSam Leffler 	case IEEE80211_M_HOSTAP:
5621a1e1d21SSam Leffler 		imr->ifm_active |= IFM_IEEE80211_HOSTAP;
5631a1e1d21SSam Leffler 		break;
5642bbe529dSSam Leffler 	case IEEE80211_M_MONITOR:
5652bbe529dSSam Leffler 		imr->ifm_active |= IFM_IEEE80211_MONITOR;
5662bbe529dSSam Leffler 		break;
5671a1e1d21SSam Leffler 	}
5681a1e1d21SSam Leffler 	switch (ic->ic_curmode) {
5691a1e1d21SSam Leffler 	case IEEE80211_MODE_11A:
5704844aa7dSAtsushi Onoe 		imr->ifm_active |= IFM_IEEE80211_11A;
5711a1e1d21SSam Leffler 		break;
5721a1e1d21SSam Leffler 	case IEEE80211_MODE_11B:
5734844aa7dSAtsushi Onoe 		imr->ifm_active |= IFM_IEEE80211_11B;
5741a1e1d21SSam Leffler 		break;
5751a1e1d21SSam Leffler 	case IEEE80211_MODE_11G:
5764844aa7dSAtsushi Onoe 		imr->ifm_active |= IFM_IEEE80211_11G;
5774844aa7dSAtsushi Onoe 		break;
5784844aa7dSAtsushi Onoe 	case IEEE80211_MODE_FH:
5794844aa7dSAtsushi Onoe 		imr->ifm_active |= IFM_IEEE80211_FH;
5801a1e1d21SSam Leffler 		break;
5811a1e1d21SSam Leffler 	case IEEE80211_MODE_TURBO:
5824844aa7dSAtsushi Onoe 		imr->ifm_active |= IFM_IEEE80211_11A
5831a1e1d21SSam Leffler 				|  IFM_IEEE80211_TURBO;
5841a1e1d21SSam Leffler 		break;
5851a1e1d21SSam Leffler 	}
586ba99a9b1SAndre Oppermann 
587ba99a9b1SAndre Oppermann 	/* Notify that the link state has changed. */
588ba99a9b1SAndre Oppermann 	if (imr->ifm_status != old_status)
589ba99a9b1SAndre Oppermann 		rt_ifmsg(ifp);
5901a1e1d21SSam Leffler }
5911a1e1d21SSam Leffler 
5921a1e1d21SSam Leffler void
5931a1e1d21SSam Leffler ieee80211_watchdog(struct ifnet *ifp)
5941a1e1d21SSam Leffler {
5951a1e1d21SSam Leffler 	struct ieee80211com *ic = (void *)ifp;
5961a1e1d21SSam Leffler 
5971a1e1d21SSam Leffler 	if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
598a11c9a5cSSam Leffler 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
5991a1e1d21SSam Leffler 	if (ic->ic_inact_timer && --ic->ic_inact_timer == 0)
6001a1e1d21SSam Leffler 		ieee80211_timeout_nodes(ic);
6011a1e1d21SSam Leffler 
6021a1e1d21SSam Leffler 	if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0)
6031a1e1d21SSam Leffler 		ifp->if_timer = 1;
6041a1e1d21SSam Leffler }
6051a1e1d21SSam Leffler 
6061a1e1d21SSam Leffler /*
6071a1e1d21SSam Leffler  * Mark the basic rates for the 11g rate table based on the
6081a1e1d21SSam Leffler  * operating mode.  For real 11g we mark all the 11b rates
6091a1e1d21SSam Leffler  * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
6101a1e1d21SSam Leffler  * 11b rates.  There's also a pseudo 11a-mode used to mark only
6111a1e1d21SSam Leffler  * the basic OFDM rates.
6121a1e1d21SSam Leffler  */
6131a1e1d21SSam Leffler static void
6141a1e1d21SSam Leffler ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
6151a1e1d21SSam Leffler {
6161a1e1d21SSam Leffler 	static const struct ieee80211_rateset basic[] = {
6171a1e1d21SSam Leffler 	    { 3, { 12, 24, 48 } },		/* IEEE80211_MODE_11A */
6181a1e1d21SSam Leffler 	    { 4, { 2, 4, 11, 22 } },		/* IEEE80211_MODE_11B */
6191a1e1d21SSam Leffler 	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */
6204844aa7dSAtsushi Onoe 	    { 0 },				/* IEEE80211_MODE_FH */
6211a1e1d21SSam Leffler 	    { 0 },				/* IEEE80211_MODE_TURBO	*/
6221a1e1d21SSam Leffler 	};
6231a1e1d21SSam Leffler 	int i, j;
6241a1e1d21SSam Leffler 
6251a1e1d21SSam Leffler 	for (i = 0; i < rs->rs_nrates; i++) {
6261a1e1d21SSam Leffler 		rs->rs_rates[i] &= IEEE80211_RATE_VAL;
6271a1e1d21SSam Leffler 		for (j = 0; j < basic[mode].rs_nrates; j++)
6281a1e1d21SSam Leffler 			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
6291a1e1d21SSam Leffler 				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
6301a1e1d21SSam Leffler 				break;
6311a1e1d21SSam Leffler 			}
6321a1e1d21SSam Leffler 	}
6331a1e1d21SSam Leffler }
6341a1e1d21SSam Leffler 
6351a1e1d21SSam Leffler /*
6361a1e1d21SSam Leffler  * Set the current phy mode and recalculate the active channel
6371a1e1d21SSam Leffler  * set based on the available channels for this mode.  Also
6381a1e1d21SSam Leffler  * select a new default/current channel if the current one is
6391a1e1d21SSam Leffler  * inappropriate for this mode.
6401a1e1d21SSam Leffler  */
6411a1e1d21SSam Leffler int
6421a1e1d21SSam Leffler ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
6431a1e1d21SSam Leffler {
6441a1e1d21SSam Leffler #define	N(a)	(sizeof(a) / sizeof(a[0]))
6451a1e1d21SSam Leffler 	static const u_int chanflags[] = {
6461a1e1d21SSam Leffler 		0,			/* IEEE80211_MODE_AUTO */
6471a1e1d21SSam Leffler 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
6481a1e1d21SSam Leffler 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
6491a1e1d21SSam Leffler 		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
6504844aa7dSAtsushi Onoe 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
6511a1e1d21SSam Leffler 		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO	*/
6521a1e1d21SSam Leffler 	};
6531a1e1d21SSam Leffler 	struct ieee80211_channel *c;
6541a1e1d21SSam Leffler 	u_int modeflags;
6551a1e1d21SSam Leffler 	int i;
6561a1e1d21SSam Leffler 
6571a1e1d21SSam Leffler 	/* validate new mode */
6581a1e1d21SSam Leffler 	if ((ic->ic_modecaps & (1<<mode)) == 0) {
6591a1e1d21SSam Leffler 		IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n",
6601a1e1d21SSam Leffler 			__func__, mode, ic->ic_modecaps));
6611a1e1d21SSam Leffler 		return EINVAL;
6621a1e1d21SSam Leffler 	}
6631a1e1d21SSam Leffler 
6641a1e1d21SSam Leffler 	/*
6651a1e1d21SSam Leffler 	 * Verify at least one channel is present in the available
6661a1e1d21SSam Leffler 	 * channel list before committing to the new mode.
6671a1e1d21SSam Leffler 	 */
66863beab83SSam Leffler 	KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
6691a1e1d21SSam Leffler 	modeflags = chanflags[mode];
6701a1e1d21SSam Leffler 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
6711a1e1d21SSam Leffler 		c = &ic->ic_channels[i];
6721a1e1d21SSam Leffler 		if (mode == IEEE80211_MODE_AUTO) {
6731a1e1d21SSam Leffler 			/* ignore turbo channels for autoselect */
6741a1e1d21SSam Leffler 			if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
6751a1e1d21SSam Leffler 				break;
6761a1e1d21SSam Leffler 		} else {
6771a1e1d21SSam Leffler 			if ((c->ic_flags & modeflags) == modeflags)
6781a1e1d21SSam Leffler 				break;
6791a1e1d21SSam Leffler 		}
6801a1e1d21SSam Leffler 	}
6811a1e1d21SSam Leffler 	if (i > IEEE80211_CHAN_MAX) {
6821a1e1d21SSam Leffler 		IEEE80211_DPRINTF(("%s: no channels found for mode %u\n",
6831a1e1d21SSam Leffler 			__func__, mode));
6841a1e1d21SSam Leffler 		return EINVAL;
6851a1e1d21SSam Leffler 	}
6861a1e1d21SSam Leffler 
6871a1e1d21SSam Leffler 	/*
6881a1e1d21SSam Leffler 	 * Calculate the active channel set.
6891a1e1d21SSam Leffler 	 */
6901a1e1d21SSam Leffler 	memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active));
6911a1e1d21SSam Leffler 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
6921a1e1d21SSam Leffler 		c = &ic->ic_channels[i];
6931a1e1d21SSam Leffler 		if (mode == IEEE80211_MODE_AUTO) {
6941a1e1d21SSam Leffler 			/* take anything but pure turbo channels */
6951a1e1d21SSam Leffler 			if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
6961a1e1d21SSam Leffler 				setbit(ic->ic_chan_active, i);
6971a1e1d21SSam Leffler 		} else {
6981a1e1d21SSam Leffler 			if ((c->ic_flags & modeflags) == modeflags)
6991a1e1d21SSam Leffler 				setbit(ic->ic_chan_active, i);
7001a1e1d21SSam Leffler 		}
7011a1e1d21SSam Leffler 	}
7021a1e1d21SSam Leffler 	/*
7031a1e1d21SSam Leffler 	 * If no current/default channel is setup or the current
7041a1e1d21SSam Leffler 	 * channel is wrong for the mode then pick the first
7051a1e1d21SSam Leffler 	 * available channel from the active list.  This is likely
7061a1e1d21SSam Leffler 	 * not the right one.
7071a1e1d21SSam Leffler 	 */
7081a1e1d21SSam Leffler 	if (ic->ic_ibss_chan == NULL ||
7091a1e1d21SSam Leffler 	    isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
7101a1e1d21SSam Leffler 		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
7111a1e1d21SSam Leffler 			if (isset(ic->ic_chan_active, i)) {
7121a1e1d21SSam Leffler 				ic->ic_ibss_chan = &ic->ic_channels[i];
7131a1e1d21SSam Leffler 				break;
7141a1e1d21SSam Leffler 			}
7151a1e1d21SSam Leffler 	}
7161a1e1d21SSam Leffler 
7171a1e1d21SSam Leffler 	/*
7181a1e1d21SSam Leffler 	 * Set/reset state flags that influence beacon contents, etc.
7191a1e1d21SSam Leffler 	 *
7201a1e1d21SSam Leffler 	 * XXX what if we have stations already associated???
7211a1e1d21SSam Leffler 	 * XXX probably not right for autoselect?
7221a1e1d21SSam Leffler 	 */
723fba3db9fSSam Leffler 	if (ic->ic_caps & IEEE80211_C_SHPREAMBLE)
724fba3db9fSSam Leffler 		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
7251a1e1d21SSam Leffler 	if (mode == IEEE80211_MODE_11G) {
7261a1e1d21SSam Leffler 		if (ic->ic_caps & IEEE80211_C_SHSLOT)
7271a1e1d21SSam Leffler 			ic->ic_flags |= IEEE80211_F_SHSLOT;
7281a1e1d21SSam Leffler 		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
7291a1e1d21SSam Leffler 			IEEE80211_MODE_11G);
7301a1e1d21SSam Leffler 	} else {
731fba3db9fSSam Leffler 		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
7321a1e1d21SSam Leffler 	}
7331a1e1d21SSam Leffler 
7341a1e1d21SSam Leffler 	ic->ic_curmode = mode;
7351a1e1d21SSam Leffler 	return 0;
7361a1e1d21SSam Leffler #undef N
7371a1e1d21SSam Leffler }
7381a1e1d21SSam Leffler 
7391a1e1d21SSam Leffler /*
7401a1e1d21SSam Leffler  * Return the phy mode for with the specified channel so the
7411a1e1d21SSam Leffler  * caller can select a rate set.  This is problematic and the
7421a1e1d21SSam Leffler  * work here assumes how things work elsewhere in this code.
7431a1e1d21SSam Leffler  */
7441a1e1d21SSam Leffler enum ieee80211_phymode
7451a1e1d21SSam Leffler ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
7461a1e1d21SSam Leffler {
7471a1e1d21SSam Leffler 	/*
7481a1e1d21SSam Leffler 	 * NB: this assumes the channel would not be supplied to us
7491a1e1d21SSam Leffler 	 *     unless it was already compatible with the current mode.
7501a1e1d21SSam Leffler 	 */
7511a1e1d21SSam Leffler 	if (ic->ic_curmode != IEEE80211_MODE_AUTO)
7521a1e1d21SSam Leffler 		return ic->ic_curmode;
7531a1e1d21SSam Leffler 	/*
7541a1e1d21SSam Leffler 	 * In autoselect mode; deduce a mode based on the channel
7551a1e1d21SSam Leffler 	 * characteristics.  We assume that turbo-only channels
7561a1e1d21SSam Leffler 	 * are not considered when the channel set is constructed.
7571a1e1d21SSam Leffler 	 */
7581a1e1d21SSam Leffler 	if (IEEE80211_IS_CHAN_5GHZ(chan))
7591a1e1d21SSam Leffler 		return IEEE80211_MODE_11A;
7604844aa7dSAtsushi Onoe 	else if (IEEE80211_IS_CHAN_FHSS(chan))
7614844aa7dSAtsushi Onoe 		return IEEE80211_MODE_FH;
7621a1e1d21SSam Leffler 	else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN))
7631a1e1d21SSam Leffler 		return IEEE80211_MODE_11G;
7641a1e1d21SSam Leffler 	else
7651a1e1d21SSam Leffler 		return IEEE80211_MODE_11B;
7661a1e1d21SSam Leffler }
7671a1e1d21SSam Leffler 
7681a1e1d21SSam Leffler /*
7691a1e1d21SSam Leffler  * convert IEEE80211 rate value to ifmedia subtype.
7701a1e1d21SSam Leffler  * ieee80211 rate is in unit of 0.5Mbps.
7711a1e1d21SSam Leffler  */
7721a1e1d21SSam Leffler int
7731a1e1d21SSam Leffler ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
7741a1e1d21SSam Leffler {
7751a1e1d21SSam Leffler #define	N(a)	(sizeof(a) / sizeof(a[0]))
7761a1e1d21SSam Leffler 	static const struct {
7771a1e1d21SSam Leffler 		u_int	m;	/* rate + mode */
7781a1e1d21SSam Leffler 		u_int	r;	/* if_media rate */
7791a1e1d21SSam Leffler 	} rates[] = {
7804844aa7dSAtsushi Onoe 		{   2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
7814844aa7dSAtsushi Onoe 		{   4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
7824844aa7dSAtsushi Onoe 		{   2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 },
7834844aa7dSAtsushi Onoe 		{   4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 },
7844844aa7dSAtsushi Onoe 		{  11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 },
7854844aa7dSAtsushi Onoe 		{  22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 },
7864844aa7dSAtsushi Onoe 		{  44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 },
7874844aa7dSAtsushi Onoe 		{  12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 },
7884844aa7dSAtsushi Onoe 		{  18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 },
7894844aa7dSAtsushi Onoe 		{  24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 },
7904844aa7dSAtsushi Onoe 		{  36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 },
7914844aa7dSAtsushi Onoe 		{  48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 },
7924844aa7dSAtsushi Onoe 		{  72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 },
7934844aa7dSAtsushi Onoe 		{  96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 },
7944844aa7dSAtsushi Onoe 		{ 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 },
7954844aa7dSAtsushi Onoe 		{   2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 },
7964844aa7dSAtsushi Onoe 		{   4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 },
7974844aa7dSAtsushi Onoe 		{  11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 },
7984844aa7dSAtsushi Onoe 		{  22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 },
7994844aa7dSAtsushi Onoe 		{  12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 },
8004844aa7dSAtsushi Onoe 		{  18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 },
8014844aa7dSAtsushi Onoe 		{  24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 },
8024844aa7dSAtsushi Onoe 		{  36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 },
8034844aa7dSAtsushi Onoe 		{  48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 },
8044844aa7dSAtsushi Onoe 		{  72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 },
8054844aa7dSAtsushi Onoe 		{  96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 },
8064844aa7dSAtsushi Onoe 		{ 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 },
8071a1e1d21SSam Leffler 		/* NB: OFDM72 doesn't realy exist so we don't handle it */
8081a1e1d21SSam Leffler 	};
8091a1e1d21SSam Leffler 	u_int mask, i;
8101a1e1d21SSam Leffler 
8111a1e1d21SSam Leffler 	mask = rate & IEEE80211_RATE_VAL;
8121a1e1d21SSam Leffler 	switch (mode) {
8131a1e1d21SSam Leffler 	case IEEE80211_MODE_11A:
8141a1e1d21SSam Leffler 	case IEEE80211_MODE_TURBO:
8154844aa7dSAtsushi Onoe 		mask |= IFM_IEEE80211_11A;
8161a1e1d21SSam Leffler 		break;
8171a1e1d21SSam Leffler 	case IEEE80211_MODE_11B:
8184844aa7dSAtsushi Onoe 		mask |= IFM_IEEE80211_11B;
8194844aa7dSAtsushi Onoe 		break;
8204844aa7dSAtsushi Onoe 	case IEEE80211_MODE_FH:
8214844aa7dSAtsushi Onoe 		mask |= IFM_IEEE80211_FH;
8221a1e1d21SSam Leffler 		break;
8231a1e1d21SSam Leffler 	case IEEE80211_MODE_AUTO:
8241a1e1d21SSam Leffler 		/* NB: ic may be NULL for some drivers */
8251a1e1d21SSam Leffler 		if (ic && ic->ic_phytype == IEEE80211_T_FH) {
8264844aa7dSAtsushi Onoe 			mask |= IFM_IEEE80211_FH;
8274844aa7dSAtsushi Onoe 			break;
8281a1e1d21SSam Leffler 		}
8291a1e1d21SSam Leffler 		/* NB: hack, 11g matches both 11b+11a rates */
8301a1e1d21SSam Leffler 		/* fall thru... */
8311a1e1d21SSam Leffler 	case IEEE80211_MODE_11G:
8324844aa7dSAtsushi Onoe 		mask |= IFM_IEEE80211_11G;
8331a1e1d21SSam Leffler 		break;
8341a1e1d21SSam Leffler 	}
8351a1e1d21SSam Leffler 	for (i = 0; i < N(rates); i++)
8361a1e1d21SSam Leffler 		if (rates[i].m == mask)
8371a1e1d21SSam Leffler 			return rates[i].r;
8381a1e1d21SSam Leffler 	return IFM_AUTO;
8391a1e1d21SSam Leffler #undef N
8401a1e1d21SSam Leffler }
8411a1e1d21SSam Leffler 
8421a1e1d21SSam Leffler int
8431a1e1d21SSam Leffler ieee80211_media2rate(int mword)
8441a1e1d21SSam Leffler {
8451a1e1d21SSam Leffler #define	N(a)	(sizeof(a) / sizeof(a[0]))
8461a1e1d21SSam Leffler 	static const int ieeerates[] = {
8471a1e1d21SSam Leffler 		-1,		/* IFM_AUTO */
8481a1e1d21SSam Leffler 		0,		/* IFM_MANUAL */
8491a1e1d21SSam Leffler 		0,		/* IFM_NONE */
8501a1e1d21SSam Leffler 		2,		/* IFM_IEEE80211_FH1 */
8511a1e1d21SSam Leffler 		4,		/* IFM_IEEE80211_FH2 */
8521a1e1d21SSam Leffler 		2,		/* IFM_IEEE80211_DS1 */
8531a1e1d21SSam Leffler 		4,		/* IFM_IEEE80211_DS2 */
8541a1e1d21SSam Leffler 		11,		/* IFM_IEEE80211_DS5 */
8551a1e1d21SSam Leffler 		22,		/* IFM_IEEE80211_DS11 */
8561a1e1d21SSam Leffler 		44,		/* IFM_IEEE80211_DS22 */
8571a1e1d21SSam Leffler 		12,		/* IFM_IEEE80211_OFDM6 */
8581a1e1d21SSam Leffler 		18,		/* IFM_IEEE80211_OFDM9 */
8591a1e1d21SSam Leffler 		24,		/* IFM_IEEE80211_OFDM12 */
8601a1e1d21SSam Leffler 		36,		/* IFM_IEEE80211_OFDM18 */
8611a1e1d21SSam Leffler 		48,		/* IFM_IEEE80211_OFDM24 */
8621a1e1d21SSam Leffler 		72,		/* IFM_IEEE80211_OFDM36 */
8631a1e1d21SSam Leffler 		96,		/* IFM_IEEE80211_OFDM48 */
8641a1e1d21SSam Leffler 		108,		/* IFM_IEEE80211_OFDM54 */
8651a1e1d21SSam Leffler 		144,		/* IFM_IEEE80211_OFDM72 */
8661a1e1d21SSam Leffler 	};
8671a1e1d21SSam Leffler 	return IFM_SUBTYPE(mword) < N(ieeerates) ?
8681a1e1d21SSam Leffler 		ieeerates[IFM_SUBTYPE(mword)] : 0;
8691a1e1d21SSam Leffler #undef N
8701a1e1d21SSam Leffler }
8711a1e1d21SSam Leffler 
8721a1e1d21SSam Leffler /*
8731a1e1d21SSam Leffler  * Module glue.
8741a1e1d21SSam Leffler  *
8751a1e1d21SSam Leffler  * NB: the module name is "wlan" for compatibility with NetBSD.
8761a1e1d21SSam Leffler  */
8771a1e1d21SSam Leffler 
8781a1e1d21SSam Leffler static int
8791a1e1d21SSam Leffler ieee80211_modevent(module_t mod, int type, void *unused)
8801a1e1d21SSam Leffler {
8811a1e1d21SSam Leffler 	switch (type) {
8821a1e1d21SSam Leffler 	case MOD_LOAD:
8831a1e1d21SSam Leffler 		if (bootverbose)
8841a1e1d21SSam Leffler 			printf("wlan: <802.11 Link Layer>\n");
8851a1e1d21SSam Leffler 		return 0;
8861a1e1d21SSam Leffler 	case MOD_UNLOAD:
8871a1e1d21SSam Leffler 		return 0;
8881a1e1d21SSam Leffler 	}
8891a1e1d21SSam Leffler 	return EINVAL;
8901a1e1d21SSam Leffler }
8911a1e1d21SSam Leffler 
8921a1e1d21SSam Leffler static moduledata_t ieee80211_mod = {
8931a1e1d21SSam Leffler 	"wlan",
8941a1e1d21SSam Leffler 	ieee80211_modevent,
8951a1e1d21SSam Leffler 	0
8961a1e1d21SSam Leffler };
8971a1e1d21SSam Leffler DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
8981a1e1d21SSam Leffler MODULE_VERSION(wlan, 1);
8991a1e1d21SSam Leffler MODULE_DEPEND(wlan, rc4, 1, 1, 1);
90045c7aff7SMaxime Henrion MODULE_DEPEND(wlan, ether, 1, 1, 1);
901