diff options
author | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
commit | 3569e353ca63336d80ab0143dd9669b0b9e6b123 (patch) | |
tree | bc7985c57e7ecfa1ac03e48c406a25430dba634b /sys/net80211/ieee80211_freebsd.c | |
parent | 682b4ae9be70192e298129ada878af3486683aaf (diff) | |
download | FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.zip FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.tar.gz |
Multi-bss (aka vap) support for 802.11 devices.
Note this includes changes to all drivers and moves some device firmware
loading to use firmware(9) and a separate module (e.g. ral). Also there
no longer are separate wlan_scan* modules; this functionality is now
bundled into the wlan module.
Supported by: Hobnob and Marvell
Reviewed by: many
Obtained from: Atheros (some bits)
Diffstat (limited to 'sys/net80211/ieee80211_freebsd.c')
-rw-r--r-- | sys/net80211/ieee80211_freebsd.c | 397 |
1 files changed, 331 insertions, 66 deletions
diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c index 1cb7813..d0b1b69 100644 --- a/sys/net80211/ieee80211_freebsd.c +++ b/sys/net80211/ieee80211_freebsd.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 support (FreeBSD-specific code) */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> @@ -41,7 +43,9 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_clone.h> #include <net/if_media.h> +#include <net/if_types.h> #include <net/ethernet.h> #include <net/route.h> @@ -57,24 +61,112 @@ SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, extern int ieee80211_recv_bar_ena; SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena, 0, "BAR frame processing (ena/dis)"); +extern int ieee80211_nol_timeout; +SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW, + &ieee80211_nol_timeout, 0, "NOL timeout (secs)"); +extern int ieee80211_cac_timeout; +SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW, + &ieee80211_cac_timeout, 0, "CAC timeout (secs)"); + +MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state"); + +/* + * Allocate/free com structure in conjunction with ifnet; + * these routines are registered with if_register_com_alloc + * below and are called automatically by the ifnet code + * when the ifnet of the parent device is created. + */ +static void * +wlan_alloc(u_char type, struct ifnet *ifp) +{ + struct ieee80211com *ic; + + ic = malloc(sizeof(struct ieee80211com), M_80211_COM, M_WAITOK|M_ZERO); + ic->ic_ifp = ifp; + + return (ic); +} + +static void +wlan_free(void *ic, u_char type) +{ + free(ic, M_80211_COM); +} -#ifdef IEEE80211_AMPDU_AGE static int -ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS) +wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) { - extern int ieee80211_ampdu_age; - int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age); + struct ieee80211_clone_params cp; + struct ieee80211vap *vap; + struct ieee80211com *ic; + struct ifnet *ifp; int error; - error = sysctl_handle_int(oidp, &du_age, 0, req); + error = copyin(params, &cp, sizeof(cp)); + if (error) + return error; + ifp = ifunit(cp.icp_parent); + if (ifp == NULL) + return ENXIO; + if (ifp->if_type != IFT_IEEE80211) { + if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__); + return EINVAL; + } + ic = ifp->if_l2com; + vap = ic->ic_vap_create(ic, ifc->ifc_name, unit, + cp.icp_opmode, cp.icp_flags, cp.icp_bssid, + cp.icp_flags & IEEE80211_CLONE_MACADDR ? + cp.icp_macaddr : ic->ic_myaddr); + return (vap == NULL ? EIO : 0); +} + +static void +wlan_clone_destroy(struct ifnet *ifp) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + + ic->ic_vap_delete(vap); +} +IFC_SIMPLE_DECLARE(wlan, 0); + +void +ieee80211_vap_destroy(struct ieee80211vap *vap) +{ + ifc_simple_destroy(&wlan_cloner, vap->iv_ifp); +} + +static int +ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS) +{ + int msecs = ticks_to_msecs(*(int *)arg1); + int error, t; + + error = sysctl_handle_int(oidp, &msecs, 0, req); if (error || !req->newptr) return error; - ieee80211_ampdu_age = msecs_to_ticks(ampdu_age); + t = msecs_to_ticks(msecs); + *(int *)arg1 = (t < 1) ? 1 : t; return 0; } -SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0, - ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)"); + +#ifdef IEEE80211_AMPDU_AGE +extern int ieee80211_ampdu_age; +SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLFLAG_RW, + &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I", + "AMPDU max reorder age (ms)"); #endif +extern int ieee80211_addba_timeout; +SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLFLAG_RW, + &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "ADDBA request timeout (ms)"); +extern int ieee80211_addba_backoff; +SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLFLAG_RW, + &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I", + "ADDBA request backoff (ms)"); +extern int ieee80211_addba_maxtries; +SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW, + &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff"); static int ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) @@ -101,6 +193,17 @@ ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) void ieee80211_sysctl_attach(struct ieee80211com *ic) { +} + +void +ieee80211_sysctl_detach(struct ieee80211com *ic) +{ +} + +void +ieee80211_sysctl_vattach(struct ieee80211vap *vap) +{ + struct ifnet *ifp = vap->iv_ifp; struct sysctl_ctx_list *ctx; struct sysctl_oid *oid; char num[14]; /* sufficient for 32 bits */ @@ -108,57 +211,76 @@ ieee80211_sysctl_attach(struct ieee80211com *ic) MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list), M_DEVBUF, M_NOWAIT | M_ZERO); if (ctx == NULL) { - if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n", + if_printf(ifp, "%s: cannot allocate sysctl context!\n", __func__); return; } sysctl_ctx_init(ctx); - snprintf(num, sizeof(num), "%u", ic->ic_vap); + snprintf(num, sizeof(num), "%u", ifp->if_dunit); oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), OID_AUTO, num, CTLFLAG_RD, NULL, ""); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A", - "parent device"); + "%parent", CTLFLAG_RD, vap->iv_ic, 0, + ieee80211_sysctl_parent, "A", "parent device"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0, + "driver capabilities"); #ifdef IEEE80211_DEBUG - ic->ic_debug = ieee80211_debug; + vap->iv_debug = ieee80211_debug; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "debug", CTLFLAG_RW, &ic->ic_debug, 0, + "debug", CTLFLAG_RW, &vap->iv_debug, 0, "control debugging printfs"); #endif + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0, + "consecutive beacon misses before scanning"); /* XXX inherit from tunables */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0, + "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0, ieee80211_sysctl_inact, "I", "station inactivity timeout (sec)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0, + "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0, ieee80211_sysctl_inact, "I", "station inactivity probe timeout (sec)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0, + "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0, ieee80211_sysctl_inact, "I", "station authentication timeout (sec)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0, + "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0, ieee80211_sysctl_inact, "I", "station initial state timeout (sec)"); - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "driver_caps", CTLFLAG_RW, &ic->ic_caps, 0, - "driver capabilities"); - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0, - "consecutive beacon misses before scanning"); - ic->ic_sysctl = ctx; + if (vap->iv_htcaps & IEEE80211_HTC_HT) { + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_bk", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_BK], 0, + "BK traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_be", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_BE], 0, + "BE traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_vo", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_VO], 0, + "VO traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_vi", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_VI], 0, + "VI traffic tx aggr threshold (pps)"); + } + vap->iv_sysctl = ctx; + vap->iv_oid = oid; } void -ieee80211_sysctl_detach(struct ieee80211com *ic) +ieee80211_sysctl_vdetach(struct ieee80211vap *vap) { - if (ic->ic_sysctl != NULL) { - sysctl_ctx_free(ic->ic_sysctl); - FREE(ic->ic_sysctl, M_DEVBUF); - ic->ic_sysctl = NULL; + if (vap->iv_sysctl != NULL) { + sysctl_ctx_free(vap->iv_sysctl); + FREE(vap->iv_sysctl, M_DEVBUF); + vap->iv_sysctl = NULL; } } @@ -190,6 +312,33 @@ ieee80211_drain_ifq(struct ifqueue *ifq) } } +void +ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap) +{ + struct ieee80211_node *ni; + struct mbuf *m, **mprev; + + IF_LOCK(ifq); + mprev = &ifq->ifq_head; + while ((m = *mprev) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (ni != NULL && ni->ni_vap == vap) { + *mprev = m->m_nextpkt; /* remove from list */ + ifq->ifq_len--; + + m_freem(m); + ieee80211_free_node(ni); /* reclaim ref */ + } else + mprev = &m->m_nextpkt; + } + /* recalculate tail ptr */ + m = ifq->ifq_head; + for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt) + ; + ifq->ifq_tail = m; + IF_UNLOCK(ifq); +} + /* * As above, for mbufs allocated with m_gethdr/MGETHDR * or initialized by M_COPY_PKTHDR. @@ -290,66 +439,78 @@ get_random_bytes(void *p, size_t n) } } -void -ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc) +/* + * Helper function for events that pass just a single mac address. + */ +static void +notify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN]) { - struct ifnet *ifp = ic->ic_ifp; struct ieee80211_join_event iev; memset(&iev, 0, sizeof(iev)); - if (ni == ic->ic_bss) { - IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid); - rt_ieee80211msg(ifp, newassoc ? - RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, - &iev, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, mac); + rt_ieee80211msg(ifp, op, &iev, sizeof(iev)); +} + +void +ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join", + (ni == vap->iv_bss) ? "bss " : ""); + + if (ni == vap->iv_bss) { + notify_macaddr(ifp, newassoc ? + RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid); if_link_state_change(ifp, LINK_STATE_UP); } else { - IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); - rt_ieee80211msg(ifp, newassoc ? - RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, - &iev, sizeof(iev)); + notify_macaddr(ifp, newassoc ? + RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr); } } void -ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_notify_node_leave(struct ieee80211_node *ni) { - struct ifnet *ifp = ic->ic_ifp; - struct ieee80211_leave_event iev; + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; - if (ni == ic->ic_bss) { + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave", + (ni == vap->iv_bss) ? "bss " : ""); + + if (ni == vap->iv_bss) { rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); if_link_state_change(ifp, LINK_STATE_DOWN); } else { /* fire off wireless event station leaving */ - memset(&iev, 0, sizeof(iev)); - IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); - rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev)); + notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr); } } void -ieee80211_notify_scan_done(struct ieee80211com *ic) +ieee80211_notify_scan_done(struct ieee80211vap *vap) { - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); /* dispatch wireless event indicating scan completed */ rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); } void -ieee80211_notify_replay_failure(struct ieee80211com *ic, +ieee80211_notify_replay_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const struct ieee80211_key *k, u_int64_t rsc) { - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n", - ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name, - (intmax_t) rsc, (intmax_t) k->wk_keyrsc, + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>", + k->wk_cipher->ic_name, (intmax_t) rsc, + (intmax_t) k->wk_keyrsc[IEEE80211_NONQOS_TID], k->wk_keyix, k->wk_rxkeyix); if (ifp != NULL) { /* NB: for cipher test modules */ @@ -362,22 +523,21 @@ ieee80211_notify_replay_failure(struct ieee80211com *ic, iev.iev_keyix = k->wk_rxkeyix; else iev.iev_keyix = k->wk_keyix; - iev.iev_keyrsc = k->wk_keyrsc; + iev.iev_keyrsc = k->wk_keyrsc[0]; /* XXX need tid */ iev.iev_rsc = rsc; rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); } } void -ieee80211_notify_michael_failure(struct ieee80211com *ic, +ieee80211_notify_michael_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, u_int keyix) { - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] michael MIC verification failed <keyix %u>\n", - ether_sprintf(wh->i_addr2), keyix); - ic->ic_stats.is_rx_tkipmic++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "michael MIC verification failed <keyix %u>", keyix); + vap->iv_stats.is_rx_tkipmic++; if (ifp != NULL) { /* NB: for cipher test modules */ struct ieee80211_michael_event iev; @@ -391,6 +551,107 @@ ieee80211_notify_michael_failure(struct ieee80211com *ic, } void +ieee80211_notify_wds_discover(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr); +} + +void +ieee80211_notify_csa(struct ieee80211com *ic, + const struct ieee80211_channel *c, int mode, int count) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_csa_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + iev.iev_mode = mode; + iev.iev_count = count; + rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev)); +} + +void +ieee80211_notify_radar(struct ieee80211com *ic, + const struct ieee80211_channel *c) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_radar_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev)); +} + +void +ieee80211_notify_cac(struct ieee80211com *ic, + const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_cac_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + iev.iev_type = type; + rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev)); +} + +void +ieee80211_notify_node_deauth(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth"); + + notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr); +} + +void +ieee80211_notify_node_auth(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth"); + + notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr); +} + +void +ieee80211_notify_country(struct ieee80211vap *vap, + const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2]) +{ + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_country_event iev; + + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, bssid); + iev.iev_cc[0] = cc[0]; + iev.iev_cc[1] = cc[1]; + rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev)); +} + +void +ieee80211_notify_radio(struct ieee80211com *ic, int state) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_radio_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_state = state; + rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev)); +} + +void ieee80211_load_module(const char *modname) { @@ -413,8 +674,12 @@ wlan_modevent(module_t mod, int type, void *unused) case MOD_LOAD: if (bootverbose) printf("wlan: <802.11 Link Layer>\n"); + if_clone_attach(&wlan_cloner); + if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free); return 0; case MOD_UNLOAD: + if_deregister_com_alloc(IFT_IEEE80211); + if_clone_detach(&wlan_cloner); return 0; } return EINVAL; |