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