xref: /titanic_44/usr/src/uts/common/io/net80211/net80211.c (revision 39c23413b8df94a95f67b34cfd4a4dfc3fd0b48d)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2001 Atsushi Onoe
8  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * Alternatively, this software may be distributed under the terms of the
23  * GNU General Public License ("GPL") version 2 as published by the Free
24  * Software Foundation.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 /*
41  * IEEE 802.11 generic handler
42  */
43 
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/cmn_err.h>
47 #include <sys/modctl.h>
48 #include "net80211_impl.h"
49 
50 uint32_t ieee80211_debug = 0x0;	/* debug msg flags */
51 
52 const char *ieee80211_phymode_name[] = {
53 	"auto",		/* IEEE80211_MODE_AUTO */
54 	"11a",		/* IEEE80211_MODE_11A */
55 	"11b",		/* IEEE80211_MODE_11B */
56 	"11g",		/* IEEE80211_MODE_11G */
57 	"FH",		/* IEEE80211_MODE_FH */
58 	"turboA",	/* IEEE80211_MODE_TURBO_A */
59 	"turboG",	/* IEEE80211_MODE_TURBO_G */
60 };
61 
62 #define	IEEE80211_DPRINT(_level, _fmt)	do {	\
63 		_NOTE(CONSTCOND)		\
64 		va_list ap;			\
65 		va_start(ap, (_fmt));		\
66 		vcmn_err((_level), (_fmt), ap);	\
67 		va_end(ap);			\
68 		_NOTE(CONSTCOND)		\
69 	} while (0)
70 
71 /*
72  * Print error messages
73  */
74 void
75 ieee80211_err(const int8_t *fmt, ...)
76 {
77 	IEEE80211_DPRINT(CE_WARN, fmt);
78 }
79 
80 /*
81  * Print debug messages
82  */
83 void
84 ieee80211_dbg(uint32_t flag, const int8_t *fmt, ...)
85 {
86 	if (flag & ieee80211_debug)
87 		IEEE80211_DPRINT(CE_CONT, fmt);
88 }
89 
90 /*
91  * Default reset method for use with the ioctl support.  This
92  * method is invoked after any state change in the 802.11
93  * layer that should be propagated to the hardware but not
94  * require re-initialization of the 802.11 state machine (e.g
95  * rescanning for an ap).  We always return ENETRESET which
96  * should cause the driver to re-initialize the device. Drivers
97  * can override this method to implement more optimized support.
98  */
99 /* ARGSUSED */
100 static int
101 ieee80211_default_reset(ieee80211com_t *ic)
102 {
103 	return (ENETRESET);
104 }
105 
106 /*
107  * Convert channel to IEEE channel number.
108  */
109 uint32_t
110 ieee80211_chan2ieee(ieee80211com_t *ic, struct ieee80211_channel *ch)
111 {
112 	if ((ic->ic_sup_channels <= ch) &&
113 	    (ch <= &ic->ic_sup_channels[IEEE80211_CHAN_MAX])) {
114 		return (ch - ic->ic_sup_channels);
115 	} else if (ch == IEEE80211_CHAN_ANYC) {
116 		return (IEEE80211_CHAN_ANY);
117 	} else if (ch != NULL) {
118 		ieee80211_err("invalid channel freq %u flags %x\n",
119 			ch->ich_freq, ch->ich_flags);
120 		return (0);
121 	}
122 	ieee80211_err("invalid channel (NULL)\n");	/* ch == NULL */
123 	return (0);
124 }
125 
126 /*
127  * Convert IEEE channel number to MHz frequency.
128  *    chan    IEEE channel number
129  *    flags   specify whether the frequency is in the 2GHz ISM
130  *            band or the 5GHz band
131  *
132  * 802.11b 2GHz: 14 channels, each 5 MHz wide. Channel 1 is placed
133  * at 2.412 GHz, channel 2 at 2.417 GHz, and so on up to channel 13
134  * at 2.472 GHz. Channel 14 was defined especially for operation in
135  * Japan, and has a center frequency 2.484 GHz.
136  * 802.11g 2GHz: adopts the frequency plan of 802.11b. Japan only
137  * allows 802.11g operation in channels 1-13
138  * 802.11a 5GHz: starting every 5 MHz
139  * 802.11b/g channels 15-24 (2512-2692) are used by some implementation
140  * (Atheros etc.)
141  */
142 uint32_t
143 ieee80211_ieee2mhz(uint32_t chan, uint32_t flags)
144 {
145 	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
146 		if (chan == 14)
147 			return (2484);
148 		if (chan < 14)
149 			return (2412 + (chan - 1) * 5);
150 		else
151 			return (2512 + ((chan - 15) * 20));
152 	} else if (flags & IEEE80211_CHAN_5GHZ) {	/* 5Ghz band */
153 		return (5000 + (chan * 5));	/* OFDM */
154 	} else {				/* either, guess */
155 		if (chan == 14)
156 			return (2484);
157 		if (chan < 14)			/* 0-13 */
158 			return (2412 + (chan - 1) * 5);
159 		if (chan < 27)			/* 15-26 */
160 			return (2512 + ((chan - 15) * 20));
161 		return (5000 + (chan * 5));
162 	}
163 }
164 
165 /*
166  * Do late attach work. It must be called by the driver after
167  * calling ieee80211_attach() and before calling most ieee80211
168  * functions.
169  */
170 void
171 ieee80211_media_init(ieee80211com_t *ic)
172 {
173 	/*
174 	 * Do late attach work that must wait for any subclass
175 	 * (i.e. driver) work such as overriding methods.
176 	 */
177 	ieee80211_node_lateattach(ic);
178 }
179 
180 /*
181  * Start Watchdog timer. After count down timer(s), ic_watchdog
182  * will be called
183  */
184 void
185 ieee80211_start_watchdog(ieee80211com_t *ic, uint32_t timer)
186 {
187 	if (ic->ic_watchdog_timer == 0 && ic->ic_watchdog != NULL) {
188 		ic->ic_watchdog_timer = timeout(ic->ic_watchdog, ic,
189 			drv_usectohz(1000000 * timer));
190 	}
191 }
192 
193 /*
194  * Stop watchdog timer.
195  */
196 void
197 ieee80211_stop_watchdog(ieee80211com_t *ic)
198 {
199 	if (ic->ic_watchdog_timer != 0) {
200 		if (ic->ic_watchdog != NULL)
201 			(void) untimeout(ic->ic_watchdog_timer);
202 		ic->ic_watchdog_timer = 0;
203 	}
204 }
205 
206 /*
207  * Called from a driver's xxx_watchdog routine. It is used to
208  * perform periodic cleanup of state for net80211, as well as
209  * timeout scans.
210  */
211 void
212 ieee80211_watchdog(void *arg)
213 {
214 	ieee80211com_t *ic = arg;
215 	struct ieee80211_impl *im = ic->ic_private;
216 	ieee80211_node_table_t *nt;
217 	int inact_timer = 0;
218 
219 	if (ic->ic_state == IEEE80211_S_INIT)
220 		return;
221 
222 	IEEE80211_LOCK(ic);
223 	if ((im->im_mgt_timer != 0) && (--im->im_mgt_timer == 0)) {
224 		IEEE80211_UNLOCK(ic);
225 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
226 		IEEE80211_LOCK(ic);
227 	}
228 
229 	nt = &ic->ic_scan;
230 	if (nt->nt_inact_timer != 0) {
231 		if (--nt->nt_inact_timer == 0)
232 			nt->nt_timeout(nt);
233 		inact_timer += nt->nt_inact_timer;
234 	}
235 	nt = &ic->ic_sta;
236 	if (nt->nt_inact_timer != 0) {
237 		if (--nt->nt_inact_timer == 0)
238 			nt->nt_timeout(nt);
239 		inact_timer += nt->nt_inact_timer;
240 	}
241 
242 	IEEE80211_UNLOCK(ic);
243 
244 	if (im->im_mgt_timer != 0 || inact_timer > 0)
245 		ieee80211_start_watchdog(ic, 1);
246 }
247 
248 /*
249  * Set the current phy mode and recalculate the active channel
250  * set and supported rates based on the available channels for
251  * this mode. Also select a new BSS channel if the current one
252  * is inappropriate for this mode.
253  * This function is called by net80211, and not intended to be
254  * called directly.
255  */
256 static int
257 ieee80211_setmode(ieee80211com_t *ic, enum ieee80211_phymode mode)
258 {
259 	static const uint32_t chanflags[] = {
260 		0,			/* IEEE80211_MODE_AUTO */
261 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
262 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
263 		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
264 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
265 		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO_A */
266 		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
267 	};
268 	struct ieee80211_channel *ch;
269 	uint32_t modeflags;
270 	int i;
271 	int achannels = 0;
272 
273 	/* validate new mode */
274 	if ((ic->ic_modecaps & (1 << mode)) == 0) {
275 		ieee80211_err("ieee80211_setmode(): mode %u not supported"
276 			" (caps 0x%x)\n", mode, ic->ic_modecaps);
277 		return (EINVAL);
278 	}
279 
280 	/*
281 	 * Verify at least one channel is present in the available
282 	 * channel list before committing to the new mode.
283 	 * Calculate the active channel set.
284 	 */
285 	ASSERT(mode < IEEE80211_N(chanflags));
286 	modeflags = chanflags[mode];
287 	bzero(ic->ic_chan_active, sizeof (ic->ic_chan_active));
288 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
289 		ch = &ic->ic_sup_channels[i];
290 		if (ch->ich_flags == 0)
291 			continue;
292 		if (mode == IEEE80211_MODE_AUTO) {
293 			/* take anything but pure turbo channels */
294 			if ((ch->ich_flags & ~IEEE80211_CHAN_TURBO) != 0) {
295 				ieee80211_setbit(ic->ic_chan_active, i);
296 				achannels++;
297 			}
298 		} else {
299 			if ((ch->ich_flags & modeflags) == modeflags) {
300 				ieee80211_setbit(ic->ic_chan_active, i);
301 				achannels++;
302 			}
303 		}
304 	}
305 	if (achannels == 0) {
306 		ieee80211_err("ieee80211_setmode(): "
307 			"no channel found for mode %u\n", mode);
308 		return (EINVAL);
309 	}
310 
311 	/*
312 	 * If no current/default channel is setup or the current
313 	 * channel is wrong for the mode then pick the first
314 	 * available channel from the active list.  This is likely
315 	 * not the right one.
316 	 */
317 	if (ic->ic_ibss_chan == NULL ||
318 	    ieee80211_isclr(ic->ic_chan_active,
319 	    ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
320 		for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
321 			if (ieee80211_isset(ic->ic_chan_active, i)) {
322 				ic->ic_ibss_chan = &ic->ic_sup_channels[i];
323 				break;
324 			}
325 		}
326 	}
327 	/*
328 	 * If the desired channel is set but no longer valid then reset it.
329 	 */
330 	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
331 	    ieee80211_isclr(ic->ic_chan_active,
332 	    ieee80211_chan2ieee(ic, ic->ic_des_chan))) {
333 		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
334 	}
335 
336 	/*
337 	 * Do mode-specific rate setup.
338 	 */
339 	if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
340 		ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode);
341 
342 	/*
343 	 * Setup an initial rate set according to the
344 	 * current/default channel.  This will be changed
345 	 * when scanning but must exist now so drivers have
346 	 * consistent state of ic_bsschan.
347 	 */
348 	if (ic->ic_bss != NULL)
349 		ic->ic_bss->in_rates = ic->ic_sup_rates[mode];
350 	ic->ic_curmode = mode;
351 	ieee80211_reset_erp(ic);	/* reset ERP state */
352 
353 	return (0);
354 }
355 
356 /*
357  * Return the phy mode for with the specified channel so the
358  * caller can select a rate set.  This is problematic for channels
359  * where multiple operating modes are possible (e.g. 11g+11b).
360  * In those cases we defer to the current operating mode when set.
361  */
362 enum ieee80211_phymode
363 ieee80211_chan2mode(ieee80211com_t *ic, struct ieee80211_channel *chan)
364 {
365 	if (IEEE80211_IS_CHAN_T(chan)) {
366 		return (IEEE80211_MODE_TURBO_A);
367 	} else if (IEEE80211_IS_CHAN_5GHZ(chan)) {
368 		return (IEEE80211_MODE_11A);
369 	} else if (IEEE80211_IS_CHAN_FHSS(chan)) {
370 		return (IEEE80211_MODE_FH);
371 	} else if (chan->ich_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) {
372 		/*
373 		 * This assumes all 11g channels are also usable
374 		 * for 11b, which is currently true.
375 		 */
376 		if (ic->ic_curmode == IEEE80211_MODE_TURBO_G)
377 			return (IEEE80211_MODE_TURBO_G);
378 		if (ic->ic_curmode == IEEE80211_MODE_11B)
379 			return (IEEE80211_MODE_11B);
380 		return (IEEE80211_MODE_11G);
381 	} else {
382 		return (IEEE80211_MODE_11B);
383 	}
384 }
385 
386 /*
387  * Return the size of the 802.11 header for a management or data frame.
388  */
389 int
390 ieee80211_hdrspace(const void *data)
391 {
392 	const struct ieee80211_frame *wh = data;
393 	int size = sizeof (struct ieee80211_frame);
394 
395 	/* NB: we don't handle control frames */
396 	ASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) !=
397 		IEEE80211_FC0_TYPE_CTL);
398 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
399 		size += IEEE80211_ADDR_LEN;
400 
401 	return (size);
402 }
403 
404 /*
405  * Allocate and setup a management frame of the specified
406  * size.  We return the mblk and a pointer to the start
407  * of the contiguous data area that's been reserved based
408  * on the packet length.
409  */
410 mblk_t *
411 ieee80211_getmgtframe(uint8_t **frm, int pktlen)
412 {
413 	mblk_t *mp;
414 	int len;
415 
416 	len = sizeof (struct ieee80211_frame) + pktlen;
417 	mp = allocb(len, BPRI_MED);
418 	if (mp != NULL) {
419 		*frm = mp->b_rptr + sizeof (struct ieee80211_frame);
420 		mp->b_wptr = mp->b_rptr + len;
421 	} else {
422 		ieee80211_err("ieee80211_getmgtframe: "
423 			"alloc frame failed, %d\n", len);
424 	}
425 	return (mp);
426 }
427 
428 /*
429  * Send system messages to notify the device has joined a WLAN.
430  * This is an OS specific function. Solaris marks link status
431  * as up.
432  */
433 void
434 ieee80211_notify_node_join(ieee80211com_t *ic, ieee80211_node_t *in)
435 {
436 	if (in == ic->ic_bss)
437 		mac_link_update(ic->ic_mach, LINK_STATE_UP);
438 }
439 
440 /*
441  * Send system messages to notify the device has left a WLAN.
442  * This is an OS specific function. Solaris marks link status
443  * as down.
444  */
445 void
446 ieee80211_notify_node_leave(ieee80211com_t *ic, ieee80211_node_t *in)
447 {
448 	if (in == ic->ic_bss)
449 		mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
450 }
451 
452 /*
453  * Get 802.11 kstats defined in ieee802.11(5)
454  *
455  * Return 0 on success
456  */
457 int
458 ieee80211_stat(ieee80211com_t *ic, uint_t stat, uint64_t *val)
459 {
460 	ASSERT(val != NULL);
461 	IEEE80211_LOCK(ic);
462 	switch (stat) {
463 	case WIFI_STAT_TX_FRAGS:
464 		*val = ic->ic_stats.is_tx_frags;
465 		break;
466 	case WIFI_STAT_MCAST_TX:
467 		*val = ic->ic_stats.is_tx_mcast;
468 		break;
469 	case WIFI_STAT_TX_FAILED:
470 		*val = ic->ic_stats.is_tx_failed;
471 		break;
472 	case WIFI_STAT_TX_RETRANS:
473 		*val = ic->ic_stats.is_tx_retries;
474 		break;
475 	case WIFI_STAT_RTS_SUCCESS:
476 		*val = ic->ic_stats.is_rts_success;
477 		break;
478 	case WIFI_STAT_RTS_FAILURE:
479 		*val = ic->ic_stats.is_rts_failure;
480 		break;
481 	case WIFI_STAT_ACK_FAILURE:
482 		*val = ic->ic_stats.is_ack_failure;
483 		break;
484 	case WIFI_STAT_RX_FRAGS:
485 		*val = ic->ic_stats.is_rx_frags;
486 		break;
487 	case WIFI_STAT_MCAST_RX:
488 		*val = ic->ic_stats.is_rx_mcast;
489 		break;
490 	case WIFI_STAT_RX_DUPS:
491 		*val = ic->ic_stats.is_rx_dups;
492 		break;
493 	case WIFI_STAT_FCS_ERRORS:
494 		*val = ic->ic_stats.is_fcs_errors;
495 		break;
496 	case WIFI_STAT_WEP_ERRORS:
497 		*val = ic->ic_stats.is_wep_errors;
498 		break;
499 	}
500 	IEEE80211_UNLOCK(ic);
501 	return (0);
502 }
503 
504 /*
505  * Attach network interface to the 802.11 support module. This
506  * function must be called before using any of the ieee80211
507  * functionss. The parameter "ic" MUST be initialized to tell
508  * net80211 about interface's capabilities.
509  */
510 void
511 ieee80211_attach(ieee80211com_t *ic)
512 {
513 	struct ieee80211_impl		*im;
514 	struct ieee80211_channel	*ch;
515 	int				i;
516 
517 	/* Check mandatory callback functions not NULL */
518 	ASSERT(ic->ic_xmit != NULL);
519 
520 	mutex_init(&ic->ic_genlock, NULL, MUTEX_DRIVER, NULL);
521 
522 	im = kmem_alloc(sizeof (ieee80211_impl_t), KM_SLEEP);
523 	ic->ic_private = im;
524 	cv_init(&im->im_scan_cv, NULL, CV_DRIVER, NULL);
525 
526 	/*
527 	 * Fill in 802.11 available channel set, mark
528 	 * all available channels as active, and pick
529 	 * a default channel if not already specified.
530 	 */
531 	bzero(im->im_chan_avail, sizeof (im->im_chan_avail));
532 	ic->ic_modecaps |= 1 << IEEE80211_MODE_AUTO;
533 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
534 		ch = &ic->ic_sup_channels[i];
535 		if (ch->ich_flags) {
536 			/* Verify driver passed us valid data */
537 			if (i != ieee80211_chan2ieee(ic, ch)) {
538 				ieee80211_err("bad channel ignored: "
539 					"freq %u flags%x number %u\n",
540 					ch->ich_freq, ch->ich_flags, i);
541 				ch->ich_flags = 0;
542 				continue;
543 			}
544 			ieee80211_setbit(im->im_chan_avail, i);
545 			/* Identify mode capabilities */
546 			if (IEEE80211_IS_CHAN_A(ch))
547 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11A;
548 			if (IEEE80211_IS_CHAN_B(ch))
549 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11B;
550 			if (IEEE80211_IS_CHAN_PUREG(ch))
551 				ic->ic_modecaps |= 1 << IEEE80211_MODE_11G;
552 			if (IEEE80211_IS_CHAN_FHSS(ch))
553 				ic->ic_modecaps |= 1 << IEEE80211_MODE_FH;
554 			if (IEEE80211_IS_CHAN_T(ch))
555 				ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A;
556 			if (IEEE80211_IS_CHAN_108G(ch))
557 				ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G;
558 			if (ic->ic_curchan == NULL) {
559 				/* arbitrarily pick the first channel */
560 				ic->ic_curchan = &ic->ic_sup_channels[i];
561 			}
562 		}
563 	}
564 	/* validate ic->ic_curmode */
565 	if ((ic->ic_modecaps & (1 << ic->ic_curmode)) == 0)
566 		ic->ic_curmode = IEEE80211_MODE_AUTO;
567 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
568 	(void) ieee80211_setmode(ic, ic->ic_curmode);
569 
570 	if (ic->ic_caps & IEEE80211_C_BURST)
571 		ic->ic_flags |= IEEE80211_F_BURST;
572 	ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
573 	ic->ic_lintval = ic->ic_bintval;
574 	ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
575 	ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
576 
577 	ic->ic_reset = ieee80211_default_reset;
578 
579 	ieee80211_node_attach(ic);
580 	ieee80211_proto_attach(ic);
581 	ieee80211_crypto_attach(ic);
582 
583 	ic->ic_watchdog_timer = 0;
584 }
585 
586 /*
587  * Free any ieee80211 structures associated with the driver.
588  */
589 void
590 ieee80211_detach(ieee80211com_t *ic)
591 {
592 	struct ieee80211_impl *im = ic->ic_private;
593 
594 	ieee80211_stop_watchdog(ic);
595 	cv_destroy(&im->im_scan_cv);
596 	kmem_free(im, sizeof (ieee80211_impl_t));
597 
598 	ieee80211_node_detach(ic);
599 	ieee80211_crypto_detach(ic);
600 
601 	mutex_destroy(&ic->ic_genlock);
602 }
603 
604 static struct modlmisc	i_wifi_modlmisc = {
605 	&mod_miscops,
606 	"IEEE80211 Kernel Misc Module"
607 };
608 
609 static struct modlinkage	i_wifi_modlinkage = {
610 	MODREV_1,
611 	&i_wifi_modlmisc,
612 	NULL
613 };
614 
615 /*
616  * modlinkage functions
617  */
618 int
619 _init(void)
620 {
621 	return (mod_install(&i_wifi_modlinkage));
622 }
623 
624 int
625 _fini(void)
626 {
627 	return (mod_remove(&i_wifi_modlinkage));
628 }
629 
630 int
631 _info(struct modinfo *modinfop)
632 {
633 	return (mod_info(&i_wifi_modlinkage, modinfop));
634 }
635