xref: /freebsd/sys/net80211/ieee80211_freebsd.c (revision 472f08ca8f92bfee4c17ef02e992c978b8b1fd8e)
18a1b9b6aSSam Leffler /*-
28a1b9b6aSSam Leffler  * Copyright (c) 2003-2004 Sam Leffler, Errno Consulting
38a1b9b6aSSam Leffler  * All rights reserved.
48a1b9b6aSSam Leffler  *
58a1b9b6aSSam Leffler  * Redistribution and use in source and binary forms, with or without
68a1b9b6aSSam Leffler  * modification, are permitted provided that the following conditions
78a1b9b6aSSam Leffler  * are met:
88a1b9b6aSSam Leffler  * 1. Redistributions of source code must retain the above copyright
98a1b9b6aSSam Leffler  *    notice, this list of conditions and the following disclaimer.
108a1b9b6aSSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
118a1b9b6aSSam Leffler  *    notice, this list of conditions and the following disclaimer in the
128a1b9b6aSSam Leffler  *    documentation and/or other materials provided with the distribution.
138a1b9b6aSSam Leffler  * 3. The name of the author may not be used to endorse or promote products
148a1b9b6aSSam Leffler  *    derived from this software without specific prior written permission.
158a1b9b6aSSam Leffler  *
168a1b9b6aSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178a1b9b6aSSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188a1b9b6aSSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198a1b9b6aSSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208a1b9b6aSSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
218a1b9b6aSSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
228a1b9b6aSSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
238a1b9b6aSSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
248a1b9b6aSSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
258a1b9b6aSSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
268a1b9b6aSSam Leffler  */
278a1b9b6aSSam Leffler 
288a1b9b6aSSam Leffler #include <sys/cdefs.h>
298a1b9b6aSSam Leffler __FBSDID("$FreeBSD$");
308a1b9b6aSSam Leffler 
318a1b9b6aSSam Leffler /*
328a1b9b6aSSam Leffler  * IEEE 802.11 support (FreeBSD-specific code)
338a1b9b6aSSam Leffler  */
348a1b9b6aSSam Leffler #include <sys/param.h>
358a1b9b6aSSam Leffler #include <sys/kernel.h>
368a1b9b6aSSam Leffler #include <sys/systm.h>
378a1b9b6aSSam Leffler #include <sys/linker.h>
388a1b9b6aSSam Leffler #include <sys/mbuf.h>
398a1b9b6aSSam Leffler #include <sys/module.h>
408a1b9b6aSSam Leffler #include <sys/proc.h>
418a1b9b6aSSam Leffler #include <sys/sysctl.h>
428a1b9b6aSSam Leffler 
438a1b9b6aSSam Leffler #include <sys/socket.h>
448a1b9b6aSSam Leffler 
458a1b9b6aSSam Leffler #include <net/if.h>
468a1b9b6aSSam Leffler #include <net/if_media.h>
478a1b9b6aSSam Leffler #include <net/ethernet.h>
488a1b9b6aSSam Leffler #include <net/route.h>
498a1b9b6aSSam Leffler 
508a1b9b6aSSam Leffler #include <net80211/ieee80211_var.h>
518a1b9b6aSSam Leffler 
528a1b9b6aSSam Leffler SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters");
538a1b9b6aSSam Leffler 
548a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG
558a1b9b6aSSam Leffler int	ieee80211_debug = 0;
568a1b9b6aSSam Leffler SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
578a1b9b6aSSam Leffler 	    0, "debugging printfs");
588a1b9b6aSSam Leffler #endif
598a1b9b6aSSam Leffler 
608a1b9b6aSSam Leffler static int
618a1b9b6aSSam Leffler ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
628a1b9b6aSSam Leffler {
638a1b9b6aSSam Leffler 	int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT;
648a1b9b6aSSam Leffler 	int error;
658a1b9b6aSSam Leffler 
668a1b9b6aSSam Leffler 	error = sysctl_handle_int(oidp, &inact, 0, req);
678a1b9b6aSSam Leffler 	if (error || !req->newptr)
688a1b9b6aSSam Leffler 		return error;
698a1b9b6aSSam Leffler 	*(int *)arg1 = inact / IEEE80211_INACT_WAIT;
708a1b9b6aSSam Leffler 	return 0;
718a1b9b6aSSam Leffler }
728a1b9b6aSSam Leffler 
738a1b9b6aSSam Leffler static int
748a1b9b6aSSam Leffler ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
758a1b9b6aSSam Leffler {
768a1b9b6aSSam Leffler 	struct ieee80211com *ic = arg1;
778a1b9b6aSSam Leffler 	const char *name = ic->ic_ifp->if_xname;
788a1b9b6aSSam Leffler 
798a1b9b6aSSam Leffler 	return SYSCTL_OUT(req, name, strlen(name));
808a1b9b6aSSam Leffler }
818a1b9b6aSSam Leffler 
828a1b9b6aSSam Leffler void
838a1b9b6aSSam Leffler ieee80211_sysctl_attach(struct ieee80211com *ic)
848a1b9b6aSSam Leffler {
858a1b9b6aSSam Leffler 	struct sysctl_ctx_list *ctx;
868a1b9b6aSSam Leffler 	struct sysctl_oid *oid;
878a1b9b6aSSam Leffler 	char num[14];			/* sufficient for 32 bits */
888a1b9b6aSSam Leffler 
898a1b9b6aSSam Leffler 	MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
908a1b9b6aSSam Leffler 		M_DEVBUF, M_NOWAIT | M_ZERO);
918a1b9b6aSSam Leffler 	if (ctx == NULL) {
928a1b9b6aSSam Leffler 		if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n",
938a1b9b6aSSam Leffler 			__func__);
948a1b9b6aSSam Leffler 		return;
958a1b9b6aSSam Leffler 	}
968a1b9b6aSSam Leffler 	sysctl_ctx_init(ctx);
978a1b9b6aSSam Leffler 	snprintf(num, sizeof(num), "%u", ic->ic_vap);
988a1b9b6aSSam Leffler 	oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan),
998a1b9b6aSSam Leffler 		OID_AUTO, num, CTLFLAG_RD, NULL, "");
1008a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1018a1b9b6aSSam Leffler 		"%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A",
1028a1b9b6aSSam Leffler 		"parent device");
1038a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG
1048a1b9b6aSSam Leffler 	ic->ic_debug = ieee80211_debug;
1058a1b9b6aSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1068a1b9b6aSSam Leffler 		"debug", CTLFLAG_RW, &ic->ic_debug, 0,
1078a1b9b6aSSam Leffler 		"control debugging printfs");
1088a1b9b6aSSam Leffler #endif
1098a1b9b6aSSam Leffler 	/* XXX inherit from tunables */
1108a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1118a1b9b6aSSam Leffler 		"inact", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0,
1128a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1138a1b9b6aSSam Leffler 		"station inactivity timeout (sec)");
1148a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1158a1b9b6aSSam Leffler 		"inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0,
1168a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1178a1b9b6aSSam Leffler 		"station inactivity probe timeout (sec)");
1188a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1198a1b9b6aSSam Leffler 		"inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0,
1208a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1218a1b9b6aSSam Leffler 		"station authentication timeout (sec)");
1228a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1238a1b9b6aSSam Leffler 		"inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0,
1248a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1258a1b9b6aSSam Leffler 		"station initial state timeout (sec)");
1268a1b9b6aSSam Leffler 	ic->ic_sysctl = ctx;
1278a1b9b6aSSam Leffler }
1288a1b9b6aSSam Leffler 
1298a1b9b6aSSam Leffler void
1308a1b9b6aSSam Leffler ieee80211_sysctl_detach(struct ieee80211com *ic)
1318a1b9b6aSSam Leffler {
1328a1b9b6aSSam Leffler 
1338a1b9b6aSSam Leffler 	if (ic->ic_sysctl != NULL) {
1348a1b9b6aSSam Leffler 		sysctl_ctx_free(ic->ic_sysctl);
1358a1b9b6aSSam Leffler 		ic->ic_sysctl = NULL;
1368a1b9b6aSSam Leffler 	}
1378a1b9b6aSSam Leffler }
1388a1b9b6aSSam Leffler 
1398a1b9b6aSSam Leffler int
1408a1b9b6aSSam Leffler ieee80211_node_dectestref(struct ieee80211_node *ni)
1418a1b9b6aSSam Leffler {
1428a1b9b6aSSam Leffler 	/* XXX need equivalent of atomic_dec_and_test */
1438a1b9b6aSSam Leffler 	atomic_subtract_int(&ni->ni_refcnt, 1);
1448a1b9b6aSSam Leffler 	return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
1458a1b9b6aSSam Leffler }
1468a1b9b6aSSam Leffler 
1478a1b9b6aSSam Leffler /*
1488a1b9b6aSSam Leffler  * Allocate and setup a management frame of the specified
1498a1b9b6aSSam Leffler  * size.  We return the mbuf and a pointer to the start
1508a1b9b6aSSam Leffler  * of the contiguous data area that's been reserved based
1518a1b9b6aSSam Leffler  * on the packet length.  The data area is forced to 32-bit
1528a1b9b6aSSam Leffler  * alignment and the buffer length to a multiple of 4 bytes.
1538a1b9b6aSSam Leffler  * This is done mainly so beacon frames (that require this)
1548a1b9b6aSSam Leffler  * can use this interface too.
1558a1b9b6aSSam Leffler  */
1568a1b9b6aSSam Leffler struct mbuf *
1578a1b9b6aSSam Leffler ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
1588a1b9b6aSSam Leffler {
1598a1b9b6aSSam Leffler 	struct mbuf *m;
1608a1b9b6aSSam Leffler 	u_int len;
1618a1b9b6aSSam Leffler 
1628a1b9b6aSSam Leffler 	/*
1638a1b9b6aSSam Leffler 	 * NB: we know the mbuf routines will align the data area
1648a1b9b6aSSam Leffler 	 *     so we don't need to do anything special.
1658a1b9b6aSSam Leffler 	 */
1668a1b9b6aSSam Leffler 	/* XXX 4-address frame? */
1678a1b9b6aSSam Leffler 	len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
1688a1b9b6aSSam Leffler 	KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
1698a1b9b6aSSam Leffler 	if (len < MINCLSIZE) {
1708a1b9b6aSSam Leffler 		m = m_gethdr(M_NOWAIT, MT_HEADER);
1718a1b9b6aSSam Leffler 		/*
1728a1b9b6aSSam Leffler 		 * Align the data in case additional headers are added.
1738a1b9b6aSSam Leffler 		 * This should only happen when a WEP header is added
1748a1b9b6aSSam Leffler 		 * which only happens for shared key authentication mgt
1758a1b9b6aSSam Leffler 		 * frames which all fit in MHLEN.
1768a1b9b6aSSam Leffler 		 */
1778a1b9b6aSSam Leffler 		if (m != NULL)
1788a1b9b6aSSam Leffler 			MH_ALIGN(m, len);
1798a1b9b6aSSam Leffler 	} else
1808a1b9b6aSSam Leffler 		m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR);
1818a1b9b6aSSam Leffler 	if (m != NULL) {
1828a1b9b6aSSam Leffler 		m->m_data += sizeof(struct ieee80211_frame);
1838a1b9b6aSSam Leffler 		*frm = m->m_data;
1848a1b9b6aSSam Leffler 	}
1858a1b9b6aSSam Leffler 	return m;
1868a1b9b6aSSam Leffler }
1878a1b9b6aSSam Leffler 
1888a1b9b6aSSam Leffler #include <sys/libkern.h>
1898a1b9b6aSSam Leffler 
1908a1b9b6aSSam Leffler void
1918a1b9b6aSSam Leffler get_random_bytes(void *p, size_t n)
1928a1b9b6aSSam Leffler {
1938a1b9b6aSSam Leffler 	u_int8_t *dp = p;
1948a1b9b6aSSam Leffler 
1958a1b9b6aSSam Leffler 	while (n > 0) {
1968a1b9b6aSSam Leffler 		u_int32_t v = arc4random();
1978a1b9b6aSSam Leffler 		size_t nb = n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n;
1988a1b9b6aSSam Leffler 		bcopy(&v, dp, n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n);
1998a1b9b6aSSam Leffler 		dp += sizeof(u_int32_t), n -= nb;
2008a1b9b6aSSam Leffler 	}
2018a1b9b6aSSam Leffler }
2028a1b9b6aSSam Leffler 
2038a1b9b6aSSam Leffler void
2048a1b9b6aSSam Leffler ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
2058a1b9b6aSSam Leffler {
2068a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
2078a1b9b6aSSam Leffler 	struct ieee80211_join_event iev;
2088a1b9b6aSSam Leffler 
2098a1b9b6aSSam Leffler 	if (ni == ic->ic_bss) {
2108a1b9b6aSSam Leffler 		memset(&iev, 0, sizeof(iev));
2118a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
2128a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, newassoc ?
2138a1b9b6aSSam Leffler 			RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
2148a1b9b6aSSam Leffler 			&iev, sizeof(iev));
2158a1b9b6aSSam Leffler 		if_link_state_change(ifp, LINK_STATE_UP);
2168a1b9b6aSSam Leffler 	} else if (newassoc) {
2178a1b9b6aSSam Leffler 		/* fire off wireless event only for new station */
2188a1b9b6aSSam Leffler 		memset(&iev, 0, sizeof(iev));
2198a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
2208a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_JOIN, &iev, sizeof(iev));
2218a1b9b6aSSam Leffler 	}
2228a1b9b6aSSam Leffler }
2238a1b9b6aSSam Leffler 
2248a1b9b6aSSam Leffler void
2258a1b9b6aSSam Leffler ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
2268a1b9b6aSSam Leffler {
2278a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
2288a1b9b6aSSam Leffler 	struct ieee80211_leave_event iev;
2298a1b9b6aSSam Leffler 
2308a1b9b6aSSam Leffler 	if (ni == ic->ic_bss) {
2318a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
2328a1b9b6aSSam Leffler 		if_link_state_change(ifp, LINK_STATE_DOWN);
2338a1b9b6aSSam Leffler 	} else {
2348a1b9b6aSSam Leffler 		/* fire off wireless event station leaving */
2358a1b9b6aSSam Leffler 		memset(&iev, 0, sizeof(iev));
2368a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
2378a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
2388a1b9b6aSSam Leffler 	}
2398a1b9b6aSSam Leffler }
2408a1b9b6aSSam Leffler 
2418a1b9b6aSSam Leffler void
2428a1b9b6aSSam Leffler ieee80211_notify_scan_done(struct ieee80211com *ic)
2438a1b9b6aSSam Leffler {
2448a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
2458a1b9b6aSSam Leffler 
2468a1b9b6aSSam Leffler 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
2478a1b9b6aSSam Leffler 		"%s: notify scan done\n", ic->ic_ifp->if_xname);
2488a1b9b6aSSam Leffler 
2498a1b9b6aSSam Leffler 	/* dispatch wireless event indicating scan completed */
2508a1b9b6aSSam Leffler 	rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
2518a1b9b6aSSam Leffler }
2528a1b9b6aSSam Leffler 
2538a1b9b6aSSam Leffler void
2548a1b9b6aSSam Leffler ieee80211_notify_replay_failure(struct ieee80211com *ic,
2558a1b9b6aSSam Leffler 	const struct ieee80211_frame *wh, const struct ieee80211_key *k,
2568a1b9b6aSSam Leffler 	u_int64_t rsc)
2578a1b9b6aSSam Leffler {
2588a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
2598a1b9b6aSSam Leffler 
2608a1b9b6aSSam Leffler 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
26116574882SSam Leffler 		"[%s] %s replay detected <rsc %ju, csc %ju>\n",
2628a1b9b6aSSam Leffler 		ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
26316574882SSam Leffler 		(intmax_t) rsc, (intmax_t) k->wk_keyrsc);
2648a1b9b6aSSam Leffler 
2658a1b9b6aSSam Leffler 	if (ifp != NULL) {		/* NB: for cipher test modules */
2668a1b9b6aSSam Leffler 		struct ieee80211_replay_event iev;
2678a1b9b6aSSam Leffler 
2688a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
2698a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
2708a1b9b6aSSam Leffler 		iev.iev_cipher = k->wk_cipher->ic_cipher;
2718a1b9b6aSSam Leffler 		iev.iev_keyix = k->wk_keyix;
2728a1b9b6aSSam Leffler 		iev.iev_keyrsc = k->wk_keyrsc;
2738a1b9b6aSSam Leffler 		iev.iev_rsc = rsc;
2748a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
2758a1b9b6aSSam Leffler 	}
2768a1b9b6aSSam Leffler }
2778a1b9b6aSSam Leffler 
2788a1b9b6aSSam Leffler void
2798a1b9b6aSSam Leffler ieee80211_notify_michael_failure(struct ieee80211com *ic,
2808a1b9b6aSSam Leffler 	const struct ieee80211_frame *wh, u_int keyix)
2818a1b9b6aSSam Leffler {
2828a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
2838a1b9b6aSSam Leffler 
2848a1b9b6aSSam Leffler 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
2858a1b9b6aSSam Leffler 		"[%s] Michael MIC verification failed <keyidx %d>\n",
2868a1b9b6aSSam Leffler 	       ether_sprintf(wh->i_addr2), keyix);
2878a1b9b6aSSam Leffler 	ic->ic_stats.is_rx_tkipmic++;
2888a1b9b6aSSam Leffler 
2898a1b9b6aSSam Leffler 	if (ifp != NULL) {		/* NB: for cipher test modules */
2908a1b9b6aSSam Leffler 		struct ieee80211_michael_event iev;
2918a1b9b6aSSam Leffler 
2928a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
2938a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
2948a1b9b6aSSam Leffler 		iev.iev_cipher = IEEE80211_CIPHER_TKIP;
2958a1b9b6aSSam Leffler 		iev.iev_keyix = keyix;
2968a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev));
2978a1b9b6aSSam Leffler 	}
2988a1b9b6aSSam Leffler }
2998a1b9b6aSSam Leffler 
3008a1b9b6aSSam Leffler void
3018a1b9b6aSSam Leffler ieee80211_load_module(const char *modname)
3028a1b9b6aSSam Leffler {
303472f08caSSam Leffler #ifdef notyet
3048a1b9b6aSSam Leffler 	struct thread *td = curthread;
3058a1b9b6aSSam Leffler 
3068a1b9b6aSSam Leffler 	if (suser(td) == 0 && securelevel_gt(td->td_ucred, 0) == 0) {
3078a1b9b6aSSam Leffler 		mtx_lock(&Giant);
3088a1b9b6aSSam Leffler 		(void) linker_load_module(modname, NULL, NULL, NULL, NULL);
3098a1b9b6aSSam Leffler 		mtx_unlock(&Giant);
3108a1b9b6aSSam Leffler 	}
311472f08caSSam Leffler #else
312472f08caSSam Leffler 	printf("%s: load the %s module by hand for now.\n", __func__, modname);
313472f08caSSam Leffler #endif
3148a1b9b6aSSam Leffler }
3158a1b9b6aSSam Leffler 
3168a1b9b6aSSam Leffler /*
3178a1b9b6aSSam Leffler  * Module glue.
3188a1b9b6aSSam Leffler  *
3198a1b9b6aSSam Leffler  * NB: the module name is "wlan" for compatibility with NetBSD.
3208a1b9b6aSSam Leffler  */
3218a1b9b6aSSam Leffler static int
3228a1b9b6aSSam Leffler wlan_modevent(module_t mod, int type, void *unused)
3238a1b9b6aSSam Leffler {
3248a1b9b6aSSam Leffler 	switch (type) {
3258a1b9b6aSSam Leffler 	case MOD_LOAD:
3268a1b9b6aSSam Leffler 		if (bootverbose)
3278a1b9b6aSSam Leffler 			printf("wlan: <802.11 Link Layer>\n");
3288a1b9b6aSSam Leffler 		return 0;
3298a1b9b6aSSam Leffler 	case MOD_UNLOAD:
3308a1b9b6aSSam Leffler 		return 0;
3318a1b9b6aSSam Leffler 	}
3328a1b9b6aSSam Leffler 	return EINVAL;
3338a1b9b6aSSam Leffler }
3348a1b9b6aSSam Leffler 
3358a1b9b6aSSam Leffler static moduledata_t wlan_mod = {
3368a1b9b6aSSam Leffler 	"wlan",
3378a1b9b6aSSam Leffler 	wlan_modevent,
3388a1b9b6aSSam Leffler 	0
3398a1b9b6aSSam Leffler };
3408a1b9b6aSSam Leffler DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
3418a1b9b6aSSam Leffler MODULE_VERSION(wlan, 1);
3428a1b9b6aSSam Leffler MODULE_DEPEND(wlan, ether, 1, 1, 1);
343