xref: /onnv-gate/usr/src/uts/common/io/net80211/net80211.c (revision 10266:bbc5945eddd7)
13147Sxc151355 /*
2*10266SQuaker.Fang@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
33147Sxc151355  * Use is subject to license terms.
43147Sxc151355  */
53147Sxc151355 
63147Sxc151355 /*
73147Sxc151355  * Copyright (c) 2001 Atsushi Onoe
83147Sxc151355  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
93147Sxc151355  * All rights reserved.
103147Sxc151355  *
113147Sxc151355  * Redistribution and use in source and binary forms, with or without
123147Sxc151355  * modification, are permitted provided that the following conditions
133147Sxc151355  * are met:
143147Sxc151355  * 1. Redistributions of source code must retain the above copyright
153147Sxc151355  *    notice, this list of conditions and the following disclaimer.
163147Sxc151355  * 2. Redistributions in binary form must reproduce the above copyright
173147Sxc151355  *    notice, this list of conditions and the following disclaimer in the
183147Sxc151355  *    documentation and/or other materials provided with the distribution.
193147Sxc151355  * 3. The name of the author may not be used to endorse or promote products
203147Sxc151355  *    derived from this software without specific prior written permission.
213147Sxc151355  *
223147Sxc151355  * Alternatively, this software may be distributed under the terms of the
233147Sxc151355  * GNU General Public License ("GPL") version 2 as published by the Free
243147Sxc151355  * Software Foundation.
253147Sxc151355  *
263147Sxc151355  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
273147Sxc151355  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
283147Sxc151355  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
293147Sxc151355  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
303147Sxc151355  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
313147Sxc151355  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
323147Sxc151355  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
333147Sxc151355  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
343147Sxc151355  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
353147Sxc151355  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
363147Sxc151355  */
373147Sxc151355 
383147Sxc151355 /*
393147Sxc151355  * IEEE 802.11 generic handler
403147Sxc151355  */
413147Sxc151355 
423147Sxc151355 #include <sys/param.h>
433147Sxc151355 #include <sys/types.h>
443147Sxc151355 #include <sys/cmn_err.h>
453147Sxc151355 #include <sys/modctl.h>
464126Szf162725 #include <sys/stropts.h>
474126Szf162725 #include <sys/door.h>
488275SEric Cheng #include <sys/mac_provider.h>
493147Sxc151355 #include "net80211_impl.h"
503147Sxc151355 
513147Sxc151355 uint32_t ieee80211_debug = 0x0;	/* debug msg flags */
523147Sxc151355 
533147Sxc151355 const char *ieee80211_phymode_name[] = {
543147Sxc151355 	"auto",		/* IEEE80211_MODE_AUTO */
553147Sxc151355 	"11a",		/* IEEE80211_MODE_11A */
563147Sxc151355 	"11b",		/* IEEE80211_MODE_11B */
573147Sxc151355 	"11g",		/* IEEE80211_MODE_11G */
583147Sxc151355 	"FH",		/* IEEE80211_MODE_FH */
593147Sxc151355 	"turboA",	/* IEEE80211_MODE_TURBO_A */
603147Sxc151355 	"turboG",	/* IEEE80211_MODE_TURBO_G */
61*10266SQuaker.Fang@Sun.COM 	"sturboA",	/* IEEE80211_MODE_STURBO_A */
62*10266SQuaker.Fang@Sun.COM 	"11na",		/* IEEE80211_MODE_11NA */
63*10266SQuaker.Fang@Sun.COM 	"11ng",		/* IEEE80211_MODE_11NG */
643147Sxc151355 };
653147Sxc151355 
663147Sxc151355 #define	IEEE80211_DPRINT(_level, _fmt)	do {	\
673147Sxc151355 		_NOTE(CONSTCOND)		\
683147Sxc151355 		va_list ap;			\
693147Sxc151355 		va_start(ap, (_fmt));		\
703147Sxc151355 		vcmn_err((_level), (_fmt), ap);	\
713147Sxc151355 		va_end(ap);			\
723147Sxc151355 		_NOTE(CONSTCOND)		\
733147Sxc151355 	} while (0)
743147Sxc151355 
753147Sxc151355 /*
763147Sxc151355  * Print error messages
773147Sxc151355  */
783147Sxc151355 void
ieee80211_err(const int8_t * fmt,...)793147Sxc151355 ieee80211_err(const int8_t *fmt, ...)
803147Sxc151355 {
813147Sxc151355 	IEEE80211_DPRINT(CE_WARN, fmt);
823147Sxc151355 }
833147Sxc151355 
843147Sxc151355 /*
853147Sxc151355  * Print debug messages
863147Sxc151355  */
873147Sxc151355 void
ieee80211_dbg(uint32_t flag,const int8_t * fmt,...)883147Sxc151355 ieee80211_dbg(uint32_t flag, const int8_t *fmt, ...)
893147Sxc151355 {
903147Sxc151355 	if (flag & ieee80211_debug)
913147Sxc151355 		IEEE80211_DPRINT(CE_CONT, fmt);
923147Sxc151355 }
933147Sxc151355 
943147Sxc151355 /*
954126Szf162725  * Alloc memory, and save the size
964126Szf162725  */
974126Szf162725 void *
ieee80211_malloc(size_t size)984126Szf162725 ieee80211_malloc(size_t size)
994126Szf162725 {
1004126Szf162725 	void *p = kmem_zalloc((size + 4), KM_SLEEP);
1014126Szf162725 	*(int *)p = size;
1024126Szf162725 	p = (char *)p + 4;
1034126Szf162725 
1044126Szf162725 	return (p);
1054126Szf162725 }
1064126Szf162725 
1074126Szf162725 void
ieee80211_free(void * p)1084126Szf162725 ieee80211_free(void *p)
1094126Szf162725 {
1104126Szf162725 	void *tp = (char *)p - 4;
1114126Szf162725 	kmem_free((char *)p - 4, *(int *)tp + 4);
1124126Szf162725 }
1134126Szf162725 
1144126Szf162725 void
ieee80211_mac_update(ieee80211com_t * ic)1154126Szf162725 ieee80211_mac_update(ieee80211com_t *ic)
1164126Szf162725 {
1174126Szf162725 	wifi_data_t wd = { 0 };
1184126Szf162725 	ieee80211_node_t *in;
1194126Szf162725 
1204126Szf162725 	/*
1214126Szf162725 	 * We can send data now; update the fastpath with our
1224126Szf162725 	 * current associated BSSID and other relevant settings.
1234126Szf162725 	 */
1244126Szf162725 	in = ic->ic_bss;
1254126Szf162725 	wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
1264126Szf162725 	wd.wd_opmode = ic->ic_opmode;
1274126Szf162725 	IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
128*10266SQuaker.Fang@Sun.COM 	wd.wd_qospad = 0;
129*10266SQuaker.Fang@Sun.COM 	if (in->in_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) {
130*10266SQuaker.Fang@Sun.COM 		wd.wd_qospad = 2;
131*10266SQuaker.Fang@Sun.COM 		if (ic->ic_flags & IEEE80211_F_DATAPAD)
132*10266SQuaker.Fang@Sun.COM 			wd.wd_qospad = roundup(wd.wd_qospad, sizeof (uint32_t));
133*10266SQuaker.Fang@Sun.COM 	}
1344126Szf162725 	(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
1354126Szf162725 	mac_tx_update(ic->ic_mach);
1364126Szf162725 	ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_mac_update"
1374126Szf162725 	    "(cipher = %d)\n", wd.wd_secalloc);
1384126Szf162725 }
1394126Szf162725 
1404126Szf162725 /*
1414126Szf162725  * ieee80211_event_thread
1424126Szf162725  * open door of wpa, send event to wpad service
1434126Szf162725  */
1444126Szf162725 static void
ieee80211_event_thread(void * arg)1454126Szf162725 ieee80211_event_thread(void *arg)
1464126Szf162725 {
1474126Szf162725 	ieee80211com_t *ic = arg;
1484126Szf162725 	door_handle_t event_door = NULL;	/* Door for upcalls */
1494126Szf162725 	wl_events_t ev;
1504126Szf162725 	door_arg_t darg;
1514126Szf162725 
1524126Szf162725 	mutex_enter(&ic->ic_doorlock);
1534126Szf162725 
1544126Szf162725 	ev.event = ic->ic_eventq[ic->ic_evq_head];
1554126Szf162725 	ic->ic_evq_head ++;
1564126Szf162725 	if (ic->ic_evq_head >= MAX_EVENT)
1574126Szf162725 		ic->ic_evq_head = 0;
1584126Szf162725 
1594126Szf162725 	ieee80211_dbg(IEEE80211_MSG_DEBUG, "ieee80211_event(%d)\n", ev.event);
1604126Szf162725 	/*
1614126Szf162725 	 * Locate the door used for upcalls
1624126Szf162725 	 */
1634126Szf162725 	if (door_ki_open(ic->ic_wpadoor, &event_door) != 0) {
1644126Szf162725 		ieee80211_err("ieee80211_event: door_ki_open(%s) failed\n",
1654126Szf162725 		    ic->ic_wpadoor);
1664126Szf162725 		goto out;
1674126Szf162725 	}
1684126Szf162725 
1694126Szf162725 	darg.data_ptr = (char *)&ev;
1704126Szf162725 	darg.data_size = sizeof (wl_events_t);
1714126Szf162725 	darg.desc_ptr = NULL;
1724126Szf162725 	darg.desc_num = 0;
1734126Szf162725 	darg.rbuf = NULL;
1744126Szf162725 	darg.rsize = 0;
1754126Szf162725 
1766997Sjwadams 	if (door_ki_upcall_limited(event_door, &darg, NULL, SIZE_MAX, 0) != 0) {
1774126Szf162725 		ieee80211_err("ieee80211_event: door_ki_upcall() failed\n");
1784126Szf162725 	}
1794126Szf162725 
1804126Szf162725 	if (event_door) {	/* release our hold (if any) */
1814126Szf162725 		door_ki_rele(event_door);
1824126Szf162725 	}
1834126Szf162725 
1844126Szf162725 out:
1854126Szf162725 	mutex_exit(&ic->ic_doorlock);
1864126Szf162725 }
1874126Szf162725 
1884126Szf162725 /*
1894126Szf162725  * Notify state transition event message to WPA daemon
1904126Szf162725  */
1914126Szf162725 void
ieee80211_notify(ieee80211com_t * ic,wpa_event_type event)1924126Szf162725 ieee80211_notify(ieee80211com_t *ic, wpa_event_type event)
1934126Szf162725 {
1944126Szf162725 	if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
1954126Szf162725 		return;		/* Not running on WPA mode */
1964126Szf162725 
1974126Szf162725 	ic->ic_eventq[ic->ic_evq_tail] = event;
1984126Szf162725 	ic->ic_evq_tail ++;
1994126Szf162725 	if (ic->ic_evq_tail >= MAX_EVENT) ic->ic_evq_tail = 0;
2004126Szf162725 
2014126Szf162725 	/* async */
2024126Szf162725 	(void) timeout(ieee80211_event_thread, (void *)ic, 0);
2034126Szf162725 }
2044126Szf162725 
2054126Szf162725 /*
2065296Szf162725  * Register WPA door
2075296Szf162725  */
2085296Szf162725 void
ieee80211_register_door(ieee80211com_t * ic,const char * drvname,int inst)2095296Szf162725 ieee80211_register_door(ieee80211com_t *ic, const char *drvname, int inst)
2105296Szf162725 {
2115296Szf162725 	(void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d",
2125296Szf162725 	    WPA_DOOR, drvname, inst);
2135296Szf162725 }
2145296Szf162725 
2155296Szf162725 /*
2163147Sxc151355  * Default reset method for use with the ioctl support.  This
2173147Sxc151355  * method is invoked after any state change in the 802.11
2183147Sxc151355  * layer that should be propagated to the hardware but not
2193147Sxc151355  * require re-initialization of the 802.11 state machine (e.g
2203147Sxc151355  * rescanning for an ap).  We always return ENETRESET which
2213147Sxc151355  * should cause the driver to re-initialize the device. Drivers
2223147Sxc151355  * can override this method to implement more optimized support.
2233147Sxc151355  */
2243147Sxc151355 /* ARGSUSED */
2253147Sxc151355 static int
ieee80211_default_reset(ieee80211com_t * ic)2263147Sxc151355 ieee80211_default_reset(ieee80211com_t *ic)
2273147Sxc151355 {
2283147Sxc151355 	return (ENETRESET);
2293147Sxc151355 }
2303147Sxc151355 
2313147Sxc151355 /*
2323147Sxc151355  * Convert channel to IEEE channel number.
2333147Sxc151355  */
2343147Sxc151355 uint32_t
ieee80211_chan2ieee(ieee80211com_t * ic,struct ieee80211_channel * ch)2353147Sxc151355 ieee80211_chan2ieee(ieee80211com_t *ic, struct ieee80211_channel *ch)
2363147Sxc151355 {
2373147Sxc151355 	if ((ic->ic_sup_channels <= ch) &&
2383147Sxc151355 	    (ch <= &ic->ic_sup_channels[IEEE80211_CHAN_MAX])) {
2393147Sxc151355 		return (ch - ic->ic_sup_channels);
2403147Sxc151355 	} else if (ch == IEEE80211_CHAN_ANYC) {
2413147Sxc151355 		return (IEEE80211_CHAN_ANY);
2423147Sxc151355 	} else if (ch != NULL) {
2433147Sxc151355 		ieee80211_err("invalid channel freq %u flags %x\n",
2445296Szf162725 		    ch->ich_freq, ch->ich_flags);
2453147Sxc151355 		return (0);
2463147Sxc151355 	}
2473147Sxc151355 	ieee80211_err("invalid channel (NULL)\n");	/* ch == NULL */
2483147Sxc151355 	return (0);
2493147Sxc151355 }
2503147Sxc151355 
2513147Sxc151355 /*
2523147Sxc151355  * Convert IEEE channel number to MHz frequency.
2533147Sxc151355  *    chan    IEEE channel number
2543147Sxc151355  *    flags   specify whether the frequency is in the 2GHz ISM
2553147Sxc151355  *            band or the 5GHz band
2563147Sxc151355  *
2573147Sxc151355  * 802.11b 2GHz: 14 channels, each 5 MHz wide. Channel 1 is placed
2583147Sxc151355  * at 2.412 GHz, channel 2 at 2.417 GHz, and so on up to channel 13
2593147Sxc151355  * at 2.472 GHz. Channel 14 was defined especially for operation in
2603147Sxc151355  * Japan, and has a center frequency 2.484 GHz.
2613147Sxc151355  * 802.11g 2GHz: adopts the frequency plan of 802.11b. Japan only
2623147Sxc151355  * allows 802.11g operation in channels 1-13
2633147Sxc151355  * 802.11a 5GHz: starting every 5 MHz
2643147Sxc151355  * 802.11b/g channels 15-24 (2512-2692) are used by some implementation
2653147Sxc151355  * (Atheros etc.)
2663147Sxc151355  */
2673147Sxc151355 uint32_t
ieee80211_ieee2mhz(uint32_t chan,uint32_t flags)2683147Sxc151355 ieee80211_ieee2mhz(uint32_t chan, uint32_t flags)
2693147Sxc151355 {
2703147Sxc151355 	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
2713147Sxc151355 		if (chan == 14)
2723147Sxc151355 			return (2484);
2733147Sxc151355 		if (chan < 14)
2743147Sxc151355 			return (2412 + (chan - 1) * 5);
2753147Sxc151355 		else
2763147Sxc151355 			return (2512 + ((chan - 15) * 20));
2773147Sxc151355 	} else if (flags & IEEE80211_CHAN_5GHZ) {	/* 5Ghz band */
2783147Sxc151355 		return (5000 + (chan * 5));	/* OFDM */
2793147Sxc151355 	} else {				/* either, guess */
2803147Sxc151355 		if (chan == 14)
2813147Sxc151355 			return (2484);
2823147Sxc151355 		if (chan < 14)			/* 0-13 */
2833147Sxc151355 			return (2412 + (chan - 1) * 5);
2843147Sxc151355 		if (chan < 27)			/* 15-26 */
2853147Sxc151355 			return (2512 + ((chan - 15) * 20));
2863147Sxc151355 		return (5000 + (chan * 5));
2873147Sxc151355 	}
2883147Sxc151355 }
2893147Sxc151355 
2903147Sxc151355 /*
2913147Sxc151355  * Do late attach work. It must be called by the driver after
2923147Sxc151355  * calling ieee80211_attach() and before calling most ieee80211
2933147Sxc151355  * functions.
2943147Sxc151355  */
2953147Sxc151355 void
ieee80211_media_init(ieee80211com_t * ic)2963147Sxc151355 ieee80211_media_init(ieee80211com_t *ic)
2973147Sxc151355 {
2983147Sxc151355 	/*
2993147Sxc151355 	 * Do late attach work that must wait for any subclass
3003147Sxc151355 	 * (i.e. driver) work such as overriding methods.
3013147Sxc151355 	 */
3023147Sxc151355 	ieee80211_node_lateattach(ic);
3033147Sxc151355 }
3043147Sxc151355 
3053147Sxc151355 /*
3063147Sxc151355  * Start Watchdog timer. After count down timer(s), ic_watchdog
3073147Sxc151355  * will be called
3083147Sxc151355  */
3093147Sxc151355 void
ieee80211_start_watchdog(ieee80211com_t * ic,uint32_t timer)3103147Sxc151355 ieee80211_start_watchdog(ieee80211com_t *ic, uint32_t timer)
3113147Sxc151355 {
3123147Sxc151355 	if (ic->ic_watchdog_timer == 0 && ic->ic_watchdog != NULL) {
3133147Sxc151355 		ic->ic_watchdog_timer = timeout(ic->ic_watchdog, ic,
3145296Szf162725 		    drv_usectohz(1000000 * timer));
3153147Sxc151355 	}
3163147Sxc151355 }
3173147Sxc151355 
3183147Sxc151355 /*
3193147Sxc151355  * Stop watchdog timer.
3203147Sxc151355  */
3213147Sxc151355 void
ieee80211_stop_watchdog(ieee80211com_t * ic)3223147Sxc151355 ieee80211_stop_watchdog(ieee80211com_t *ic)
3233147Sxc151355 {
3243147Sxc151355 	if (ic->ic_watchdog_timer != 0) {
3253147Sxc151355 		if (ic->ic_watchdog != NULL)
3263147Sxc151355 			(void) untimeout(ic->ic_watchdog_timer);
3273147Sxc151355 		ic->ic_watchdog_timer = 0;
3283147Sxc151355 	}
3293147Sxc151355 }
3303147Sxc151355 
3313147Sxc151355 /*
3323147Sxc151355  * Called from a driver's xxx_watchdog routine. It is used to
3333147Sxc151355  * perform periodic cleanup of state for net80211, as well as
3343147Sxc151355  * timeout scans.
3353147Sxc151355  */
3363147Sxc151355 void
ieee80211_watchdog(void * arg)3373147Sxc151355 ieee80211_watchdog(void *arg)
3383147Sxc151355 {
3393147Sxc151355 	ieee80211com_t *ic = arg;
3403147Sxc151355 	struct ieee80211_impl *im = ic->ic_private;
3413147Sxc151355 	ieee80211_node_table_t *nt;
3423147Sxc151355 	int inact_timer = 0;
3433147Sxc151355 
3443147Sxc151355 	if (ic->ic_state == IEEE80211_S_INIT)
3453147Sxc151355 		return;
3463147Sxc151355 
3473147Sxc151355 	IEEE80211_LOCK(ic);
3483147Sxc151355 	if ((im->im_mgt_timer != 0) && (--im->im_mgt_timer == 0)) {
3493147Sxc151355 		IEEE80211_UNLOCK(ic);
3503147Sxc151355 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
3513147Sxc151355 		IEEE80211_LOCK(ic);
3523147Sxc151355 	}
3533147Sxc151355 
3543147Sxc151355 	nt = &ic->ic_scan;
3553147Sxc151355 	if (nt->nt_inact_timer != 0) {
3563147Sxc151355 		if (--nt->nt_inact_timer == 0)
3573147Sxc151355 			nt->nt_timeout(nt);
3583147Sxc151355 		inact_timer += nt->nt_inact_timer;
3593147Sxc151355 	}
3603147Sxc151355 	nt = &ic->ic_sta;
3613147Sxc151355 	if (nt->nt_inact_timer != 0) {
3623147Sxc151355 		if (--nt->nt_inact_timer == 0)
3633147Sxc151355 			nt->nt_timeout(nt);
3643147Sxc151355 		inact_timer += nt->nt_inact_timer;
3653147Sxc151355 	}
3663147Sxc151355 
3673147Sxc151355 	IEEE80211_UNLOCK(ic);
3683147Sxc151355 
3693147Sxc151355 	if (im->im_mgt_timer != 0 || inact_timer > 0)
3703147Sxc151355 		ieee80211_start_watchdog(ic, 1);
3713147Sxc151355 }
3723147Sxc151355 
3733147Sxc151355 /*
3743147Sxc151355  * Set the current phy mode and recalculate the active channel
3753147Sxc151355  * set and supported rates based on the available channels for
3763147Sxc151355  * this mode. Also select a new BSS channel if the current one
3773147Sxc151355  * is inappropriate for this mode.
3783147Sxc151355  * This function is called by net80211, and not intended to be
3793147Sxc151355  * called directly.
3803147Sxc151355  */
3813147Sxc151355 static int
ieee80211_setmode(ieee80211com_t * ic,enum ieee80211_phymode mode)3823147Sxc151355 ieee80211_setmode(ieee80211com_t *ic, enum ieee80211_phymode mode)
3833147Sxc151355 {
3843147Sxc151355 	static const uint32_t chanflags[] = {
3853147Sxc151355 		0,			/* IEEE80211_MODE_AUTO */
3863147Sxc151355 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
3873147Sxc151355 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
3883147Sxc151355 		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
3893147Sxc151355 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
3903147Sxc151355 		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO_A */
3913147Sxc151355 		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
392*10266SQuaker.Fang@Sun.COM 		IEEE80211_CHAN_ST,	/* IEEE80211_MODE_STURBO_A */
393*10266SQuaker.Fang@Sun.COM 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA (check legacy) */
394*10266SQuaker.Fang@Sun.COM 		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG (check legacy) */
3953147Sxc151355 	};
3963147Sxc151355 	struct ieee80211_channel *ch;
3973147Sxc151355 	uint32_t modeflags;
3983147Sxc151355 	int i;
3993147Sxc151355 	int achannels = 0;
4003147Sxc151355 
4013147Sxc151355 	/* validate new mode */
4023147Sxc151355 	if ((ic->ic_modecaps & (1 << mode)) == 0) {
4033147Sxc151355 		ieee80211_err("ieee80211_setmode(): mode %u not supported"
4045296Szf162725 		    " (caps 0x%x)\n", mode, ic->ic_modecaps);
4053147Sxc151355 		return (EINVAL);
4063147Sxc151355 	}
4073147Sxc151355 
4083147Sxc151355 	/*
4093147Sxc151355 	 * Verify at least one channel is present in the available
4103147Sxc151355 	 * channel list before committing to the new mode.
4113147Sxc151355 	 * Calculate the active channel set.
4123147Sxc151355 	 */
4133147Sxc151355 	ASSERT(mode < IEEE80211_N(chanflags));
4143147Sxc151355 	modeflags = chanflags[mode];
4153147Sxc151355 	bzero(ic->ic_chan_active, sizeof (ic->ic_chan_active));
4163147Sxc151355 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
4173147Sxc151355 		ch = &ic->ic_sup_channels[i];
4183147Sxc151355 		if (ch->ich_flags == 0)
4193147Sxc151355 			continue;
4203147Sxc151355 		if (mode == IEEE80211_MODE_AUTO) {
4213147Sxc151355 			/* take anything but pure turbo channels */
4223147Sxc151355 			if ((ch->ich_flags & ~IEEE80211_CHAN_TURBO) != 0) {
4233147Sxc151355 				ieee80211_setbit(ic->ic_chan_active, i);
4243147Sxc151355 				achannels++;
4253147Sxc151355 			}
4263147Sxc151355 		} else {
4273147Sxc151355 			if ((ch->ich_flags & modeflags) == modeflags) {
4283147Sxc151355 				ieee80211_setbit(ic->ic_chan_active, i);
4293147Sxc151355 				achannels++;
4303147Sxc151355 			}
4313147Sxc151355 		}
4323147Sxc151355 	}
4333147Sxc151355 	if (achannels == 0) {
4343147Sxc151355 		ieee80211_err("ieee80211_setmode(): "
4355296Szf162725 		    "no channel found for mode %u\n", mode);
4363147Sxc151355 		return (EINVAL);
4373147Sxc151355 	}
4383147Sxc151355 
4393147Sxc151355 	/*
4403147Sxc151355 	 * If no current/default channel is setup or the current
4413147Sxc151355 	 * channel is wrong for the mode then pick the first
4423147Sxc151355 	 * available channel from the active list.  This is likely
4433147Sxc151355 	 * not the right one.
4443147Sxc151355 	 */
4453147Sxc151355 	if (ic->ic_ibss_chan == NULL ||
4463147Sxc151355 	    ieee80211_isclr(ic->ic_chan_active,
4473147Sxc151355 	    ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
4483147Sxc151355 		for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
4493147Sxc151355 			if (ieee80211_isset(ic->ic_chan_active, i)) {
4503147Sxc151355 				ic->ic_ibss_chan = &ic->ic_sup_channels[i];
4513147Sxc151355 				break;
4523147Sxc151355 			}
4533147Sxc151355 		}
4543147Sxc151355 	}
4553147Sxc151355 	/*
4563147Sxc151355 	 * If the desired channel is set but no longer valid then reset it.
4573147Sxc151355 	 */
4583147Sxc151355 	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
4593147Sxc151355 	    ieee80211_isclr(ic->ic_chan_active,
4603147Sxc151355 	    ieee80211_chan2ieee(ic, ic->ic_des_chan))) {
4613147Sxc151355 		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
4623147Sxc151355 	}
4633147Sxc151355 
4643147Sxc151355 	/*
4653147Sxc151355 	 * Do mode-specific rate setup.
4663147Sxc151355 	 */
4673147Sxc151355 	if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
4683147Sxc151355 		ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode);
4693147Sxc151355 
4703147Sxc151355 	/*
4713147Sxc151355 	 * Setup an initial rate set according to the
4723147Sxc151355 	 * current/default channel.  This will be changed
4733147Sxc151355 	 * when scanning but must exist now so drivers have
4743147Sxc151355 	 * consistent state of ic_bsschan.
4753147Sxc151355 	 */
4763147Sxc151355 	if (ic->ic_bss != NULL)
4773147Sxc151355 		ic->ic_bss->in_rates = ic->ic_sup_rates[mode];
4783147Sxc151355 	ic->ic_curmode = mode;
4793147Sxc151355 	ieee80211_reset_erp(ic);	/* reset ERP state */
480*10266SQuaker.Fang@Sun.COM 	ieee80211_wme_initparams(ic);	/* reset WME stat */
4813147Sxc151355 
4823147Sxc151355 	return (0);
4833147Sxc151355 }
4843147Sxc151355 
4853147Sxc151355 /*
4863147Sxc151355  * Return the phy mode for with the specified channel so the
4873147Sxc151355  * caller can select a rate set.  This is problematic for channels
4883147Sxc151355  * where multiple operating modes are possible (e.g. 11g+11b).
4893147Sxc151355  * In those cases we defer to the current operating mode when set.
4903147Sxc151355  */
491*10266SQuaker.Fang@Sun.COM /* ARGSUSED */
4923147Sxc151355 enum ieee80211_phymode
ieee80211_chan2mode(ieee80211com_t * ic,struct ieee80211_channel * chan)4933147Sxc151355 ieee80211_chan2mode(ieee80211com_t *ic, struct ieee80211_channel *chan)
4943147Sxc151355 {
495*10266SQuaker.Fang@Sun.COM 	if (IEEE80211_IS_CHAN_HTA(chan))
496*10266SQuaker.Fang@Sun.COM 		return (IEEE80211_MODE_11NA);
497*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_HTG(chan))
498*10266SQuaker.Fang@Sun.COM 		return (IEEE80211_MODE_11NG);
499*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_108G(chan))
500*10266SQuaker.Fang@Sun.COM 		return (IEEE80211_MODE_TURBO_G);
501*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_ST(chan))
502*10266SQuaker.Fang@Sun.COM 		return (IEEE80211_MODE_STURBO_A);
503*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_T(chan))
5043147Sxc151355 		return (IEEE80211_MODE_TURBO_A);
505*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_A(chan))
5063147Sxc151355 		return (IEEE80211_MODE_11A);
507*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_ANYG(chan))
508*10266SQuaker.Fang@Sun.COM 		return (IEEE80211_MODE_11G);
509*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_B(chan))
510*10266SQuaker.Fang@Sun.COM 		return (IEEE80211_MODE_11B);
511*10266SQuaker.Fang@Sun.COM 	else if (IEEE80211_IS_CHAN_FHSS(chan))
5123147Sxc151355 		return (IEEE80211_MODE_FH);
513*10266SQuaker.Fang@Sun.COM 
514*10266SQuaker.Fang@Sun.COM 	/* NB: should not get here */
515*10266SQuaker.Fang@Sun.COM 	ieee80211_err("cannot map channel to mode; freq %u flags 0x%x\n",
516*10266SQuaker.Fang@Sun.COM 	    chan->ich_freq, chan->ich_flags);
517*10266SQuaker.Fang@Sun.COM 
518*10266SQuaker.Fang@Sun.COM 	return (IEEE80211_MODE_11B);
519*10266SQuaker.Fang@Sun.COM }
520*10266SQuaker.Fang@Sun.COM 
521*10266SQuaker.Fang@Sun.COM const struct ieee80211_rateset *
ieee80211_get_suprates(ieee80211com_t * ic,struct ieee80211_channel * c)522*10266SQuaker.Fang@Sun.COM ieee80211_get_suprates(ieee80211com_t *ic, struct ieee80211_channel *c)
523*10266SQuaker.Fang@Sun.COM {
524*10266SQuaker.Fang@Sun.COM 	if (IEEE80211_IS_CHAN_HTA(c))
525*10266SQuaker.Fang@Sun.COM 		return (&ic->ic_sup_rates[IEEE80211_MODE_11A]);
526*10266SQuaker.Fang@Sun.COM 	if (IEEE80211_IS_CHAN_HTG(c)) {
527*10266SQuaker.Fang@Sun.COM 		return (&ic->ic_sup_rates[IEEE80211_MODE_11G]);
5283147Sxc151355 	}
529*10266SQuaker.Fang@Sun.COM 	return (&ic->ic_sup_rates[ieee80211_chan2mode(ic, c)]);
530*10266SQuaker.Fang@Sun.COM }
531*10266SQuaker.Fang@Sun.COM 
532*10266SQuaker.Fang@Sun.COM /*
533*10266SQuaker.Fang@Sun.COM  * Locate a channel given a frequency+flags.  We cache
534*10266SQuaker.Fang@Sun.COM  * the previous lookup to optimize swithing between two
535*10266SQuaker.Fang@Sun.COM  * channels--as happens with dynamic turbo.
536*10266SQuaker.Fang@Sun.COM  */
537*10266SQuaker.Fang@Sun.COM struct ieee80211_channel *
ieee80211_find_channel(ieee80211com_t * ic,int freq,int flags)538*10266SQuaker.Fang@Sun.COM ieee80211_find_channel(ieee80211com_t *ic, int freq, int flags)
539*10266SQuaker.Fang@Sun.COM {
540*10266SQuaker.Fang@Sun.COM 	struct ieee80211_channel *c;
541*10266SQuaker.Fang@Sun.COM 	int i;
542*10266SQuaker.Fang@Sun.COM 
543*10266SQuaker.Fang@Sun.COM 	flags &= IEEE80211_CHAN_ALLTURBO;
544*10266SQuaker.Fang@Sun.COM 	/* brute force search */
545*10266SQuaker.Fang@Sun.COM 	for (i = 0; i < IEEE80211_CHAN_MAX; i++) {
546*10266SQuaker.Fang@Sun.COM 		c = &ic->ic_sup_channels[i];
547*10266SQuaker.Fang@Sun.COM 		if (c->ich_freq == freq &&
548*10266SQuaker.Fang@Sun.COM 		    (c->ich_flags & IEEE80211_CHAN_ALLTURBO) == flags)
549*10266SQuaker.Fang@Sun.COM 			return (c);
550*10266SQuaker.Fang@Sun.COM 	}
551*10266SQuaker.Fang@Sun.COM 	return (NULL);
5523147Sxc151355 }
5533147Sxc151355 
5543147Sxc151355 /*
5553147Sxc151355  * Return the size of the 802.11 header for a management or data frame.
5563147Sxc151355  */
5573147Sxc151355 int
ieee80211_hdrsize(const void * data)558*10266SQuaker.Fang@Sun.COM ieee80211_hdrsize(const void *data)
5593147Sxc151355 {
5603147Sxc151355 	const struct ieee80211_frame *wh = data;
5613147Sxc151355 	int size = sizeof (struct ieee80211_frame);
5623147Sxc151355 
5633147Sxc151355 	/* NB: we don't handle control frames */
5643147Sxc151355 	ASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) !=
5655296Szf162725 	    IEEE80211_FC0_TYPE_CTL);
5663147Sxc151355 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
5673147Sxc151355 		size += IEEE80211_ADDR_LEN;
568*10266SQuaker.Fang@Sun.COM 	if (IEEE80211_QOS_HAS_SEQ(wh))
569*10266SQuaker.Fang@Sun.COM 		size += sizeof (uint16_t);
5703147Sxc151355 
5713147Sxc151355 	return (size);
5723147Sxc151355 }
5733147Sxc151355 
5743147Sxc151355 /*
575*10266SQuaker.Fang@Sun.COM  * Return the space occupied by the 802.11 header and any
576*10266SQuaker.Fang@Sun.COM  * padding required by the driver.  This works for a
577*10266SQuaker.Fang@Sun.COM  * management or data frame.
578*10266SQuaker.Fang@Sun.COM  */
579*10266SQuaker.Fang@Sun.COM int
ieee80211_hdrspace(ieee80211com_t * ic,const void * data)580*10266SQuaker.Fang@Sun.COM ieee80211_hdrspace(ieee80211com_t *ic, const void *data)
581*10266SQuaker.Fang@Sun.COM {
582*10266SQuaker.Fang@Sun.COM 	int size = ieee80211_hdrsize(data);
583*10266SQuaker.Fang@Sun.COM 	if (ic->ic_flags & IEEE80211_F_DATAPAD)
584*10266SQuaker.Fang@Sun.COM 		size = roundup(size, sizeof (uint32_t));
585*10266SQuaker.Fang@Sun.COM 	return (size);
586*10266SQuaker.Fang@Sun.COM }
587*10266SQuaker.Fang@Sun.COM 
588*10266SQuaker.Fang@Sun.COM /*
589*10266SQuaker.Fang@Sun.COM  * Like ieee80211_hdrsize, but handles any type of frame.
590*10266SQuaker.Fang@Sun.COM  */
591*10266SQuaker.Fang@Sun.COM int
ieee80211_anyhdrsize(const void * data)592*10266SQuaker.Fang@Sun.COM ieee80211_anyhdrsize(const void *data)
593*10266SQuaker.Fang@Sun.COM {
594*10266SQuaker.Fang@Sun.COM 	const struct ieee80211_frame *wh = data;
595*10266SQuaker.Fang@Sun.COM 
596*10266SQuaker.Fang@Sun.COM 	if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
597*10266SQuaker.Fang@Sun.COM 		switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
598*10266SQuaker.Fang@Sun.COM 		case IEEE80211_FC0_SUBTYPE_CTS:
599*10266SQuaker.Fang@Sun.COM 		case IEEE80211_FC0_SUBTYPE_ACK:
600*10266SQuaker.Fang@Sun.COM 			return (sizeof (struct ieee80211_frame_ack));
601*10266SQuaker.Fang@Sun.COM 		case IEEE80211_FC0_SUBTYPE_BAR:
602*10266SQuaker.Fang@Sun.COM 			return (sizeof (struct ieee80211_frame_bar));
603*10266SQuaker.Fang@Sun.COM 		}
604*10266SQuaker.Fang@Sun.COM 		return (sizeof (struct ieee80211_frame_min));
605*10266SQuaker.Fang@Sun.COM 	} else
606*10266SQuaker.Fang@Sun.COM 		return (ieee80211_hdrsize(data));
607*10266SQuaker.Fang@Sun.COM }
608*10266SQuaker.Fang@Sun.COM 
609*10266SQuaker.Fang@Sun.COM /*
610*10266SQuaker.Fang@Sun.COM  * Like ieee80211_hdrspace, but handles any type of frame.
611*10266SQuaker.Fang@Sun.COM  */
612*10266SQuaker.Fang@Sun.COM int
ieee80211_anyhdrspace(ieee80211com_t * ic,const void * data)613*10266SQuaker.Fang@Sun.COM ieee80211_anyhdrspace(ieee80211com_t *ic, const void *data)
614*10266SQuaker.Fang@Sun.COM {
615*10266SQuaker.Fang@Sun.COM 	int size = ieee80211_anyhdrsize(data);
616*10266SQuaker.Fang@Sun.COM 	if (ic->ic_flags & IEEE80211_F_DATAPAD)
617*10266SQuaker.Fang@Sun.COM 		size = roundup(size, sizeof (uint32_t));
618*10266SQuaker.Fang@Sun.COM 	return (size);
619*10266SQuaker.Fang@Sun.COM }
620*10266SQuaker.Fang@Sun.COM 
621*10266SQuaker.Fang@Sun.COM /*
6223147Sxc151355  * Allocate and setup a management frame of the specified
6233147Sxc151355  * size.  We return the mblk and a pointer to the start
6243147Sxc151355  * of the contiguous data area that's been reserved based
6253147Sxc151355  * on the packet length.
6263147Sxc151355  */
6273147Sxc151355 mblk_t *
ieee80211_getmgtframe(uint8_t ** frm,int pktlen)6283147Sxc151355 ieee80211_getmgtframe(uint8_t **frm, int pktlen)
6293147Sxc151355 {
6303147Sxc151355 	mblk_t *mp;
6313147Sxc151355 	int len;
6323147Sxc151355 
6333147Sxc151355 	len = sizeof (struct ieee80211_frame) + pktlen;
6343147Sxc151355 	mp = allocb(len, BPRI_MED);
6353147Sxc151355 	if (mp != NULL) {
6363147Sxc151355 		*frm = mp->b_rptr + sizeof (struct ieee80211_frame);
6373147Sxc151355 		mp->b_wptr = mp->b_rptr + len;
6383147Sxc151355 	} else {
6393147Sxc151355 		ieee80211_err("ieee80211_getmgtframe: "
6405296Szf162725 		    "alloc frame failed, %d\n", len);
6413147Sxc151355 	}
6423147Sxc151355 	return (mp);
6433147Sxc151355 }
6443147Sxc151355 
6453147Sxc151355 /*
6463147Sxc151355  * Send system messages to notify the device has joined a WLAN.
6473147Sxc151355  * This is an OS specific function. Solaris marks link status
6483147Sxc151355  * as up.
6493147Sxc151355  */
6503147Sxc151355 void
ieee80211_notify_node_join(ieee80211com_t * ic,ieee80211_node_t * in)6513147Sxc151355 ieee80211_notify_node_join(ieee80211com_t *ic, ieee80211_node_t *in)
6523147Sxc151355 {
6533147Sxc151355 	if (in == ic->ic_bss)
6543147Sxc151355 		mac_link_update(ic->ic_mach, LINK_STATE_UP);
6554126Szf162725 	ieee80211_notify(ic, EVENT_ASSOC);	/* notify WPA service */
6563147Sxc151355 }
6573147Sxc151355 
6583147Sxc151355 /*
6593147Sxc151355  * Send system messages to notify the device has left a WLAN.
6603147Sxc151355  * This is an OS specific function. Solaris marks link status
6613147Sxc151355  * as down.
6623147Sxc151355  */
6633147Sxc151355 void
ieee80211_notify_node_leave(ieee80211com_t * ic,ieee80211_node_t * in)6643147Sxc151355 ieee80211_notify_node_leave(ieee80211com_t *ic, ieee80211_node_t *in)
6653147Sxc151355 {
6663147Sxc151355 	if (in == ic->ic_bss)
6673147Sxc151355 		mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
6684126Szf162725 	ieee80211_notify(ic, EVENT_DISASSOC);	/* notify WPA service */
6693147Sxc151355 }
6703147Sxc151355 
671*10266SQuaker.Fang@Sun.COM 
6723147Sxc151355 /*
6733147Sxc151355  * Get 802.11 kstats defined in ieee802.11(5)
6743147Sxc151355  *
6753147Sxc151355  * Return 0 on success
6763147Sxc151355  */
6773147Sxc151355 int
ieee80211_stat(ieee80211com_t * ic,uint_t stat,uint64_t * val)6783147Sxc151355 ieee80211_stat(ieee80211com_t *ic, uint_t stat, uint64_t *val)
6793147Sxc151355 {
6803147Sxc151355 	ASSERT(val != NULL);
6813147Sxc151355 	IEEE80211_LOCK(ic);
6823147Sxc151355 	switch (stat) {
6833147Sxc151355 	case WIFI_STAT_TX_FRAGS:
6843147Sxc151355 		*val = ic->ic_stats.is_tx_frags;
6853147Sxc151355 		break;
6863147Sxc151355 	case WIFI_STAT_MCAST_TX:
6873147Sxc151355 		*val = ic->ic_stats.is_tx_mcast;
6883147Sxc151355 		break;
6893147Sxc151355 	case WIFI_STAT_TX_FAILED:
6903147Sxc151355 		*val = ic->ic_stats.is_tx_failed;
6913147Sxc151355 		break;
6923147Sxc151355 	case WIFI_STAT_TX_RETRANS:
6933147Sxc151355 		*val = ic->ic_stats.is_tx_retries;
6943147Sxc151355 		break;
6953147Sxc151355 	case WIFI_STAT_RTS_SUCCESS:
6963147Sxc151355 		*val = ic->ic_stats.is_rts_success;
6973147Sxc151355 		break;
6983147Sxc151355 	case WIFI_STAT_RTS_FAILURE:
6993147Sxc151355 		*val = ic->ic_stats.is_rts_failure;
7003147Sxc151355 		break;
7013147Sxc151355 	case WIFI_STAT_ACK_FAILURE:
7023147Sxc151355 		*val = ic->ic_stats.is_ack_failure;
7033147Sxc151355 		break;
7043147Sxc151355 	case WIFI_STAT_RX_FRAGS:
7053147Sxc151355 		*val = ic->ic_stats.is_rx_frags;
7063147Sxc151355 		break;
7073147Sxc151355 	case WIFI_STAT_MCAST_RX:
7083147Sxc151355 		*val = ic->ic_stats.is_rx_mcast;
7093147Sxc151355 		break;
7103147Sxc151355 	case WIFI_STAT_RX_DUPS:
7113147Sxc151355 		*val = ic->ic_stats.is_rx_dups;
7123147Sxc151355 		break;
7133147Sxc151355 	case WIFI_STAT_FCS_ERRORS:
7143147Sxc151355 		*val = ic->ic_stats.is_fcs_errors;
7153147Sxc151355 		break;
7163147Sxc151355 	case WIFI_STAT_WEP_ERRORS:
7173147Sxc151355 		*val = ic->ic_stats.is_wep_errors;
7183147Sxc151355 		break;
7193147Sxc151355 	}
7203147Sxc151355 	IEEE80211_UNLOCK(ic);
7213147Sxc151355 	return (0);
7223147Sxc151355 }
7233147Sxc151355 
7243147Sxc151355 /*
7253147Sxc151355  * Attach network interface to the 802.11 support module. This
7263147Sxc151355  * function must be called before using any of the ieee80211
7273147Sxc151355  * functionss. The parameter "ic" MUST be initialized to tell
7283147Sxc151355  * net80211 about interface's capabilities.
7293147Sxc151355  */
7303147Sxc151355 void
ieee80211_attach(ieee80211com_t * ic)7313147Sxc151355 ieee80211_attach(ieee80211com_t *ic)
7323147Sxc151355 {
7333147Sxc151355 	struct ieee80211_impl		*im;
7343147Sxc151355 	struct ieee80211_channel	*ch;
7353147Sxc151355 	int				i;
7363147Sxc151355 
7373147Sxc151355 	/* Check mandatory callback functions not NULL */
7383147Sxc151355 	ASSERT(ic->ic_xmit != NULL);
7393147Sxc151355 
7403147Sxc151355 	mutex_init(&ic->ic_genlock, NULL, MUTEX_DRIVER, NULL);
7414126Szf162725 	mutex_init(&ic->ic_doorlock, NULL, MUTEX_DRIVER, NULL);
7423147Sxc151355 
7433147Sxc151355 	im = kmem_alloc(sizeof (ieee80211_impl_t), KM_SLEEP);
7443147Sxc151355 	ic->ic_private = im;
7453147Sxc151355 	cv_init(&im->im_scan_cv, NULL, CV_DRIVER, NULL);
7463147Sxc151355 
7473147Sxc151355 	/*
7483147Sxc151355 	 * Fill in 802.11 available channel set, mark
7493147Sxc151355 	 * all available channels as active, and pick
7503147Sxc151355 	 * a default channel if not already specified.
7513147Sxc151355 	 */
7523147Sxc151355 	bzero(im->im_chan_avail, sizeof (im->im_chan_avail));
7533147Sxc151355 	ic->ic_modecaps |= 1 << IEEE80211_MODE_AUTO;
7543147Sxc151355 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
7553147Sxc151355 		ch = &ic->ic_sup_channels[i];
7563147Sxc151355 		if (ch->ich_flags) {
7573147Sxc151355 			/* Verify driver passed us valid data */
7583147Sxc151355 			if (i != ieee80211_chan2ieee(ic, ch)) {
7593147Sxc151355 				ieee80211_err("bad channel ignored: "
7605296Szf162725 				    "freq %u flags%x number %u\n",
7615296Szf162725 				    ch->ich_freq, ch->ich_flags, i);
7623147Sxc151355 				ch->ich_flags = 0;
7633147Sxc151355 				continue;
7643147Sxc151355 			}
7653147Sxc151355 			ieee80211_setbit(im->im_chan_avail, i);
7663147Sxc151355 			/* Identify mode capabilities */
7673147Sxc151355 			if (IEEE80211_IS_CHAN_A(ch))
7683147Sxc151355 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11A;
7693147Sxc151355 			if (IEEE80211_IS_CHAN_B(ch))
7703147Sxc151355 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11B;
7713147Sxc151355 			if (IEEE80211_IS_CHAN_PUREG(ch))
7723147Sxc151355 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11G;
7733147Sxc151355 			if (IEEE80211_IS_CHAN_FHSS(ch))
7743147Sxc151355 				ic->ic_modecaps |= 1 << IEEE80211_MODE_FH;
7753147Sxc151355 			if (IEEE80211_IS_CHAN_T(ch))
7763147Sxc151355 				ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A;
7773147Sxc151355 			if (IEEE80211_IS_CHAN_108G(ch))
7783147Sxc151355 				ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G;
779*10266SQuaker.Fang@Sun.COM 			if (IEEE80211_IS_CHAN_ST(ch))
780*10266SQuaker.Fang@Sun.COM 				ic->ic_modecaps |= 1 << IEEE80211_MODE_STURBO_A;
781*10266SQuaker.Fang@Sun.COM 			if (IEEE80211_IS_CHAN_HTA(ch))
782*10266SQuaker.Fang@Sun.COM 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11NA;
783*10266SQuaker.Fang@Sun.COM 			if (IEEE80211_IS_CHAN_HTG(ch))
784*10266SQuaker.Fang@Sun.COM 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11NG;
7853147Sxc151355 			if (ic->ic_curchan == NULL) {
7863147Sxc151355 				/* arbitrarily pick the first channel */
7873147Sxc151355 				ic->ic_curchan = &ic->ic_sup_channels[i];
7883147Sxc151355 			}
7893147Sxc151355 		}
7903147Sxc151355 	}
7913147Sxc151355 	/* validate ic->ic_curmode */
7923147Sxc151355 	if ((ic->ic_modecaps & (1 << ic->ic_curmode)) == 0)
7933147Sxc151355 		ic->ic_curmode = IEEE80211_MODE_AUTO;
7943147Sxc151355 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
7953147Sxc151355 	(void) ieee80211_setmode(ic, ic->ic_curmode);
7963147Sxc151355 
797*10266SQuaker.Fang@Sun.COM 	if (ic->ic_caps & IEEE80211_C_WME)	/* enable if capable */
798*10266SQuaker.Fang@Sun.COM 		ic->ic_flags |= IEEE80211_F_WME;
7993147Sxc151355 	if (ic->ic_caps & IEEE80211_C_BURST)
8003147Sxc151355 		ic->ic_flags |= IEEE80211_F_BURST;
8013147Sxc151355 	ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
8023147Sxc151355 	ic->ic_lintval = ic->ic_bintval;
8033147Sxc151355 	ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
8043147Sxc151355 	ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
8053147Sxc151355 
8063147Sxc151355 	ic->ic_reset = ieee80211_default_reset;
8073147Sxc151355 
8083147Sxc151355 	ieee80211_node_attach(ic);
8093147Sxc151355 	ieee80211_proto_attach(ic);
8103147Sxc151355 	ieee80211_crypto_attach(ic);
811*10266SQuaker.Fang@Sun.COM 	ieee80211_ht_attach(ic);
8123147Sxc151355 
8133147Sxc151355 	ic->ic_watchdog_timer = 0;
8143147Sxc151355 }
8153147Sxc151355 
8163147Sxc151355 /*
8173147Sxc151355  * Free any ieee80211 structures associated with the driver.
8183147Sxc151355  */
8193147Sxc151355 void
ieee80211_detach(ieee80211com_t * ic)8203147Sxc151355 ieee80211_detach(ieee80211com_t *ic)
8213147Sxc151355 {
8223147Sxc151355 	struct ieee80211_impl *im = ic->ic_private;
8233147Sxc151355 
8243147Sxc151355 	ieee80211_stop_watchdog(ic);
8253147Sxc151355 	cv_destroy(&im->im_scan_cv);
8263147Sxc151355 	kmem_free(im, sizeof (ieee80211_impl_t));
8273147Sxc151355 
8286629Szf162725 	if (ic->ic_opt_ie != NULL)
8296629Szf162725 		ieee80211_free(ic->ic_opt_ie);
8306629Szf162725 
831*10266SQuaker.Fang@Sun.COM 	ieee80211_ht_detach(ic);
8323147Sxc151355 	ieee80211_node_detach(ic);
8333147Sxc151355 	ieee80211_crypto_detach(ic);
8343147Sxc151355 
8353147Sxc151355 	mutex_destroy(&ic->ic_genlock);
8364126Szf162725 	mutex_destroy(&ic->ic_doorlock);
8373147Sxc151355 }
8383147Sxc151355 
8393147Sxc151355 static struct modlmisc	i_wifi_modlmisc = {
8403147Sxc151355 	&mod_miscops,
841*10266SQuaker.Fang@Sun.COM 	"IEEE80211 Kernel Module v2.0"
8423147Sxc151355 };
8433147Sxc151355 
8443147Sxc151355 static struct modlinkage	i_wifi_modlinkage = {
8453147Sxc151355 	MODREV_1,
8463147Sxc151355 	&i_wifi_modlmisc,
8473147Sxc151355 	NULL
8483147Sxc151355 };
8493147Sxc151355 
8503147Sxc151355 /*
8513147Sxc151355  * modlinkage functions
8523147Sxc151355  */
8533147Sxc151355 int
_init(void)8543147Sxc151355 _init(void)
8553147Sxc151355 {
8563147Sxc151355 	return (mod_install(&i_wifi_modlinkage));
8573147Sxc151355 }
8583147Sxc151355 
8593147Sxc151355 int
_fini(void)8603147Sxc151355 _fini(void)
8613147Sxc151355 {
8623147Sxc151355 	return (mod_remove(&i_wifi_modlinkage));
8633147Sxc151355 }
8643147Sxc151355 
8653147Sxc151355 int
_info(struct modinfo * modinfop)8663147Sxc151355 _info(struct modinfo *modinfop)
8673147Sxc151355 {
8683147Sxc151355 	return (mod_info(&i_wifi_modlinkage, modinfop));
8693147Sxc151355 }
870