Index: src/sys/conf/files
===================================================================
RCS file: /usr/dfly/repo/src/sys/conf/files,v
retrieving revision 1.89
diff -u -r1.89 files
--- src/sys/conf/files	21 Apr 2005 14:43:58 -0000	1.89
+++ src/sys/conf/files	22 Apr 2005 19:56:14 -0000
@@ -840,12 +840,18 @@
 net/zlib.c		optional ppp_deflate
 net/zlib.c		optional ipsec
 netproto/802_11/ieee80211.c		optional wlan
+netproto/802_11/ieee80211_acl.c		optional wlan
 netproto/802_11/ieee80211_crypto.c	optional wlan
+netproto/802_11/ieee80211_crypto_none.c	optional wlan
+netproto/802_11/ieee80211_crypto_tkip.c	optional wlan
+netproto/802_11/ieee80211_crypto_wep.c	optional wlan
+netproto/802_11/ieee80211_dragonfly.c	optional wlan
 netproto/802_11/ieee80211_input.c	optional wlan
 netproto/802_11/ieee80211_ioctl.c	optional wlan
 netproto/802_11/ieee80211_node.c	optional wlan
 netproto/802_11/ieee80211_output.c	optional wlan
 netproto/802_11/ieee80211_proto.c	optional wlan
+netproto/802_11/ieee80211_xauth.c	optional wlan
 netproto/atalk/aarp.c		optional netatalk
 netproto/atalk/at_control.c	optional netatalk
 netproto/atalk/at_proto.c	optional netatalk
Index: src/sys/dev/netif/Makefile
===================================================================
RCS file: /usr/dfly/repo/src/sys/dev/netif/Makefile,v
retrieving revision 1.13
diff -u -r1.13 Makefile
--- src/sys/dev/netif/Makefile	6 Mar 2005 18:25:08 -0000	1.13
+++ src/sys/dev/netif/Makefile	22 Apr 2005 20:11:51 -0000
@@ -1,8 +1,8 @@
 # $DragonFly: src/sys/dev/netif/Makefile,v 1.13 2005/03/06 18:25:08 dillon Exp $
 #
 
-SUBDIR= an ar aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue lge lnc \
-	mii_layer my nge nv owi pcn ray re rl sbni sbsh sf sis sk snc \
+SUBDIR= an ar aue axe bfe bge cue dc ed em ep fwe fxp gx kue lge lnc \
+	mii_layer my nge nv owi pcn re rl sbni sbsh sf sis sk snc \
 	sr ste ti tl tx txp vr vx wb wi xe xl
 
 .include <bsd.subdir.mk>
Index: src/sys/dev/netif/wi/if_wi.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/dev/netif/wi/if_wi.c,v
retrieving revision 1.20
diff -u -r1.20 if_wi.c
--- src/sys/dev/netif/wi/if_wi.c	11 Feb 2005 22:25:56 -0000	1.20
+++ src/sys/dev/netif/wi/if_wi.c	22 Apr 2005 20:08:42 -0000
@@ -233,7 +233,7 @@
 {
 	struct wi_softc	*sc = device_get_softc(dev);
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int i, nrates, buflen;
 	u_int16_t val;
 	u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE];
@@ -256,6 +256,7 @@
 		return (error);
 	}
 
+	sc->sc_firmware_type = WI_NOTYPE;
 	sc->wi_cmd_count = 500;
 	/* Reset the NIC. */
 	if (wi_reset(sc) != 0)
@@ -299,10 +300,14 @@
 #endif
 	ifp->if_capenable = ifp->if_capabilities;
 
+	ic->ic_ifp = ifp;
 	ic->ic_phytype = IEEE80211_T_DS;
 	ic->ic_opmode = IEEE80211_M_STA;
-	ic->ic_caps = IEEE80211_C_PMGT | IEEE80211_C_AHDEMO;
 	ic->ic_state = IEEE80211_S_INIT;
+	ic->ic_caps = IEEE80211_C_PMGT
+		    | IEEE80211_C_WEP		/* everyone supports WEP */
+		    ;
+	ic->ic_max_aid = WI_MAX_AID;
 
 	/*
 	 * Query the card for available channels and setup the
@@ -315,11 +320,11 @@
 
 	val <<= 1;			/* shift for base 1 indices */
 	for (i = 1; i < 16; i++) {
-		if (isset((u_int8_t*)&val, i)) {
-			ic->ic_channels[i].ic_freq =
-				ieee80211_ieee2mhz(i, IEEE80211_CHAN_B);
-			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_B;
-		}
+		if (!isset((u_int8_t*)&val, i))
+			continue;
+		ic->ic_channels[i].ic_freq =
+			ieee80211_ieee2mhz(i, IEEE80211_CHAN_B);
+		ic->ic_channels[i].ic_flags = IEEE80211_CHAN_B;
 	}
 
 	/*
@@ -461,15 +466,14 @@
 	/*
 	 * Call MI attach routine.
 	 */
-	ieee80211_ifattach(ifp);
+	ieee80211_ifattach(ic);
 	/* override state transition method */
 	sc->sc_newstate = ic->ic_newstate;
 	ic->ic_newstate = wi_newstate;
-	ieee80211_media_init(ifp, wi_media_change, wi_media_status);
+	ieee80211_media_init(ic, wi_media_change, wi_media_status);
 
 	bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO,
-		sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
-		&sc->sc_drvbpf);
+		sizeof(struct ieee80211_frame) + 64, &sc->sc_drvbpf);
 	/*
 	 * Initialize constant fields.
 	 * XXX make header lengths a multiple of 32-bits so subsequent
@@ -487,6 +491,9 @@
 	sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len);
 	sc->sc_rx_th.wr_ihdr.it_present = htole32(WI_RX_RADIOTAP_PRESENT);
 
+	if (bootverbose)
+		ieee80211_announce(ic);
+
 	return (0);
 }
 
@@ -494,7 +501,7 @@
 wi_detach(device_t dev)
 {
 	struct wi_softc	*sc = device_get_softc(dev);
-	struct ifnet *ifp = &sc->sc_ic.ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	WI_LOCK_DECL();
 
 	WI_LOCK(sc);
@@ -504,13 +511,16 @@
 
 	wi_stop(ifp, 0);
 
-	ieee80211_ifdetach(ifp);
+	bpfdetach(ifp);
+
+	ieee80211_ifdetach(&sc->sc_ic);
 	WI_UNLOCK(sc);
 	bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
 	wi_free(dev);
 	return (0);
 }
 
+
 void
 wi_shutdown(device_t dev)
 {
@@ -559,7 +569,7 @@
 wi_intr(void *arg)
 {
 	struct wi_softc *sc = arg;
-	struct ifnet *ifp = &sc->sc_ic.ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	u_int16_t status;
 	WI_LOCK_DECL();
 
@@ -682,8 +692,9 @@
 	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(sdl));
 	wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN);
 
-	wi_write_val(sc, WI_RID_PM_ENABLED,
-	    (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
+	if (ic->ic_caps & IEEE80211_C_PMGT)
+		wi_write_val(sc, WI_RID_PM_ENABLED,
+		    (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
 
 	/* not yet common 802.11 configuration */
 	wi_write_val(sc, WI_RID_MAX_DATALEN, sc->sc_max_datalen);
@@ -757,9 +768,10 @@
 	ifp->if_flags |= IFF_RUNNING;
 	ifp->if_flags &= ~IFF_OACTIVE;
 	if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
+	    ic->ic_opmode == IEEE80211_M_IBSS ||
 	    ic->ic_opmode == IEEE80211_M_MONITOR ||
 	    ic->ic_opmode == IEEE80211_M_HOSTAP)
-		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+		ieee80211_create_ibss(ic, ic->ic_ibss_chan);
 
 	/* Enable interrupts if not polling */
 #ifdef DEVICE_POLLING
@@ -804,8 +816,8 @@
 void
 wi_stop(struct ifnet *ifp, int disable)
 {
-	struct ieee80211com *ic = (struct ieee80211com *) ifp;
 	struct wi_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
 	WI_LOCK_DECL();
 
 	WI_LOCK(sc);
@@ -847,6 +859,7 @@
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ieee80211_node *ni;
 	struct ieee80211_frame *wh;
+	struct ether_header *eh;
 	struct mbuf *m0;
 	struct wi_frame frmhdr;
 	int cur, fid, off, error;
@@ -896,36 +909,49 @@
 			if (m0 == NULL)
 				break;
 			if (sc->sc_txd[cur].d_len != 0) {
+				IF_PREPEND(&ifp->if_snd, m0);
 				ifp->if_flags |= IFF_OACTIVE;
 				break;
 			}
-			m0 = ifq_dequeue(&ifp->if_snd);
+			if (m0->m_len < sizeof(struct ether_header) &&
+			    (m0 = m_pullup(m0, sizeof(struct ether_header))) == NULL) {
+				ifp->if_oerrors++;
+				continue;
+			}
+			eh = mtod(m0, struct ether_header *);
+			ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+			if (ni == NULL) {
+				m_freem(m0);
+				continue;
+			}
 			ifp->if_opackets++;
 			m_copydata(m0, 0, ETHER_HDR_LEN, 
 			    (caddr_t)&frmhdr.wi_ehdr);
 			BPF_MTAP(ifp, m0);
 
-			m0 = ieee80211_encap(ifp, m0, &ni);
+			m0 = ieee80211_encap(ic, m0, ni);
 			if (m0 == NULL) {
 				ifp->if_oerrors++;
+				ieee80211_free_node(ni);
 				continue;
 			}
                         wh = mtod(m0, struct ieee80211_frame *);
-			if (ic->ic_flags & IEEE80211_F_WEPON)
-				wh->i_fc[1] |= IEEE80211_FC1_WEP;
-
 		}
 
 		if (ic->ic_rawbpf != NULL)
 			bpf_mtap(ic->ic_rawbpf, m0);
 
 		frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
+		/* XXX check key for SWCRYPT instead of using operating mode */
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
 		    (wh->i_fc[1] & IEEE80211_FC1_WEP)) {
-			if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) {
-				ifp->if_oerrors++;
-				if (ni && ni != ic->ic_bss)
-					ieee80211_free_node(ic, ni);
+			struct ieee80211_key *k;
+
+			k = ieee80211_crypto_encap(ic, ni, m0);
+			if (k == NULL) {
+				if (ni != NULL)
+					ieee80211_free_node(ni);
+				m_freem(m0);
 				continue;
 			}
 			frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
@@ -949,8 +975,8 @@
 		error = wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0
 		     || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0;
 		m_freem(m0);
-		if (ni && ni != ic->ic_bss)
-			ieee80211_free_node(ic, ni);
+		if (ni != NULL)
+			ieee80211_free_node(ni);
 		if (error) {
 			ifp->if_oerrors++;
 			continue;
@@ -974,8 +1000,7 @@
 static int
 wi_reset(struct wi_softc *sc)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 #define WI_INIT_TRIES 3
 	int i;
 	int error = 0;
@@ -1053,7 +1078,7 @@
 	}
 
 	/* TODO: rate control */
-	ieee80211_watchdog(ifp);
+	ieee80211_watchdog(&sc->sc_ic);
 }
 
 static int
@@ -1149,7 +1174,7 @@
 					ireq->i_len);
 			break;
 		default:
-			error = ieee80211_ioctl(ifp, cmd, data, cr);
+			error = ieee80211_ioctl(ic, cmd, data, cr);
 			break;
 		}
 		break;
@@ -1179,7 +1204,7 @@
 			sc->sc_nodelen = ireq->i_len;
 			break;
 		default:
-			error = ieee80211_ioctl(ifp, cmd, data, cr);
+			error = ieee80211_ioctl(ic, cmd, data, cr);
 			break;
 		}
 		break;
@@ -1190,7 +1215,7 @@
 			wi_init(sc);
 		break;
 	default:
-		error = ieee80211_ioctl(ifp, cmd, data, cr);
+		error = ieee80211_ioctl(ic, cmd, data, cr);
 		break;
 	}
 	if (error == ENETRESET) {
@@ -1239,23 +1264,22 @@
 	    (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0)
 		imr->ifm_status |= IFM_ACTIVE;
 	len = sizeof(val);
-	if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) != 0)
-		rate = 0;
-	else {
+	if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
+	    len == sizeof(val)) {
 		/* convert to 802.11 rate */
+		val = le16toh(val);
 		rate = val * 2;
 		if (sc->sc_firmware_type == WI_LUCENT) {
-			if (rate == 4 * 2)
+			if (rate == 10)
 				rate = 11;	/* 5.5Mbps */
-			else if (rate == 5 * 2)
-				rate = 22;	/* 11Mbps */
 		} else {
 			if (rate == 4*2)
 				rate = 11;	/* 5.5Mbps */
 			else if (rate == 8*2)
 				rate = 22;	/* 11Mbps */
 		}
-	}
+	} else
+		rate = 0;
 	imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
@@ -1280,7 +1304,7 @@
 {
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ieee80211_node *ni = ic->ic_bss;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 
 	if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid))
 		return;
@@ -1293,17 +1317,25 @@
 	 * change-of-BSSID indications.
 	 */
 	if ((ifp->if_flags & IFF_PROMISC) != 0 &&
-	    sc->sc_false_syns >= WI_MAX_FALSE_SYNS)
+	    !ppsratecheck(&sc->sc_last_syn, &sc->sc_false_syns,
+	                 WI_MAX_FALSE_SYNS))
 		return;
 
-	ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	sc->sc_false_syns = MAX(0, sc->sc_false_syns - 1);
+	/*
+	 * XXX hack; we should create a new node with the new bssid
+	 * and replace the existing ic_bss with it but since we don't
+	 * process management frames to collect state we cheat by
+	 * reusing the existing node as we know wi_newstate will be
+	 * called and it will overwrite the node state.
+	 */
+	ieee80211_sta_join(ic, ieee80211_ref_node(ni));
 }
 
 static void
 wi_rx_monitor(struct wi_softc *sc, int fid)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	struct wi_frame *rx_frame;
 	struct mbuf *m;
 	int datlen, hdrlen;
@@ -1386,7 +1418,7 @@
 wi_rx_intr(struct wi_softc *sc)
 {
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	struct wi_frame frmhdr;
 	struct mbuf *m;
 	struct ieee80211_frame *wh;
@@ -1477,6 +1509,17 @@
 
 	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
 
+	wh = mtod(m, struct ieee80211_frame *);
+	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		/*
+		 * WEP is decrypted by hardware and the IV
+		 * is stripped.  Clear WEP bit so we don't
+		 * try to process it in ieee80211_input.
+		 * XXX fix for TKIP, et. al.
+		 */
+		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+	}
+
 	if (sc->sc_drvbpf) {
 		/* XXX replace divide by table */
 		sc->sc_rx_th.wr_rate = frmhdr.wi_rx_rate / 5;
@@ -1488,14 +1531,6 @@
 		bpf_ptap(sc->sc_drvbpf, m, &sc->sc_rx_th, sc->sc_rx_th_len);
 	}
 
-	wh = mtod(m, struct ieee80211_frame *);
-	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
-		/*
-		 * WEP is decrypted by hardware. Clear WEP bit
-		 * header for ieee80211_input().
-		 */
-		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
-	}
 
 	/* synchronize driver's BSSID with firmware's BSSID */
 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
@@ -1505,36 +1540,25 @@
 	/*
 	 * Locate the node for sender, track state, and
 	 * then pass this node (referenced) up to the 802.11
-	 * layer for its use.  We are required to pass
-	 * something so we fallback to ic_bss when this frame
-	 * is from an unknown sender.
-	 */
-	if (ic->ic_opmode != IEEE80211_M_STA) {
-		ni = ieee80211_find_node(ic, wh->i_addr2);
-		if (ni == NULL)
-			ni = ieee80211_ref_node(ic->ic_bss);
-	} else
-		ni = ieee80211_ref_node(ic->ic_bss);
+	 * layer for its use.
+	 */
+	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *) wh);
 	/*
 	 * Send frame up for processing.
 	 */
-	ieee80211_input(ifp, m, ni, rssi, rstamp);
+	ieee80211_input(ic, m, ni, rssi, rstamp);
 	/*
 	 * The frame may have caused the node to be marked for
 	 * reclamation (e.g. in response to a DEAUTH message)
 	 * so use free_node here instead of unref_node.
 	 */
-	if (ni == ic->ic_bss)
-		ieee80211_unref_node(&ni);
-	else
-		ieee80211_free_node(ic, ni);
+	ieee80211_free_node(ni);
 }
 
 static void
 wi_tx_ex_intr(struct wi_softc *sc)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	struct wi_frame frmhdr;
 	int fid;
 
@@ -1578,8 +1602,7 @@
 static void
 wi_tx_intr(struct wi_softc *sc)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int fid, cur;
 
 	if (sc->wi_gone)
@@ -1615,7 +1638,7 @@
 wi_info_intr(struct wi_softc *sc)
 {
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int i, fid, len, off;
 	u_int16_t ltbuf[2];
 	u_int16_t stat;
@@ -1695,7 +1718,7 @@
 static int
 wi_write_multi(struct wi_softc *sc)
 {
-	struct ifnet *ifp = &sc->sc_ic.ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int n;
 	struct ifmultiaddr *ifma;
 	struct wi_mcast mlist;
@@ -1846,7 +1869,7 @@
 	case WI_RID_TX_CRYPT_KEY:
 	case WI_RID_DEFLT_CRYPT_KEYS:
 	case WI_RID_TX_RATE:
-		return ieee80211_cfgget(ifp, cmd, data, cr);
+		return ieee80211_cfgget(ic, cmd, data, cr);
 
 	case WI_RID_MICROWAVE_OVEN:
 		if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) {
@@ -1900,7 +1923,7 @@
 
 	case WI_RID_READ_APS:
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
-			return ieee80211_cfgget(ifp, cmd, data, cr);
+			return ieee80211_cfgget(ic, cmd, data, cr);
 		if (sc->sc_scan_timer > 0) {
 			error = EINPROGRESS;
 			break;
@@ -1937,11 +1960,11 @@
 		break;
 
 	case WI_RID_READ_CACHE:
-		return ieee80211_cfgget(ifp, cmd, data, cr);
+		return ieee80211_cfgget(ic, cmd, data, cr);
 
 	case WI_RID_SCAN_RES:		/* compatibility interface */
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
-			return ieee80211_cfgget(ifp, cmd, data, cr);
+			return ieee80211_cfgget(ic, cmd, data, cr);
 		if (sc->sc_scan_timer > 0) {
 			error = EINPROGRESS;
 			break;
@@ -2023,7 +2046,7 @@
 			    sc->sc_nodelen);
 			break;
 		default:
-			return ieee80211_cfgget(ifp, cmd, data, cr);
+			return ieee80211_cfgget(ic, cmd, data, cr);
 		}
 		break;
 	}
@@ -2043,6 +2066,7 @@
 	struct mbuf *m;
 	int i, len, error, mif, val;
 	struct ieee80211_rateset *rs;
+	WI_LOCK_DECL();
 
 	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
 	if (error)
@@ -2058,20 +2082,23 @@
 			error = ENOSPC;
 			break;
 		}
-		if (sc->sc_enabled) {
+		WI_LOCK(sc);
+		if (sc->sc_enabled)
 			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
 			    len);
-			if (error)
-				break;
+		if (error == 0) {
+			sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2;
+			memcpy(sc->sc_nodename, &wreq.wi_val[1],
+				sc->sc_nodelen);
 		}
-		sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2;
-		memcpy(sc->sc_nodename, &wreq.wi_val[1], sc->sc_nodelen);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_MICROWAVE_OVEN:
 	case WI_RID_ROAMING_MODE:
 	case WI_RID_SYSTEM_SCALE:
 	case WI_RID_FRAG_THRESH:
+		/* XXX unlocked reads */
 		if (wreq.wi_type == WI_RID_MICROWAVE_OVEN &&
 		    (sc->sc_flags & WI_FLAGS_HAS_MOR) == 0)
 			break;
@@ -2088,11 +2115,14 @@
 	case WI_RID_RTS_THRESH:
 	case WI_RID_CNFAUTHMODE:
 	case WI_RID_MAX_DATALEN:
+		WI_LOCK(sc);
 		if (sc->sc_enabled) {
 			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
 			    sizeof(u_int16_t));
-			if (error)
+			if (error != 0) {
+				WI_UNLOCK(sc);
 				break;
+			}
 		}
 		switch (wreq.wi_type) {
 		case WI_RID_FRAG_THRESH:
@@ -2117,9 +2147,11 @@
 			sc->sc_max_datalen = le16toh(wreq.wi_val[0]);
 			break;
 		}
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_TX_RATE:
+		WI_LOCK(sc);
 		switch (le16toh(wreq.wi_val[0])) {
 		case 3:
 			ic->ic_fixed_rate = -1;
@@ -2131,46 +2163,56 @@
 				    / 2 == le16toh(wreq.wi_val[0]))
 					break;
 			}
-			if (i == rs->rs_nrates)
+			if (i == rs->rs_nrates) {
+				WI_UNLOCK(sc);
 				return EINVAL;
+			}
 			ic->ic_fixed_rate = i;
 		}
 		if (sc->sc_enabled)
 			error = wi_write_txrate(sc);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_SCAN_APS:
+		WI_LOCK(sc);
 		if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
 			error = wi_scan_ap(sc, 0x3fff, 0x000f);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_SCAN_REQ:		/* compatibility interface */
+		WI_LOCK(sc);
 		if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
 			error = wi_scan_ap(sc, wreq.wi_val[0], wreq.wi_val[1]);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_MGMT_XMIT:
-		if (!sc->sc_enabled) {
+		WI_LOCK(sc);
+		if (!sc->sc_enabled)
 			error = ENETDOWN;
-			break;
-		}
-		if (ic->ic_mgtq.ifq_len > 5) {
+		else if (ic->ic_mgtq.ifq_len > 5)
 			error = EAGAIN;
-			break;
-		}
-		/* XXX wi_len looks in u_int8_t, not in u_int16_t */
-		m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0, ifp, NULL);
-		if (m == NULL) {
-			error = ENOMEM;
-			break;
+		else {
+			/* NB: m_devget uses M_DONTWAIT so can hold the lock */
+			/* XXX wi_len looks in u_int8_t, not in u_int16_t */
+			m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0,
+				ifp, NULL);
+			if (m != NULL) {
+				IF_ENQUEUE(&ic->ic_mgtq, m);
+			} else
+				error = ENOMEM;
 		}
-		IF_ENQUEUE(&ic->ic_mgtq, m);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_MIF:
 		mif = wreq.wi_val[0];
 		val = wreq.wi_val[1];
+		WI_LOCK(sc);
 		error = wi_cmd(sc, WI_CMD_WRITEMIF, mif, val, 0);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_PROCFRAME:		/* ignore for compatibility */
@@ -2182,20 +2224,30 @@
 			error = ENOSPC;
 			break;
 		}
+		WI_LOCK(sc);
 		memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
 		ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2;
 		memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen);
-		error = ENETRESET;
+		if (sc->sc_enabled)
+			wi_init(sc);	/* XXX no error return */
+		WI_UNLOCK(sc);
 		break;
 
 	default:
-		if (sc->sc_enabled) {
+		WI_LOCK(sc);
+		if (sc->sc_enabled)
 			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
 			    len);
-			if (error)
-				break;
+		if (error == 0) {
+			/* XXX ieee80211_cfgset does a copyin */
+			error = ieee80211_cfgset(ic, cmd, data);
+			if (error == ENETRESET) {
+				if (sc->sc_enabled)
+					wi_init(sc);
+				error = 0;
+			}
 		}
-		error = ieee80211_cfgset(ifp, cmd, data);
+		WI_UNLOCK(sc);
 		break;
 	}
 	return error;
@@ -2267,16 +2319,18 @@
 
 	switch (sc->sc_firmware_type) {
 	case WI_LUCENT:
-		val = (ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0;
+		val = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
 		error = wi_write_val(sc, WI_RID_ENCRYPTION, val);
 		if (error)
 			break;
-		error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_wep_txkey);
+		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+			break;
+		error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_def_txkey);
 		if (error)
 			break;
 		memset(wkey, 0, sizeof(wkey));
 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
-			keylen = ic->ic_nw_keys[i].wk_len;
+			keylen = ic->ic_nw_keys[i].wk_keylen;
 			wkey[i].wi_keylen = htole16(keylen);
 			memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key,
 			    keylen);
@@ -2287,7 +2341,7 @@
 
 	case WI_INTERSIL:
 	case WI_SYMBOL:
-		if (ic->ic_flags & IEEE80211_F_WEPON) {
+		if (ic->ic_flags & IEEE80211_F_PRIVACY) {
 			/*
 			 * ONLY HWB3163 EVAL-CARD Firmware version
 			 * less than 0.8 variant2
@@ -2319,17 +2373,24 @@
 		error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val);
 		if (error)
 			break;
+		if ((val & PRIVACY_INVOKED) == 0)
+			break;
 		error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY,
-		    ic->ic_wep_txkey);
+		    ic->ic_def_txkey);
 		if (error)
 			break;
+		if (val & HOST_DECRYPT)
+			break;
 		/*
 		 * It seems that the firmware accept 104bit key only if
 		 * all the keys have 104bit length.  We get the length of
 		 * the transmit key and use it for all other keys.
 		 * Perhaps we should use software WEP for such situation.
 		 */
-		keylen = ic->ic_nw_keys[ic->ic_wep_txkey].wk_len;
+		if (ic->ic_def_txkey != IEEE80211_KEYIX_NONE)
+			keylen = ic->ic_nw_keys[ic->ic_def_txkey].wk_keylen;
+		else	/* XXX should not hapen */
+			keylen = IEEE80211_WEP_KEYLEN;
 		if (keylen > IEEE80211_WEP_KEYLEN)
 			keylen = 13;	/* 104bit keys */
 		else
@@ -2555,12 +2616,12 @@
 	for (i = 0; i < WI_TIMEOUT; i++) {
 		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
 			break;
-		if (i == WI_TIMEOUT) {
-			device_printf(sc->sc_dev, "timeout in alloc\n");
-			return ETIMEDOUT;
-		}
 		DELAY(1);
 	}
+	if (i == WI_TIMEOUT) {
+		device_printf(sc->sc_dev, "timeout in alloc\n");
+		return ETIMEDOUT;
+	}
 	*idp = CSR_READ_2(sc, WI_ALLOC_FID);
 	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
 	return 0;
@@ -2619,7 +2680,7 @@
 static int
 wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct wi_softc *sc = ifp->if_softc;
 	struct ieee80211_node *ni = ic->ic_bss;
 	int buflen;
@@ -2631,40 +2692,53 @@
 		ieee80211_state_name[ic->ic_state],
 		ieee80211_state_name[nstate]));
 
+	/*
+	 * Internal to the driver the INIT and RUN states are used
+	 * so bypass the net80211 state machine for other states.
+	 * Beware however that this requires use to net80211 state
+	 * management that otherwise would be handled for us.
+	 */
 	switch (nstate) {
 	case IEEE80211_S_INIT:
-		ic->ic_flags &= ~IEEE80211_F_SIBSS;
 		sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
 		return (*sc->sc_newstate)(ic, nstate, arg);
 
+	case IEEE80211_S_SCAN:
+	case IEEE80211_S_AUTH:
+	case IEEE80211_S_ASSOC:
+		ic->ic_state = nstate;	/* NB: skip normal ieee80211 handling */
+		break;
+
 	case IEEE80211_S_RUN:
+		ni = ic->ic_bss;
 		sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
 		buflen = IEEE80211_ADDR_LEN;
+		IEEE80211_ADDR_COPY(old_bssid, ni->ni_bssid);
 		wi_read_rid(sc, WI_RID_CURRENT_BSSID, ni->ni_bssid, &buflen);
 		IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid);
 		buflen = sizeof(val);
 		wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen);
 		/* XXX validate channel */
 		ni->ni_chan = &ic->ic_channels[le16toh(val)];
+		ic->ic_ibss_chan = ni->ni_chan;
 		sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
 			htole16(ni->ni_chan->ic_freq);
 		sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
 			htole16(ni->ni_chan->ic_flags);
 
-		if (IEEE80211_ADDR_EQ(old_bssid, ni->ni_bssid))
-			sc->sc_false_syns++;
-		else
-			sc->sc_false_syns = 0;
-
-		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-			ni->ni_esslen = ic->ic_des_esslen;
-			memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
-			ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];
-			ni->ni_intval = ic->ic_lintval;
-			ni->ni_capinfo = IEEE80211_CAPINFO_ESS;
-			if (ic->ic_flags & IEEE80211_F_WEPON)
-				ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
-		} else {
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+			/*
+			 * XXX hack; unceremoniously clear 
+			 * IEEE80211_F_DROPUNENC when operating with
+			 * wep enabled so we don't drop unencoded frames
+			 * at the 802.11 layer.  This is necessary because
+			 * we must strip the WEP bit from the 802.11 header
+			 * before passing frames to ieee80211_input because
+			 * the card has already stripped the WEP crypto 
+			 * header from the packet.
+			 */
+			if (ic->ic_flags & IEEE80211_F_PRIVACY)
+				ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
 			/* XXX check return value */
 			buflen = sizeof(ssid);
 			wi_read_rid(sc, WI_RID_CURRENT_SSID, &ssid, &buflen);
@@ -2673,15 +2747,8 @@
 				ni->ni_esslen = IEEE80211_NWID_LEN;	/*XXX*/
 			memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen);
 		}
-		break;
-
-	case IEEE80211_S_SCAN:
-	case IEEE80211_S_AUTH:
-	case IEEE80211_S_ASSOC:
-		break;
+		return (*sc->sc_newstate)(ic, nstate, arg);
 	}
-
-	ic->ic_state = nstate;		/* NB: skip normal ieee80211 handling */
 	return 0;
 }
 
@@ -2698,8 +2765,8 @@
 		(void)wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
 		break;
 	case WI_INTERSIL:
-		val[0] = chanmask;	/* channel */
-		val[1] = txrate;	/* tx rate */
+		val[0] = htole16(chanmask);	/* channel */
+		val[1] = htole16(txrate);	/* tx rate */
 		error = wi_write_rid(sc, WI_RID_SCAN_REQ, val, sizeof(val));
 		break;
 	case WI_SYMBOL:
@@ -2713,7 +2780,7 @@
 	}
 	if (error == 0) {
 		sc->sc_scan_timer = WI_SCAN_WAIT;
-		sc->sc_ic.ic_if.if_timer = 1;
+		sc->sc_if.if_timer = 1;
 		DPRINTF(("wi_scan_ap: start scanning, "
 			"chamask 0x%x txrate 0x%x\n", chanmask, txrate));
 	}
Index: src/sys/dev/netif/wi/if_wi_pccard.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/dev/netif/wi/if_wi_pccard.c,v
retrieving revision 1.6
diff -u -r1.6 if_wi_pccard.c
--- src/sys/dev/netif/wi/if_wi_pccard.c	6 Sep 2004 13:52:24 -0000	1.6
+++ src/sys/dev/netif/wi/if_wi_pccard.c	22 Apr 2005 20:08:42 -0000
@@ -137,8 +137,11 @@
 	PCMCIA_CARD(GEMTEK, WLAN, 0),
 	PCMCIA_CARD(HWN, AIRWAY80211, 0), 
 	PCMCIA_CARD(INTEL, PRO_WLAN_2011, 0),
-	PCMCIA_CARD(INTERSIL, MA401RA, 0),
-	PCMCIA_CARD(INTERSIL, DWL650, 0),
+#if 0
+	PCMCIA_CARD(INTERSIL, ISL37100P, 0),
+	PCMCIA_CARD(INTERSIL, ISL37110P, 0),
+	PCMCIA_CARD(INTERSIL, ISL37300P, 0),
+#endif
 	PCMCIA_CARD(INTERSIL2, PRISM2, 0),
 	PCMCIA_CARD(IODATA2, WCF12, 0),
 	PCMCIA_CARD(IODATA2, WNB11PCM, 0),
@@ -147,7 +150,9 @@
 	PCMCIA_CARD(MICROSOFT, MN_520, 0),
 	PCMCIA_CARD(NOKIA, C020_WLAN, 0),
 	PCMCIA_CARD(NOKIA, C110_WLAN, 0),
-	PCMCIA_CARD(PLANEX_2, GWNS11H, 0),
+#if 0
+	PCMCIA_CARD(PLANEX, GWNS11H, 0),
+#endif
 	PCMCIA_CARD(PROXIM, HARMONY, 0),
 	PCMCIA_CARD(PROXIM, RANGELANDS_8430, 0),
 	PCMCIA_CARD(SAMSUNG, SWL_2000N, 0),
@@ -164,7 +169,7 @@
 	device_t	dev;
 {
 	const struct pccard_product *pp;
-	u_int32_t fcn;
+	u_int32_t fcn = PCCARD_FUNCTION_UNSPEC;
 
 	/* Make sure we're a network driver */
 	fcn = pccard_get_function(dev);
@@ -195,12 +200,12 @@
 	if (error)
 		return (error);
 
-	wi_free(dev);
-
 	/* Make sure interrupts are disabled. */
 	CSR_WRITE_2(sc, WI_INT_EN, 0);
 	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
 
+	wi_free(dev);
+
 	return (0);
 }
 
@@ -234,6 +239,7 @@
 		    spectrum24t_primsym, sizeof(spectrum24t_primsym),
 		    spectrum24t_secsym, sizeof(spectrum24t_secsym))) {
 			device_printf(dev, "couldn't load firmware\n");
+			return (ENXIO);
 		}
 #else
 		device_printf(dev, 
Index: src/sys/dev/netif/wi/if_wi_pci.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/dev/netif/wi/if_wi_pci.c,v
retrieving revision 1.5
diff -u -r1.5 if_wi_pci.c
--- src/sys/dev/netif/wi/if_wi_pci.c	6 Sep 2004 13:52:24 -0000	1.5
+++ src/sys/dev/netif/wi/if_wi_pci.c	22 Apr 2005 20:08:42 -0000
@@ -248,10 +248,9 @@
 static int
 wi_pci_suspend(device_t dev)
 {
-	struct wi_softc		*sc;
-	struct ifnet *ifp;
-	sc = device_get_softc(dev);
-	ifp = &sc->sc_if;
+	struct wi_softc	*sc = device_get_softc(dev);
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = ic->ic_ifp;
 
 	wi_stop(ifp, 1);
 	
@@ -261,10 +260,9 @@
 static int
 wi_pci_resume(device_t dev)
 {
-	struct wi_softc *sc;
-	struct ifnet *ifp;
-	sc = device_get_softc(dev);
-	ifp = &sc->sc_if;
+	struct wi_softc	*sc = device_get_softc(dev);
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = ic->ic_ifp;
 
 	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE)
 		return (0);
Index: src/sys/dev/netif/wi/if_wivar.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/dev/netif/wi/if_wivar.h,v
retrieving revision 1.5
diff -u -r1.5 if_wivar.h
--- src/sys/dev/netif/wi/if_wivar.h	6 Sep 2004 13:52:24 -0000	1.5
+++ src/sys/dev/netif/wi/if_wivar.h	22 Apr 2005 20:08:42 -0000
@@ -60,7 +60,10 @@
 #define WI_RID_ROAMING_MODE	0xFC2D
 #define WI_RID_CUR_TX_RATE	0xFD44 /* current TX rate */
 
+#define	WI_MAX_AID		256	/* max stations for ap operation */
+
 struct wi_softc	{
+	struct arpcom		sc_arp;
 	struct ieee80211com	sc_ic;
 	int			(*sc_newstate)(struct ieee80211com *,
 					enum ieee80211_state, int);
@@ -158,6 +161,7 @@
 		u_int16_t               wi_confbits_param0;
 	} wi_debug;
 
+	struct timeval		sc_last_syn;
 	int			sc_false_syns;
 
 	u_int16_t		sc_txbuf[IEEE80211_MAX_LEN/2];
@@ -173,7 +177,7 @@
 	} u_rx_rt;
 	int			sc_rx_th_len;
 };
-#define	sc_if			sc_ic.ic_if
+#define	sc_if			sc_arp.ac_if
 #define	sc_tx_th		u_tx_rt.th
 #define	sc_rx_th		u_rx_rt.th
 
Index: src/sys/net/ethernet.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/net/ethernet.h,v
retrieving revision 1.11
diff -u -r1.11 ethernet.h
--- src/sys/net/ethernet.h	11 Feb 2005 22:25:57 -0000	1.11
+++ src/sys/net/ethernet.h	22 Apr 2005 19:54:07 -0000
@@ -326,6 +326,7 @@
 #define	ETHERTYPE_PPPOEDISC	0x8863	/* PPP Over Ethernet Discovery Stage */
 #define	ETHERTYPE_PPPOE		0x8864	/* PPP Over Ethernet Session Stage */
 #define	ETHERTYPE_LANPROBE	0x8888	/* HP LanProbe test? */
+#define	ETHERTYPE_PAE		0x888e	/* EAPOL PAE/802.1x */
 #define	ETHERTYPE_LOOPBACK	0x9000	/* Loopback: used to test interfaces */
 #define	ETHERTYPE_LBACK		ETHERTYPE_LOOPBACK	/* DEC MOP loopback */
 #define	ETHERTYPE_XNSSM		0x9001	/* 3Com (Formerly Bridge Communications), XNS Systems Management */
@@ -348,6 +349,7 @@
 
 #define	ETHERMTU	(ETHER_MAX_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN)
 #define	ETHERMIN	(ETHER_MIN_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN)
+#define	ETHERMTU_JUMBO	(ETHER_MAX_LEN_JUMBO - ETHER_HDR_LEN - ETHER_CRC_LEN)
 
 #ifdef _KERNEL
 
Index: src/sys/net/if.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/net/if.h,v
retrieving revision 1.12
diff -u -r1.12 if.h
--- src/sys/net/if.h	21 Dec 2004 02:54:14 -0000	1.12
+++ src/sys/net/if.h	22 Apr 2005 19:54:07 -0000
@@ -142,6 +142,9 @@
 #define	IFF_MULTICAST	0x8000		/* supports multicast */
 #define	IFF_POLLING	0x10000		/* Interface is in polling mode. */
 #define	IFF_PPROMISC	0x20000		/* user-requested promisc mode */
+#define IFF_MONITOR     0x40000         /* user-requested monitor mode */
+#define IFF_STATICARP   0x80000         /* static ARP */
+#define IFF_NEEDSGIANT  0x100000        /* hold Giant over if_start calls */
 
 /* flags set internally only: */
 #define	IFF_CANTCHANGE \
Index: src/sys/net/if_media.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/net/if_media.h,v
retrieving revision 1.8
diff -u -r1.8 if_media.h
--- src/sys/net/if_media.h	14 Feb 2005 16:21:34 -0000	1.8
+++ src/sys/net/if_media.h	22 Apr 2005 19:54:07 -0000
@@ -205,6 +205,24 @@
 #define	IFM_IEEE80211_FH	0x00040000	/* 2Ghz, GFSK mode */
 
 /*
+ * ATM
+ */
+#define IFM_ATM 		0x000000a0
+#define IFM_ATM_UNKNOWN		3
+#define IFM_ATM_UTP_25		4
+#define IFM_ATM_TAXI_100	5
+#define IFM_ATM_TAXI_140	6
+#define IFM_ATM_MM_155		7
+#define IFM_ATM_SM_155		8
+#define IFM_ATM_UTP_155		9
+#define IFM_ATM_MM_622		10
+#define IFM_ATM_SM_622		11
+#define IFM_ATM_VIRTUAL		12
+#define IFM_ATM_SDH		0x00000100      /* SDH instead of SONET */
+#define IFM_ATM_NOSCRAMB	0x00000200      /* no scrambling */
+#define IFM_ATM_UNASSIGNED	0x00000400      /* unassigned cells */
+
+/*
  * Shared media sub-types
  */
 #define	IFM_AUTO	0		/* Autoselect best media */
@@ -429,6 +447,56 @@
 	{ 0, NULL },							\
 }
 
+#define IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS {                       \
+        { IFM_AUTO, "autoselect" },                                     \
+        { IFM_IEEE80211_11A, "11a" },                                   \
+        { IFM_IEEE80211_11B, "11b" },                                   \
+        { IFM_IEEE80211_11G, "11g" },                                   \
+        { IFM_IEEE80211_FH, "fh" },                                     \
+        { 0, NULL },                                                    \
+}
+
+#define IFM_SUBTYPE_IEEE80211_MODE_ALIASES {                            \
+        { IFM_AUTO, "auto" },                                           \
+        { 0, NULL },                                                    \
+}
+
+# define IFM_SUBTYPE_ATM_DESCRIPTIONS {                                 \
+        { IFM_ATM_UNKNOWN,      "Unknown" },                            \
+        { IFM_ATM_UTP_25,       "UTP/25.6MBit" },                       \
+        { IFM_ATM_TAXI_100,     "Taxi/100MBit" },                       \
+        { IFM_ATM_TAXI_140,     "Taxi/140MBit" },                       \
+        { IFM_ATM_MM_155,       "Multi-mode/155MBit" },                 \
+        { IFM_ATM_SM_155,       "Single-mode/155MBit" },                \
+        { IFM_ATM_UTP_155,      "UTP/155MBit" },                        \
+        { IFM_ATM_MM_622,       "Multi-mode/622MBit" },                 \
+        { IFM_ATM_SM_622,       "Single-mode/622MBit" },                \
+        { IFM_ATM_VIRTUAL,      "Virtual" },                            \
+        { 0, NULL },                                                    \
+}
+
+# define IFM_SUBTYPE_ATM_ALIASES {                                      \
+        { IFM_ATM_UNKNOWN,      "UNKNOWN" },                            \
+        { IFM_ATM_UTP_25,       "UTP-25" },                             \
+        { IFM_ATM_TAXI_100,     "TAXI-100" },                           \
+        { IFM_ATM_TAXI_140,     "TAXI-140" },                           \
+        { IFM_ATM_MM_155,       "MM-155" },                             \
+        { IFM_ATM_SM_155,       "SM-155" },                             \
+        { IFM_ATM_UTP_155,      "UTP-155" },                            \
+        { IFM_ATM_MM_622,       "MM-622" },                             \
+        { IFM_ATM_SM_622,       "SM-622" },                             \
+        { IFM_ATM_VIRTUAL,      "VIRTUAL" },                            \
+        { 0, NULL },                                                    \
+}
+
+#define IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS {                           \
+         { IFM_ATM_SDH, "SDH" },                                         \
+         { IFM_ATM_NOSCRAMB, "Noscramb" },                               \
+         { IFM_ATM_UNASSIGNED, "Unassigned" },                           \
+         { 0, NULL },                                                    \
+}
+
+
 #define	IFM_SUBTYPE_SHARED_DESCRIPTIONS {				\
 	{ IFM_AUTO,	"autoselect" },					\
 	{ IFM_MANUAL,	"manual" },					\
Index: src/sys/net/if_var.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/net/if_var.h,v
retrieving revision 1.24
diff -u -r1.24 if_var.h
--- src/sys/net/if_var.h	11 Feb 2005 22:25:57 -0000	1.24
+++ src/sys/net/if_var.h	22 Apr 2005 19:54:07 -0000
@@ -78,6 +78,9 @@
 struct	ucred;
 #endif
 
+#include <machine/thread.h>
+#include <sys/thread.h>
+
 #include <sys/queue.h>		/* get TAILQ macros */
 
 #include <net/altq/if_altq.h>
@@ -104,6 +107,7 @@
 	int	ifq_len;
 	int	ifq_maxlen;
 	int	ifq_drops;
+	struct  lwkt_token ifq_mtx;
 };
 
 /*
Index: src/sys/net/ifq_var.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/net/ifq_var.h,v
retrieving revision 1.3
diff -u -r1.3 ifq_var.h
--- src/sys/net/ifq_var.h	4 Apr 2005 17:08:16 -0000	1.3
+++ src/sys/net/ifq_var.h	22 Apr 2005 19:54:07 -0000
@@ -47,13 +47,13 @@
 }
 #else
 static __inline int
-ifq_is_enabled(struct ifaltq *_ifq)
+ifq_is_enabled(struct ifaltq *_ifq __unused)
 {
 	return(0);
 }
 
 static __inline int
-ifq_is_attached(struct ifaltq *_ifq)
+ifq_is_attached(struct ifaltq *_ifq __unused)
 {
 	return(0);
 }
Index: src/sys/net/route.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/net/route.h,v
retrieving revision 1.13
diff -u -r1.13 route.h
--- src/sys/net/route.h	4 Mar 2005 02:54:31 -0000	1.13
+++ src/sys/net/route.h	22 Apr 2005 19:54:07 -0000
@@ -262,6 +262,7 @@
 #define	RTM_NEWMADDR	0xf	/* mcast group membership being added to if */
 #define	RTM_DELMADDR	0x10	/* mcast group membership being deleted */
 #define	RTM_IFANNOUNCE	0x11	/* iface arrival/departure */
+#define RTM_IEEE80211   0x12    /* IEEE80211 wireless event */
 
 /*
  * Bitmask values for rtm_inits and rmx_locks.
@@ -327,6 +328,7 @@
 void	 route_init (void);
 void	 rt_dstmsg(int type, struct sockaddr *dst, int error);
 int	 rt_getifa (struct rt_addrinfo *);
+void     rt_ieee80211msg(struct ifnet *, int, void *, size_t);
 void	 rt_ifannouncemsg (struct ifnet *, int);
 void	 rt_ifmsg (struct ifnet *);
 int	 rt_llroute (struct sockaddr *dst, struct rtentry *rt0,
Index: src/sys/net/rtsock.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/net/rtsock.c,v
retrieving revision 1.25
diff -u -r1.25 rtsock.c
--- src/sys/net/rtsock.c	18 Apr 2005 23:50:30 -0000	1.25
+++ src/sys/net/rtsock.c	22 Apr 2005 19:54:07 -0000
@@ -688,6 +688,7 @@
 	case RTM_IFINFO:
 		return sizeof(struct if_msghdr);
 	case RTM_IFANNOUNCE:
+        case RTM_IEEE80211:
 		return sizeof(struct if_announcemsghdr);
 	default:
 		return sizeof(struct rt_msghdr);
@@ -996,6 +997,59 @@
 
 /*
  * This is called to generate routing socket messages indicating
+ * IEEE80211 wireless events.
+ * XXX we piggyback on the RTM_IFANNOUNCE msg format in a clumsy way.
+ */
+void
+rt_ieee80211msg(struct ifnet *ifp, int what, void *data, size_t data_len)
+{
+       struct mbuf *m;
+       struct rt_addrinfo info;
+
+       struct rt_addrinfo addrinfo;
+       struct if_announcemsghdr *ifan;
+
+        if (route_cb.any_count == 0)
+                return;
+
+        bzero(&addrinfo, sizeof addrinfo);
+        m = rt_msg_mbuf(RTM_IEEE80211, &addrinfo);
+        if (m == NULL)
+                return;
+
+        ifan = mtod(m, struct if_announcemsghdr *);
+        ifan->ifan_index = ifp->if_index;
+        strlcpy(ifan->ifan_name, ifp->if_xname, sizeof ifan->ifan_name);
+        ifan->ifan_what = what;
+
+	/*
+	 * Append the ieee80211 data.  Try to stick it in the
+	 * mbuf containing the ifannounce msg; otherwise allocate
+	 * a new mbuf and append.
+	 *
+	 * NB: we assume m is a single mbuf.
+	 */
+	if (data_len > M_TRAILINGSPACE(m)) {
+		struct mbuf *n = m_get(M_NOWAIT, MT_DATA);
+		if (n == NULL) {
+			m_freem(m);
+			return;
+		}
+		bcopy(data, mtod(n, void *), data_len);
+		n->m_len = data_len;
+		m->m_next = n;
+	} else if (data_len > 0) {
+		bcopy(data, mtod(m, u_int8_t *) + m->m_len, data_len);
+		m->m_len += data_len;
+	}
+	if (m->m_flags & M_PKTHDR)
+		m->m_pkthdr.len += data_len;
+	mtod(m, struct if_announcemsghdr *)->ifan_msglen += data_len;
+	rts_input(m, 0);
+}
+
+/*
+ * This is called to generate routing socket messages indicating
  * network interface arrival and departure.
  */
 void
Index: src/sys/netproto/802_11/Makefile
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/Makefile,v
retrieving revision 1.4
diff -u -r1.4 Makefile
--- src/sys/netproto/802_11/Makefile	18 Feb 2005 11:41:42 -0000	1.4
+++ src/sys/netproto/802_11/Makefile	22 Apr 2005 19:56:56 -0000
@@ -1,8 +1,14 @@
 # $DragonFly: src/sys/netproto/802_11/Makefile,v 1.4 2005/02/18 11:41:42 corecode Exp $
 
 KMOD	= wlan
-SRCS	= ieee80211.c ieee80211_crypto.c ieee80211_input.c ieee80211_ioctl.c
-SRCS	+= ieee80211_node.c ieee80211_output.c ieee80211_proto.c
+SRCS     = ieee80211_input.c ieee80211_ioctl.c ieee80211_node.c
+SRCS    += ieee80211.c ieee80211_acl.c ieee80211_crypto.c
+SRCS    += ieee80211_output.c ieee80211_proto.c ieee80211_xauth.c
+SRCS    += ieee80211_crypto_none.c ieee80211_crypto_tkip.c ieee80211_crypto_wep.c
+SRCS    += ieee80211_dragonfly.c
+
+#SRCS   += ieee80211_crypto_ccmp.c
+
 SRCS	+= bus_if.h device_if.h opt_inet.h opt_ipx.h
 
 .if !defined(BUILDING_WITH_KERNEL)
Index: src/sys/netproto/802_11/ieee80211.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211.c,v
retrieving revision 1.3
diff -u -r1.3 ieee80211.c
--- src/sys/netproto/802_11/ieee80211.c	26 Jan 2005 00:37:40 -0000	1.3
+++ src/sys/netproto/802_11/ieee80211.c	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,79 +28,113 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/net80211/ieee80211.c,v 1.13 2004/05/30 17:57:45 phk Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211.c,v 1.3 2005/01/26 00:37:40 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 /*
  * IEEE 802.11 generic handler
  */
 
-#include "opt_inet.h"
-
 #include <sys/param.h>
 #include <sys/systm.h> 
-#include <sys/mbuf.h>   
-#include <sys/malloc.h>
 #include <sys/kernel.h>
-#include <sys/module.h>
+ 
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
 
-#include <machine/atomic.h>
- 
+#include <sys/thread.h>
+#include <sys/thread2.h>
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
 #include <net/ethernet.h>
-#include <net/if_llc.h>
-#include <net/route.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
 #include <net/bpf.h>
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
-
-#ifdef IEEE80211_DEBUG
-int	ieee80211_debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug,
-	    0, "IEEE 802.11 media debugging printfs");
-#endif
-
-static void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
-		enum ieee80211_phymode);
-
-static const char *ieee80211_phymode_name[] = {
+const char *ieee80211_phymode_name[] = {
 	"auto",		/* IEEE80211_MODE_AUTO */
 	"11a",		/* IEEE80211_MODE_11A */
 	"11b",		/* IEEE80211_MODE_11B */
 	"11g",		/* IEEE80211_MODE_11G */
 	"FH",		/* IEEE80211_MODE_FH */
-	"turbo",	/* IEEE80211_MODE_TURBO */
+	"turboA",	/* IEEE80211_MODE_TURBO_A */
+	"turboG",	/* IEEE80211_MODE_TURBO_G */
 };
 
+/* list of all instances */
+SLIST_HEAD(ieee80211_list, ieee80211com);
+static struct ieee80211_list ieee80211_list =
+	SLIST_HEAD_INITIALIZER(ieee80211_list);
+static u_int8_t ieee80211_vapmap[32];		/* enough for 256 */
+static struct lwkt_token ieee80211_vap_mtx;
+static int vap_initted = 0;
+#if 0
+MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
+#endif
+
+static void
+ieee80211_add_vap(struct ieee80211com *ic)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	int i;
+	u_int8_t b;
+        struct lwkt_tokref tokinfo;
+
+	lwkt_gettoken(&tokinfo, &ieee80211_vap_mtx);
+	ic->ic_vap = 0;
+	for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++)
+		ic->ic_vap += NBBY;
+	if (i == N(ieee80211_vapmap))
+		panic("vap table full");
+	for (b = ieee80211_vapmap[i]; b & 1; b >>= 1)
+		ic->ic_vap++;
+	setbit(ieee80211_vapmap, ic->ic_vap);
+	SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next);
+	lwkt_reltoken(&tokinfo);
+#undef N
+}
+
+static void
+ieee80211_remove_vap(struct ieee80211com *ic)
+{
+        struct lwkt_tokref tokinfo;
+	lwkt_gettoken(&tokinfo, &ieee80211_vap_mtx);
+	SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next);
+	KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY,
+		("invalid vap id %d", ic->ic_vap));
+	KASSERT(isset(ieee80211_vapmap, ic->ic_vap),
+		("vap id %d not allocated", ic->ic_vap));
+	clrbit(ieee80211_vapmap, ic->ic_vap);
+	lwkt_reltoken(&tokinfo);
+}
+
+/*
+ * Default reset method for use with the ioctl support.  This
+ * method is invoked after any state change in the 802.11
+ * layer that should be propagated to the hardware but not
+ * require re-initialization of the 802.11 state machine (e.g
+ * rescanning for an ap).  We always return ENETRESET which
+ * should cause the driver to re-initialize the device. Drivers
+ * can override this method to implement more optimized support.
+ */
+static int
+ieee80211_default_reset(struct ifnet *ifp)
+{
+	return ENETRESET;
+}
+
 void
-ieee80211_ifattach(struct ifnet *ifp)
+ieee80211_ifattach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_channel *c;
 	int i;
 
 	ether_ifattach(ifp, ic->ic_myaddr);
 	bpfattach_dlt(ifp, DLT_IEEE802_11,
 	    sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
-	ieee80211_crypto_attach(ifp);
+	ieee80211_crypto_attach(ic);
 
 	/*
 	 * Fill in 802.11 available channel set, mark
@@ -135,33 +169,69 @@
 			if (IEEE80211_IS_CHAN_FHSS(c))
 				ic->ic_modecaps |= 1<<IEEE80211_MODE_FH;
 			if (IEEE80211_IS_CHAN_T(c))
-				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO;
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A;
+			if (IEEE80211_IS_CHAN_108G(c))
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G;
 		}
 	}
 	/* validate ic->ic_curmode */
 	if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0)
 		ic->ic_curmode = IEEE80211_MODE_AUTO;
 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
-
+#if 0
+	/*
+	 * Enable WME by default if we're capable.
+	 */
+	if (ic->ic_caps & IEEE80211_C_WME)
+		ic->ic_flags |= IEEE80211_F_WME;
+#endif
 	(void) ieee80211_setmode(ic, ic->ic_curmode);
 
 	if (ic->ic_lintval == 0)
-		ic->ic_lintval = 100;		/* default sleep */
+		ic->ic_lintval = IEEE80211_BINTVAL_DEFAULT;
 	ic->ic_bmisstimeout = 7*ic->ic_lintval;	/* default 7 beacons */
+	ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
+	IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
+
+	ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
+
+	ieee80211_node_attach(ic);
+	ieee80211_proto_attach(ic);
 
-	ieee80211_node_attach(ifp);
-	ieee80211_proto_attach(ifp);
+	crit_enter();
+	if (!vap_initted) {
+		vap_initted = 1;
+		lwkt_token_init(&ieee80211_vap_mtx);
+	}
+	crit_exit();
+	ieee80211_add_vap(ic);
+
+	ieee80211_sysctl_attach(ic);		/* NB: requires ic_vap */
+
+	/*
+	 * Install a default reset method for the ioctl support.
+	 * The driver is expected to fill this in before calling us.
+	 */
+	if (ic->ic_reset == NULL)
+		ic->ic_reset = ieee80211_default_reset;
 }
 
 void
-ieee80211_ifdetach(struct ifnet *ifp)
+ieee80211_ifdetach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 
-	ieee80211_proto_detach(ifp);
-	ieee80211_crypto_detach(ifp);
-	ieee80211_node_detach(ifp);
+	ieee80211_remove_vap(ic);
+
+	ieee80211_sysctl_detach(ic);
+	ieee80211_proto_detach(ic);
+	ieee80211_crypto_detach(ic);
+	ieee80211_node_detach(ic);
 	ifmedia_removeall(&ic->ic_media);
+
+	IEEE80211_BEACON_LOCK_DESTROY(ic);
+
+	bpfdetach(ifp);
 	ether_ifdetach(ifp);
 }
 
@@ -202,11 +272,11 @@
 	else if (c == IEEE80211_CHAN_ANYC)
 		return IEEE80211_CHAN_ANY;
 	else if (c != NULL) {
-		if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n",
+		if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n",
 			c->ic_freq, c->ic_flags);
 		return 0;		/* XXX */
 	} else {
-		if_printf(&ic->ic_if, "invalid channel (NULL)\n");
+		if_printf(ic->ic_ifp, "invalid channel (NULL)\n");
 		return 0;		/* XXX */
 	}
 }
@@ -243,13 +313,13 @@
  * ieee80211_attach and before most anything else.
  */
 void
-ieee80211_media_init(struct ifnet *ifp,
+ieee80211_media_init(struct ieee80211com *ic,
 	ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
 {
 #define	ADD(_ic, _s, _o) \
 	ifmedia_add(&(_ic)->ic_media, \
 		IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ifmediareq imr;
 	int i, j, mode, rate, maxrate, mword, mopt, r;
 	struct ieee80211_rateset *rs;
@@ -259,7 +329,7 @@
 	 * Do late attach work that must wait for any subclass
 	 * (i.e. driver) work such as overriding methods.
 	 */
-	ieee80211_node_lateattach(ifp);
+	ieee80211_node_lateattach(ic);
 
 	/*
 	 * Fill in media characteristics.
@@ -275,6 +345,7 @@
 			IFM_IEEE80211_11G,
 			IFM_IEEE80211_FH,
 			IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+			IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
 		};
 		if ((ic->ic_modecaps & (1<<mode)) == 0)
 			continue;
@@ -290,16 +361,12 @@
 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
 		if (mode == IEEE80211_MODE_AUTO)
 			continue;
-		if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
 		rs = &ic->ic_sup_rates[mode];
 		for (i = 0; i < rs->rs_nrates; i++) {
 			rate = rs->rs_rates[i];
 			mword = ieee80211_rate2media(ic, rate, mode);
 			if (mword == 0)
 				continue;
-			printf("%s%d%sMbps", (i != 0 ? " " : ""),
-			    (rate & IEEE80211_RATE_VAL) / 2,
-			    ((rate & 0x1) != 0 ? ".5" : ""));
 			ADD(ic, mword, mopt);
 			if (ic->ic_caps & IEEE80211_C_IBSS)
 				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
@@ -325,7 +392,6 @@
 			if (rate > maxrate)
 				maxrate = rate;
 		}
-		printf("\n");
 	}
 	for (i = 0; i < allrates.rs_nrates; i++) {
 		mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
@@ -351,6 +417,31 @@
 #undef ADD
 }
 
+void
+ieee80211_announce(struct ieee80211com *ic)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	int i, mode, rate, mword;
+	struct ieee80211_rateset *rs;
+
+	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
+		if ((ic->ic_modecaps & (1<<mode)) == 0)
+			continue;
+		if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
+		rs = &ic->ic_sup_rates[mode];
+		for (i = 0; i < rs->rs_nrates; i++) {
+			rate = rs->rs_rates[i];
+			mword = ieee80211_rate2media(ic, rate, mode);
+			if (mword == 0)
+				continue;
+			printf("%s%d%sMbps", (i != 0 ? " " : ""),
+			    (rate & IEEE80211_RATE_VAL) / 2,
+			    ((rate & 0x1) != 0 ? ".5" : ""));
+		}
+		printf("\n");
+	}
+}
+
 static int
 findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
 {
@@ -365,17 +456,50 @@
 }
 
 /*
+ * Find an instance by it's mac address.
+ */
+struct ieee80211com *
+ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct ieee80211com *ic;
+
+	/* XXX lock */
+	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
+		if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr))
+			return ic;
+	return NULL;
+}
+
+static struct ieee80211com *
+ieee80211_find_instance(struct ifnet *ifp)
+{
+	struct ieee80211com *ic;
+
+	/* XXX lock */
+	/* XXX not right for multiple instances but works for now */
+	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
+		if (ic->ic_ifp == ifp)
+			return ic;
+	return NULL;
+}
+
+/*
  * Handle a media change request.
  */
 int
 ieee80211_media_change(struct ifnet *ifp)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ieee80211com *ic;
 	struct ifmedia_entry *ime;
 	enum ieee80211_opmode newopmode;
 	enum ieee80211_phymode newphymode;
 	int i, j, newrate, error = 0;
 
+	ic = ieee80211_find_instance(ifp);
+	if (!ic) {
+		if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
+		return EINVAL;
+	}
 	ime = ic->ic_media.ifm_cur;
 	/*
 	 * First, identify the phy mode.
@@ -400,13 +524,16 @@
 		return EINVAL;
 	}
 	/*
-	 * Turbo mode is an ``option''.  Eventually it
-	 * needs to be applied to 11g too.
+	 * Turbo mode is an ``option''.
+	 * XXX does not apply to AUTO
 	 */
 	if (ime->ifm_media & IFM_IEEE80211_TURBO) {
-		if (newphymode != IEEE80211_MODE_11A)
+		if (newphymode == IEEE80211_MODE_11A)
+			newphymode = IEEE80211_MODE_TURBO_A;
+		else if (newphymode == IEEE80211_MODE_11G)
+			newphymode = IEEE80211_MODE_TURBO_G;
+		else
 			return EINVAL;
-		newphymode = IEEE80211_MODE_TURBO;
 	}
 	/*
 	 * Validate requested mode is available.
@@ -513,14 +640,15 @@
 			break;
 		case IEEE80211_M_IBSS:
 			ic->ic_flags |= IEEE80211_F_IBSSON;
-#ifdef notdef
-			if (ic->ic_curmode == IEEE80211_MODE_11G)
-				ieee80211_set11gbasicrates(
-					&ic->ic_suprates[newphymode],
-					IEEE80211_MODE_11B);
-#endif
 			break;
 		}
+		/*
+		 * Yech, slot time may change depending on the
+		 * operating mode so reset it to be sure everything
+		 * is setup appropriately.
+		 */
+		ieee80211_reset_erp(ic);
+		ieee80211_wme_initparams(ic);	/* after opmode change */
 		error = ENETRESET;
 	}
 #ifdef notdef
@@ -533,26 +661,39 @@
 void
 ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
 {
-	struct ieee80211com *ic = (void *)ifp;
-	struct ieee80211_node *ni = NULL;
-	int old_status = imr->ifm_status;
+	struct ieee80211com *ic;
+	struct ieee80211_rateset *rs;
 
+	ic = ieee80211_find_instance(ifp);
+	if (!ic) {
+		if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
+		return;
+	}
 	imr->ifm_status = IFM_AVALID;
 	imr->ifm_active = IFM_IEEE80211;
-/* XXX
-	if (ic->ic_state == IEEE80211_S_RUN) {
+	if (ic->ic_state == IEEE80211_S_RUN)
 		imr->ifm_status |= IFM_ACTIVE;
-		ifp->if_link_state = LINK_STATE_UP;
+	/*
+	 * Calculate a current rate if possible.
+	 */
+	if (ic->ic_fixed_rate != -1) {
+		/*
+		 * A fixed rate is set, report that.
+		 */
+		rs = &ic->ic_sup_rates[ic->ic_curmode];
+		imr->ifm_active |= ieee80211_rate2media(ic,
+			rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode);
+	} else if (ic->ic_opmode == IEEE80211_M_STA) {
+		/*
+		 * In station mode report the current transmit rate.
+		 */
+		rs = &ic->ic_bss->ni_rates;
+		imr->ifm_active |= ieee80211_rate2media(ic,
+			rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode);
 	} else
-		ifp->if_link_state = LINK_STATE_DOWN;
-*/
-	imr->ifm_active |= IFM_AUTO;
+		imr->ifm_active |= IFM_AUTO;
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
-		ni = ic->ic_bss;
-		/* calculate rate subtype */
-		imr->ifm_active |= ieee80211_rate2media(ic,
-			ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
 		break;
 	case IEEE80211_M_IBSS:
 		imr->ifm_active |= IFM_IEEE80211_ADHOC;
@@ -580,58 +721,41 @@
 	case IEEE80211_MODE_FH:
 		imr->ifm_active |= IFM_IEEE80211_FH;
 		break;
-	case IEEE80211_MODE_TURBO:
+	case IEEE80211_MODE_TURBO_A:
 		imr->ifm_active |= IFM_IEEE80211_11A
 				|  IFM_IEEE80211_TURBO;
 		break;
+	case IEEE80211_MODE_TURBO_G:
+		imr->ifm_active |= IFM_IEEE80211_11G
+				|  IFM_IEEE80211_TURBO;
+		break;
 	}
-
-	/* Notify that the link state has changed. */
-	if (imr->ifm_status != old_status)
-		rt_ifmsg(ifp);
 }
 
 void
-ieee80211_watchdog(struct ifnet *ifp)
+ieee80211_watchdog(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
-
-	if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
-		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
-	if (ic->ic_inact_timer && --ic->ic_inact_timer == 0)
-		ieee80211_timeout_nodes(ic);
-
-	if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0)
-		ifp->if_timer = 1;
-}
+	struct ieee80211_node_table *nt;
+	int need_inact_timer = 0;
 
-/*
- * Mark the basic rates for the 11g rate table based on the
- * operating mode.  For real 11g we mark all the 11b rates
- * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
- * 11b rates.  There's also a pseudo 11a-mode used to mark only
- * the basic OFDM rates.
- */
-static void
-ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
-{
-	static const struct ieee80211_rateset basic[] = {
-	    { 3, { 12, 24, 48 } },		/* IEEE80211_MODE_11A */
-	    { 4, { 2, 4, 11, 22 } },		/* IEEE80211_MODE_11B */
-	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */
-	    { 0 },				/* IEEE80211_MODE_FH */
-	    { 0 },				/* IEEE80211_MODE_TURBO	*/
-	};
-	int i, j;
-
-	for (i = 0; i < rs->rs_nrates; i++) {
-		rs->rs_rates[i] &= IEEE80211_RATE_VAL;
-		for (j = 0; j < basic[mode].rs_nrates; j++)
-			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
-				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
-				break;
-			}
+	if (ic->ic_state != IEEE80211_S_INIT) {
+		if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+		nt = &ic->ic_scan;
+		if (nt->nt_inact_timer) {
+			if (--nt->nt_inact_timer == 0)
+				nt->nt_timeout(nt);
+			need_inact_timer += nt->nt_inact_timer;
+		}
+		nt = &ic->ic_sta;
+		if (nt->nt_inact_timer) {
+			if (--nt->nt_inact_timer == 0)
+				nt->nt_timeout(nt);
+			need_inact_timer += nt->nt_inact_timer;
+		}
 	}
+	if (ic->ic_mgt_timer != 0 || need_inact_timer)
+		ic->ic_ifp->if_timer = 1;
 }
 
 /*
@@ -650,7 +774,8 @@
 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
 		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
-		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO	*/
+		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO_A */
+		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
 	};
 	struct ieee80211_channel *c;
 	u_int modeflags;
@@ -658,8 +783,9 @@
 
 	/* validate new mode */
 	if ((ic->ic_modecaps & (1<<mode)) == 0) {
-		IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n",
-			__func__, mode, ic->ic_modecaps));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: mode %u not supported (caps 0x%x)\n",
+			__func__, mode, ic->ic_modecaps);
 		return EINVAL;
 	}
 
@@ -681,8 +807,8 @@
 		}
 	}
 	if (i > IEEE80211_CHAN_MAX) {
-		IEEE80211_DPRINTF(("%s: no channels found for mode %u\n",
-			__func__, mode));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: no channels found for mode %u\n", __func__, mode);
 		return EINVAL;
 	}
 
@@ -714,56 +840,82 @@
 				ic->ic_ibss_chan = &ic->ic_channels[i];
 				break;
 			}
+		KASSERT(ic->ic_ibss_chan != NULL &&
+		    isset(ic->ic_chan_active,
+			ieee80211_chan2ieee(ic, ic->ic_ibss_chan)),
+		    ("Bad IBSS channel %u",
+		     ieee80211_chan2ieee(ic, ic->ic_ibss_chan)));
 	}
+	/*
+	 * If the desired channel is set but no longer valid then reset it.
+	 */
+	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+	    isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan)))
+		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
 
 	/*
-	 * Set/reset state flags that influence beacon contents, etc.
-	 *
-	 * XXX what if we have stations already associated???
-	 * XXX probably not right for autoselect?
+	 * Do mode-specific rate setup.
 	 */
-	if (ic->ic_caps & IEEE80211_C_SHPREAMBLE)
-		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
 	if (mode == IEEE80211_MODE_11G) {
-		if (ic->ic_caps & IEEE80211_C_SHSLOT)
-			ic->ic_flags |= IEEE80211_F_SHSLOT;
+		/*
+		 * Use a mixed 11b/11g rate set.
+		 */
 		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
 			IEEE80211_MODE_11G);
-	} else {
-		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+	} else if (mode == IEEE80211_MODE_11B) {
+		/*
+		 * Force pure 11b rate set.
+		 */
+		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
+			IEEE80211_MODE_11B);
 	}
+	/*
+	 * Setup an initial rate set according to the
+	 * current/default channel selected above.  This
+	 * will be changed when scanning but must exist
+	 * now so driver have a consistent state of ic_ibss_chan.
+	 */
+	if (ic->ic_bss)		/* NB: can be called before lateattach */
+		ic->ic_bss->ni_rates = ic->ic_sup_rates[mode];
 
 	ic->ic_curmode = mode;
+	ieee80211_reset_erp(ic);	/* reset ERP state */
+	ieee80211_wme_initparams(ic);	/* reset WME stat */
+
 	return 0;
 #undef N
 }
 
 /*
  * Return the phy mode for with the specified channel so the
- * caller can select a rate set.  This is problematic and the
- * work here assumes how things work elsewhere in this code.
+ * caller can select a rate set.  This is problematic for channels
+ * where multiple operating modes are possible (e.g. 11g+11b).
+ * In those cases we defer to the current operating mode when set.
  */
 enum ieee80211_phymode
 ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
 {
-	/*
-	 * NB: this assumes the channel would not be supplied to us
-	 *     unless it was already compatible with the current mode.
-	 */
-	if (ic->ic_curmode != IEEE80211_MODE_AUTO)
-		return ic->ic_curmode;
-	/*
-	 * In autoselect mode; deduce a mode based on the channel
-	 * characteristics.  We assume that turbo-only channels
-	 * are not considered when the channel set is constructed.
-	 */
-	if (IEEE80211_IS_CHAN_5GHZ(chan))
+	if (IEEE80211_IS_CHAN_5GHZ(chan)) {
+		/*
+		 * This assumes all 11a turbo channels are also
+		 * usable withut turbo, which is currently true.
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_TURBO_A)
+			return IEEE80211_MODE_TURBO_A;
 		return IEEE80211_MODE_11A;
-	else if (IEEE80211_IS_CHAN_FHSS(chan))
+	} else if (IEEE80211_IS_CHAN_FHSS(chan))
 		return IEEE80211_MODE_FH;
-	else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN))
+	else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) {
+		/*
+		 * This assumes all 11g channels are also usable
+		 * for 11b, which is currently true.
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_TURBO_G)
+			return IEEE80211_MODE_TURBO_G;
+		if (ic->ic_curmode == IEEE80211_MODE_11B)
+			return IEEE80211_MODE_11B;
 		return IEEE80211_MODE_11G;
-	else
+	} else
 		return IEEE80211_MODE_11B;
 }
 
@@ -813,7 +965,7 @@
 	mask = rate & IEEE80211_RATE_VAL;
 	switch (mode) {
 	case IEEE80211_MODE_11A:
-	case IEEE80211_MODE_TURBO:
+	case IEEE80211_MODE_TURBO_A:
 		mask |= IFM_IEEE80211_11A;
 		break;
 	case IEEE80211_MODE_11B:
@@ -831,6 +983,7 @@
 		/* NB: hack, 11g matches both 11b+11a rates */
 		/* fall thru... */
 	case IEEE80211_MODE_11G:
+	case IEEE80211_MODE_TURBO_G:
 		mask |= IFM_IEEE80211_11G;
 		break;
 	}
@@ -870,33 +1023,3 @@
 		ieeerates[IFM_SUBTYPE(mword)] : 0;
 #undef N
 }
-
-/*
- * Module glue.
- *
- * NB: the module name is "wlan" for compatibility with NetBSD.
- */
-
-static int
-ieee80211_modevent(module_t mod, int type, void *unused)
-{
-	switch (type) {
-	case MOD_LOAD:
-		if (bootverbose)
-			printf("wlan: <802.11 Link Layer>\n");
-		return 0;
-	case MOD_UNLOAD:
-	case MOD_SHUTDOWN:
-		return 0;
-	}
-	return EINVAL;
-}
-
-static moduledata_t ieee80211_mod = {
-	"wlan",
-	ieee80211_modevent,
-	0
-};
-DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan, 1);
-MODULE_DEPEND(wlan, crypto, 1, 1, 1);
Index: src/sys/netproto/802_11/ieee80211.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211.h,v
retrieving revision 1.1
diff -u -r1.1 ieee80211.h
--- src/sys/netproto/802_11/ieee80211.h	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211.h	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,10 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.5 2004/04/05 17:47:40 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_H_
-#define	_NETPROTO_802_11_IEEE80211_H_
+#ifndef _NET80211_IEEE80211_H_
+#define _NET80211_IEEE80211_H_
 
 /*
  * 802.11 protocol definitions.
@@ -42,112 +40,71 @@
 
 #define	IEEE80211_ADDR_LEN	6		/* size of 802.11 address */
 /* is 802.11 address multicast/broadcast? */
-#define	IEEE80211_IS_MULTICAST(a)	ETHER_IS_MULTICAST(a)
+#define	IEEE80211_IS_MULTICAST(_a)	(*(_a) & 0x01)
 
 /* IEEE 802.11 PLCP header */
 struct ieee80211_plcp_hdr {
-	uint16_t	i_sfd;
-	uint8_t		i_signal;
-	uint8_t		i_service;
-	uint16_t	i_length;
-	uint16_t	i_crc;
-} __attribute__((__packed__));
+	u_int16_t	i_sfd;
+	u_int8_t	i_signal;
+	u_int8_t	i_service;
+	u_int16_t	i_length;
+	u_int16_t	i_crc;
+} __packed;
 
-#define	IEEE80211_PLCP_SFD      0xF3A0 
-#define	IEEE80211_PLCP_SERVICE  0x00
+#define IEEE80211_PLCP_SFD      0xF3A0 
+#define IEEE80211_PLCP_SERVICE  0x00
 
 /*
  * generic definitions for IEEE 802.11 frames
  */
 struct ieee80211_frame {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
 	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
 	/* see below */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_qosframe {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
-	uint8_t		i_qos[2];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_qos[2];
 	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
 	/* see below */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_qoscntl {
-	uint8_t		i_qos[2];
+	u_int8_t	i_qos[2];
 };
 
 struct ieee80211_frame_addr4 {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
-	uint8_t		i_addr4[IEEE80211_ADDR_LEN];
-} __attribute__((__packed__));
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_addr4[IEEE80211_ADDR_LEN];
+} __packed;
 
 
 struct ieee80211_qosframe_addr4 {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
-	uint8_t		i_addr4[IEEE80211_ADDR_LEN];
-	uint8_t		i_qos[2];
-} __attribute__((__packed__));
-
-/*
- * Management Notification Frame
- */
-struct ieee80211_mnf {
-	uint8_t		mnf_category;
-	uint8_t		mnf_action;
-	uint8_t		mnf_dialog;
-	uint8_t		mnf_status;
-} __attribute__((__packed__));
-#define	MNF_SETUP_REQ	0
-#define	MNF_SETUP_RESP	1
-#define	MNF_TEARDOWN	2
-
-/*
- * WME/802.11e Tspec Element
- */
-struct ieee80211_wme_tspec {
-	uint8_t		ts_id;
-	uint8_t		ts_len;
-	uint8_t		ts_oui[3];
-	uint8_t		ts_oui_type;
-	uint8_t		ts_oui_subtype;
-	uint8_t		ts_version;
-	uint8_t		ts_tsinfo[3];
-	uint8_t		ts_nom_msdu[2];
-	uint8_t		ts_max_msdu[2];
-	uint8_t		ts_min_svc[4];
-	uint8_t		ts_max_svc[4];
-	uint8_t		ts_inactv_intv[4];
-	uint8_t		ts_susp_intv[4];
-	uint8_t		ts_start_svc[4];
-	uint8_t		ts_min_rate[4];
-	uint8_t		ts_mean_rate[4];
-	uint8_t		ts_max_burst[4];
-	uint8_t		ts_min_phy[4];
-	uint8_t		ts_peak_rate[4];
-	uint8_t		ts_delay[4];
-	uint8_t		ts_surplus[2];
-	uint8_t		ts_medium_time[2];
-} __attribute__((__packed__));
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_addr4[IEEE80211_ADDR_LEN];
+	u_int8_t	i_qos[2];
+} __packed;
 
 #define	IEEE80211_FC0_VERSION_MASK		0x03
 #define	IEEE80211_FC0_VERSION_SHIFT		0
@@ -189,6 +146,7 @@
 #define	IEEE80211_FC0_SUBTYPE_CFPOLL		0x60
 #define	IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK	0x70
 #define	IEEE80211_FC0_SUBTYPE_QOS		0x80
+#define	IEEE80211_FC0_SUBTYPE_QOS_NULL		0xc0
 
 #define	IEEE80211_FC1_DIR_MASK			0x03
 #define	IEEE80211_FC1_DIR_NODS			0x00	/* STA->STA */
@@ -212,58 +170,170 @@
 
 #define	IEEE80211_QOS_TXOP			0x00ff
 /* bit 8 is reserved */
-#define	IEEE80211_QOS_ACKPOLICY			0x0600
-#define	IEEE80211_QOS_ESOP			0x0800
-#define	IEEE80211_QOS_TID			0xf000
+#define	IEEE80211_QOS_ACKPOLICY			0x60
+#define	IEEE80211_QOS_ACKPOLICY_S		5
+#define	IEEE80211_QOS_ESOP			0x10
+#define	IEEE80211_QOS_ESOP_S			4
+#define	IEEE80211_QOS_TID			0x0f
+
+/* does frame have QoS sequence control data */
+#define	IEEE80211_QOS_HAS_SEQ(wh) \
+	(((wh)->i_fc[0] & \
+	  (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \
+	  (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+
+/*
+ * WME/802.11e information element.
+ */
+struct ieee80211_wme_info {
+	u_int8_t	wme_id;		/* IEEE80211_ELEMID_VENDOR */
+	u_int8_t	wme_len;	/* length in bytes */
+	u_int8_t	wme_oui[3];	/* 0x00, 0x50, 0xf2 */
+	u_int8_t	wme_type;	/* OUI type */
+	u_int8_t	wme_subtype;	/* OUI subtype */
+	u_int8_t	wme_version;	/* spec revision */
+	u_int8_t	wme_info;	/* QoS info */
+} __packed;
+
+/*
+ * WME/802.11e Tspec Element
+ */
+struct ieee80211_wme_tspec {
+	u_int8_t	ts_id;
+	u_int8_t	ts_len;
+	u_int8_t	ts_oui[3];
+	u_int8_t	ts_oui_type;
+	u_int8_t	ts_oui_subtype;
+	u_int8_t	ts_version;
+	u_int8_t	ts_tsinfo[3];
+	u_int8_t	ts_nom_msdu[2];
+	u_int8_t	ts_max_msdu[2];
+	u_int8_t	ts_min_svc[4];
+	u_int8_t	ts_max_svc[4];
+	u_int8_t	ts_inactv_intv[4];
+	u_int8_t	ts_susp_intv[4];
+	u_int8_t	ts_start_svc[4];
+	u_int8_t	ts_min_rate[4];
+	u_int8_t	ts_mean_rate[4];
+	u_int8_t	ts_max_burst[4];
+	u_int8_t	ts_min_phy[4];
+	u_int8_t	ts_peak_rate[4];
+	u_int8_t	ts_delay[4];
+	u_int8_t	ts_surplus[2];
+	u_int8_t	ts_medium_time[2];
+} __packed;
+
+/*
+ * WME AC parameter field
+ */
+struct ieee80211_wme_acparams {
+	u_int8_t	acp_aci_aifsn;
+	u_int8_t	acp_logcwminmax;
+	u_int16_t	acp_txop;
+} __packed;
+
+#define WME_NUM_AC		4	/* 4 AC categories */
+
+#define WME_PARAM_ACI		0x60	/* Mask for ACI field */
+#define WME_PARAM_ACI_S		5	/* Shift for ACI field */
+#define WME_PARAM_ACM		0x10	/* Mask for ACM bit */
+#define WME_PARAM_ACM_S		4	/* Shift for ACM bit */
+#define WME_PARAM_AIFSN		0x0f	/* Mask for aifsn field */
+#define WME_PARAM_AIFSN_S	0	/* Shift for aifsn field */
+#define WME_PARAM_LOGCWMIN	0x0f	/* Mask for CwMin field (in log) */
+#define WME_PARAM_LOGCWMIN_S	0	/* Shift for CwMin field */
+#define WME_PARAM_LOGCWMAX	0xf0	/* Mask for CwMax field (in log) */
+#define WME_PARAM_LOGCWMAX_S	4	/* Shift for CwMax field */
+
+#define WME_AC_TO_TID(_ac) (       \
+	((_ac) == WME_AC_VO) ? 6 : \
+	((_ac) == WME_AC_VI) ? 5 : \
+	((_ac) == WME_AC_BK) ? 1 : \
+	0)
+
+#define TID_TO_WME_AC(_tid) (      \
+	((_tid) < 1) ? WME_AC_BE : \
+	((_tid) < 3) ? WME_AC_BK : \
+	((_tid) < 6) ? WME_AC_VI : \
+	WME_AC_VO)
+
+/*
+ * WME Parameter Element
+ */
+struct ieee80211_wme_param {
+	u_int8_t	param_id;
+	u_int8_t	param_len;
+	u_int8_t	param_oui[3];
+	u_int8_t	param_oui_type;
+	u_int8_t	param_oui_sybtype;
+	u_int8_t	param_version;
+	u_int8_t	param_qosInfo;
+#define	WME_QOSINFO_COUNT	0x0f	/* Mask for param count field */
+	u_int8_t	param_reserved;
+	struct ieee80211_wme_acparams	params_acParams[WME_NUM_AC];
+} __packed;
+
+/*
+ * Management Notification Frame
+ */
+struct ieee80211_mnf {
+	u_int8_t	mnf_category;
+	u_int8_t	mnf_action;
+	u_int8_t	mnf_dialog;
+	u_int8_t	mnf_status;
+} __packed;
+#define	MNF_SETUP_REQ	0
+#define	MNF_SETUP_RESP	1
+#define	MNF_TEARDOWN	2
 
 /*
  * Control frames.
  */
 struct ieee80211_frame_min {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_rts {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
-	uint8_t		i_ta[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_ta[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_cts {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_ack {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_pspoll {
-	uint8_t		i_fc[2];
-	uint8_t		i_aid[2];
-	uint8_t		i_bssid[IEEE80211_ADDR_LEN];
-	uint8_t		i_ta[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_aid[2];
+	u_int8_t	i_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	i_ta[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_cfend {		/* NB: also CF-End+CF-Ack */
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];	/* should be zero */
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
-	uint8_t		i_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];	/* should be zero */
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_bssid[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 /*
  * BEACON management packets
@@ -277,7 +347,7 @@
  *		octet information[length]
  */
 
-typedef uint8_t *ieee80211_mgt_beacon_t;
+typedef u_int8_t *ieee80211_mgt_beacon_t;
 
 #define	IEEE80211_BEACON_INTERVAL(beacon) \
 	((beacon)[8] | ((beacon)[9] << 8))
@@ -303,103 +373,80 @@
  * 802.11i/WPA information element (maximally sized).
  */
 struct ieee80211_ie_wpa {
-	uint8_t		wpa_oui[3];	/* 0x00, 0x50, 0xf2 */
-	uint8_t		wpa_type;	/* OUI type */
-	uint16_t	wpa_version;	/* spec revision */
-	uint32_t	wpa_mcipher[1];	/* multicast/group key cipher */
-	uint16_t	wpa_uciphercnt;	/* # pairwise key ciphers */
-	uint32_t	wpa_uciphers[8];/* ciphers */
-	uint16_t	wpa_authselcnt;	/* authentication selector cnt*/
-	uint32_t	wpa_authsels[8];/* selectors */
-} __attribute__((__packed__));
-
-/*
- * Management information elements
- */
-struct ieee80211_information {
-	char	ssid[IEEE80211_NWID_LEN+1];
-	struct rates {
-		uint8_t	*p;
-	} rates;
-	struct fh {
-		uint16_t	dwell;
-		uint8_t	set;
-		uint8_t	pattern;
-		uint8_t	index;
-	} fh;
-	struct ds {
-		uint8_t	channel;
-	} ds;
-	struct cf {
-		uint8_t	count;
-		uint8_t	period;
-		uint8_t	maxdur[2];
-		uint8_t	dur[2];
-	} cf;
-	struct tim {
-		uint8_t	count;
-		uint8_t	period;
-		uint8_t	bitctl;
-		/* uint8_t	pvt[251]; The driver needs to use this. */
-	} tim;
-	struct ibss {
-		uint16_t	atim;
-	} ibss;
-	struct challenge {
-		uint8_t	*p;
-		uint8_t	len;
-	} challenge;
-	struct erp {
-		uint8_t	flags;
-	} erp;
-	struct country {
-		uint8_t	cc[3];		/* ISO CC+(I)ndoor/(O)utdoor */
-		struct {
-			uint8_t	schan;		/* starting channel */
-			uint8_t	nchan;		/* number channels */
-			uint8_t	maxtxpwr;	
-		} band[4];			/* up to 4 sub bands */
-	} country;
-	struct ath {
-		uint8_t	flags;
-	} ath;
-	struct ieee80211_ie_wpa	wpa;
-};
+	u_int8_t	wpa_id;		/* IEEE80211_ELEMID_VENDOR */
+	u_int8_t	wpa_len;	/* length in bytes */
+	u_int8_t	wpa_oui[3];	/* 0x00, 0x50, 0xf2 */
+	u_int8_t	wpa_type;	/* OUI type */
+	u_int16_t	wpa_version;	/* spec revision */
+	u_int32_t	wpa_mcipher[1];	/* multicast/group key cipher */
+	u_int16_t	wpa_uciphercnt;	/* # pairwise key ciphers */
+	u_int32_t	wpa_uciphers[8];/* ciphers */
+	u_int16_t	wpa_authselcnt;	/* authentication selector cnt*/
+	u_int32_t	wpa_authsels[8];/* selectors */
+	u_int16_t	wpa_caps;	/* 802.11i capabilities */
+	u_int16_t	wpa_pmkidcnt;	/* 802.11i pmkid count */
+	u_int16_t	wpa_pmkids[8];	/* 802.11i pmkids */
+} __packed;
+
+/*
+ * Management information element payloads.
+ */
 
 enum {
-	IEEE80211_ELEMID_SSID			= 0,
-	IEEE80211_ELEMID_RATES			= 1,
-	IEEE80211_ELEMID_FHPARMS		= 2,
-	IEEE80211_ELEMID_DSPARMS		= 3,
-	IEEE80211_ELEMID_CFPARMS		= 4,
-	IEEE80211_ELEMID_TIM			= 5,
-	IEEE80211_ELEMID_IBSSPARMS		= 6,
-	IEEE80211_ELEMID_COUNTRY		= 7,
-	IEEE80211_ELEMID_CHALLENGE		= 16,
+	IEEE80211_ELEMID_SSID		= 0,
+	IEEE80211_ELEMID_RATES		= 1,
+	IEEE80211_ELEMID_FHPARMS	= 2,
+	IEEE80211_ELEMID_DSPARMS	= 3,
+	IEEE80211_ELEMID_CFPARMS	= 4,
+	IEEE80211_ELEMID_TIM		= 5,
+	IEEE80211_ELEMID_IBSSPARMS	= 6,
+	IEEE80211_ELEMID_COUNTRY	= 7,
+	IEEE80211_ELEMID_CHALLENGE	= 16,
 	/* 17-31 reserved for challenge text extension */
-	IEEE80211_ELEMID_ERP			= 42,
-	IEEE80211_ELEMID_XRATES			= 50,
-	IEEE80211_ELEMID_TPC			= 150,
-	IEEE80211_ELEMID_CCKM			= 156,
-	IEEE80211_ELEMID_VENDOR			= 221,	/* vendor private */
+	IEEE80211_ELEMID_ERP		= 42,
+	IEEE80211_ELEMID_RSN		= 48,
+	IEEE80211_ELEMID_XRATES		= 50,
+	IEEE80211_ELEMID_TPC		= 150,
+	IEEE80211_ELEMID_CCKM		= 156,
+	IEEE80211_ELEMID_VENDOR		= 221,	/* vendor private */
 };
 
-#define	IEEE80211_CHALLENGE_LEN			128
+struct ieee80211_tim_ie {
+	u_int8_t	tim_ie;			/* IEEE80211_ELEMID_TIM */
+	u_int8_t	tim_len;
+	u_int8_t	tim_count;		/* DTIM count */
+	u_int8_t	tim_period;		/* DTIM period */
+	u_int8_t	tim_bitctl;		/* bitmap control */
+	u_int8_t	tim_bitmap[1];		/* variable-length bitmap */
+} __packed;
+
+struct ieee80211_country_ie {
+	u_int8_t	ie;			/* IEEE80211_ELEMID_COUNTRY */
+	u_int8_t	len;
+	u_int8_t	cc[3];			/* ISO CC+(I)ndoor/(O)utdoor */
+	struct {
+		u_int8_t schan;			/* starting channel */
+		u_int8_t nchan;			/* number channels */
+		u_int8_t maxtxpwr;		/* tx power cap */
+	} band[4] __packed;			/* up to 4 sub bands */
+} __packed;
+
+#define IEEE80211_CHALLENGE_LEN		128
 
-#define	IEEE80211_RATE_BASIC			0x80
-#define	IEEE80211_RATE_VAL			0x7f
+#define	IEEE80211_RATE_BASIC		0x80
+#define	IEEE80211_RATE_VAL		0x7f
 
 /* EPR information element flags */
-#define	IEEE80211_ERP_NON_ERP_PRESENT		0x01
-#define	IEEE80211_ERP_USE_PROTECTION		0x02
-#define	IEEE80211_ERP_BARKER_MODE		0x04
+#define	IEEE80211_ERP_NON_ERP_PRESENT	0x01
+#define	IEEE80211_ERP_USE_PROTECTION	0x02
+#define	IEEE80211_ERP_LONG_PREAMBLE	0x04
 
 /* Atheros private advanced capabilities info */
-#define	ATHEROS_CAP_TURBO_PRIME			0x01
-#define	ATHEROS_CAP_COMPRESSION			0x02
-#define	ATHEROS_CAP_FAST_FRAME			0x04
+#define	ATHEROS_CAP_TURBO_PRIME		0x01
+#define	ATHEROS_CAP_COMPRESSION		0x02
+#define	ATHEROS_CAP_FAST_FRAME		0x04
 /* bits 3-6 reserved */
-#define	ATHEROS_CAP_BOOST			0x80
+#define	ATHEROS_CAP_BOOST		0x80
 
 #define	ATH_OUI			0x7f0300		/* Atheros OUI */
 #define	ATH_OUI_TYPE		0x01
@@ -407,12 +454,11 @@
 
 #define	WPA_OUI			0xf25000
 #define	WPA_OUI_TYPE		0x01
-#define	WPA_OUI_VERSION		1		/* current supported version */
+#define	WPA_VERSION		1		/* current supported version */
 
 #define	WPA_CSE_NULL		0x00
 #define	WPA_CSE_WEP40		0x01
 #define	WPA_CSE_TKIP		0x02
-#define	WPA_CSE_WRAP		0x03		/* WPA2/802.11i */
 #define	WPA_CSE_CCMP		0x04
 #define	WPA_CSE_WEP104		0x05
 
@@ -420,6 +466,34 @@
 #define	WPA_ASE_8021X_UNSPEC	0x01
 #define	WPA_ASE_8021X_PSK	0x02
 
+#define	RSN_OUI			0xac0f00
+#define	RSN_VERSION		1		/* current supported version */
+
+#define	RSN_CSE_NULL		0x00
+#define	RSN_CSE_WEP40		0x01
+#define	RSN_CSE_TKIP		0x02
+#define	RSN_CSE_WRAP		0x03
+#define	RSN_CSE_CCMP		0x04
+#define	RSN_CSE_WEP104		0x05
+
+#define	RSN_ASE_NONE		0x00
+#define	RSN_ASE_8021X_UNSPEC	0x01
+#define	RSN_ASE_8021X_PSK	0x02
+
+#define	RSN_CAP_PREAUTH		0x01
+
+#define	WME_OUI			0xf25000
+#define	WME_OUI_TYPE		0x02
+#define	WME_INFO_OUI_SUBTYPE	0x00
+#define	WME_PARAM_OUI_SUBTYPE	0x01
+#define	WME_VERSION		1
+
+/* WME stream classes */
+#define	WME_AC_BE	0		/* best effort */
+#define	WME_AC_BK	1		/* background */
+#define	WME_AC_VI	2		/* video */
+#define	WME_AC_VO	3		/* voice */
+
 /*
  * AUTH management packets
  *
@@ -431,7 +505,7 @@
  *	octet chal.text[253]
  */
 
-typedef uint8_t *ieee80211_mgt_auth_t;
+typedef u_int8_t *ieee80211_mgt_auth_t;
 
 #define	IEEE80211_AUTH_ALGORITHM(auth) \
 	((auth)[0] | ((auth)[1] << 8))
@@ -440,9 +514,9 @@
 #define	IEEE80211_AUTH_STATUS(auth) \
 	((auth)[4] | ((auth)[5] << 8))
 
-#define	IEEE80211_AUTH_ALG_OPEN			0x0000
-#define	IEEE80211_AUTH_ALG_SHARED		0x0001
-#define	IEEE80211_AUTH_ALG_LEAP			0x0080
+#define	IEEE80211_AUTH_ALG_OPEN		0x0000
+#define	IEEE80211_AUTH_ALG_SHARED	0x0001
+#define	IEEE80211_AUTH_ALG_LEAP		0x0080
 
 enum {
 	IEEE80211_AUTH_OPEN_REQUEST		= 1,
@@ -498,13 +572,24 @@
 	IEEE80211_STATUS_DSSSOFDM_REQUIRED	= 26,
 };
 
-#define	IEEE80211_WEP_KEYLEN			5	/* 40bit */
-#define	IEEE80211_WEP_IVLEN			3	/* 24bit */
-#define	IEEE80211_WEP_KIDLEN			1	/* 1 octet */
-#define	IEEE80211_WEP_CRCLEN			4	/* CRC-32 */
-#define	IEEE80211_WEP_NKID			4	/* number of key ids */
+#define	IEEE80211_WEP_KEYLEN		5	/* 40bit */
+#define	IEEE80211_WEP_IVLEN		3	/* 24bit */
+#define	IEEE80211_WEP_KIDLEN		1	/* 1 octet */
+#define	IEEE80211_WEP_CRCLEN		4	/* CRC-32 */
+#define	IEEE80211_WEP_NKID		4	/* number of key ids */
+
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP.  For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define	IEEE80211_WEP_EXTIV		0x20
+#define	IEEE80211_WEP_EXTIVLEN		4	/* extended IV length */
+#define	IEEE80211_WEP_MICLEN		8	/* trailing MIC */
 
-#define	IEEE80211_CRC_LEN			4
+#define	IEEE80211_CRC_LEN		4
 
 /*
  * Maximum acceptable MTU is:
@@ -513,26 +598,40 @@
  * Min is arbitrarily chosen > IEEE80211_MIN_LEN.  The default
  * mtu is Ethernet-compatible; it's set by ether_ifattach.
  */
-#define	IEEE80211_MTU_MAX			2290
-#define	IEEE80211_MTU_MIN			32
+#define	IEEE80211_MTU_MAX		2290
+#define	IEEE80211_MTU_MIN		32
 
-#define	IEEE80211_MAX_LEN			(2300 + IEEE80211_CRC_LEN + \
+#define	IEEE80211_MAX_LEN		(2300 + IEEE80211_CRC_LEN + \
     (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN))
+#define	IEEE80211_ACK_LEN \
+	(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
 #define	IEEE80211_MIN_LEN \
 	(sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN)
 
+/*
+ * The 802.11 spec says at most 2007 stations may be
+ * associated at once.  For most AP's this is way more
+ * than is feasible so we use a default of 128.  This
+ * number may be overridden by the driver and/or by
+ * user configuration.
+ */
+#define	IEEE80211_AID_MAX		2007
+#define	IEEE80211_AID_DEF		128
+
+#define	IEEE80211_AID(b)	((b) &~ 0xc000)
+#define	IEEE80211_AID_SET(b, w) \
+	((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32)))
+#define	IEEE80211_AID_CLR(b, w) \
+	((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32)))
+#define	IEEE80211_AID_ISSET(b, w) \
+	((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
+
 /* 
  * RTS frame length parameters.  The default is specified in
  * the 802.11 spec.  The max may be wrong for jumbo frames.
  */
-#define	IEEE80211_RTS_DEFAULT			512
-#define	IEEE80211_RTS_MIN			1
-#define	IEEE80211_RTS_MAX			IEEE80211_MAX_LEN
-
-enum {
-	IEEE80211_AUTH_NONE	= 0,
-	IEEE80211_AUTH_OPEN	= 1,
-	IEEE80211_AUTH_SHARED	= 2,
-};
+#define	IEEE80211_RTS_DEFAULT		512
+#define	IEEE80211_RTS_MIN		1
+#define	IEEE80211_RTS_MAX		IEEE80211_MAX_LEN
 
-#endif /* _NETPROTO_802_11_IEEE80211_H_ */
+#endif /* _NET80211_IEEE80211_H_ */
Index: src/sys/netproto/802_11/ieee80211_crypto.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_crypto.c,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_crypto.c
--- src/sys/netproto/802_11/ieee80211_crypto.c	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_crypto.c	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,295 +28,600 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
-#include "opt_inet.h"
+#include <sys/cdefs.h>
 
+/*
+ * IEEE 802.11 generic crypto support.
+ */
 #include <sys/param.h>
-#include <sys/systm.h> 
 #include <sys/mbuf.h>   
-#include <sys/malloc.h>
-#include <sys/kernel.h>
+
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
 
-#include <machine/atomic.h>
- 
+#include <sys/thread.h>
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
+#include <net/ethernet.h>		/* XXX ETHER_HDR_LEN */
 
 #include <netproto/802_11/ieee80211_var.h>
 
-#include <net/bpf.h>
+/*
+ * Table of registered cipher modules.
+ */
+static	const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
+
+static	int _ieee80211_crypto_delkey(struct ieee80211com *,
+		struct ieee80211_key *);
+
+/*
+ * Default "null" key management routines.
+ */
+static int
+null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	return IEEE80211_KEYIX_NONE;
+}
+static int
+null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	return 1;
+}
+static 	int
+null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+	     const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	return 1;
+}
+static void null_key_update(struct ieee80211com *ic) {}
+
+/*
+ * Write-arounds for common operations.
+ */
+static __inline void
+cipher_detach(struct ieee80211_key *key)
+{
+	key->wk_cipher->ic_detach(key);
+}
+
+static __inline void *
+cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	return key->wk_cipher->ic_attach(ic, key);
+}
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
-
-#include <crypto/rc4/rc4.h>
-#define	arc4_ctxlen()			sizeof (struct rc4_state)
-#define	arc4_setkey(_c,_k,_l)		rc4_init(_c,_k,_l)
-#define	arc4_encrypt(_c,_d,_s,_l)	rc4_crypt(_c,_s,_d,_l)
+/* 
+ * Wrappers for driver key management methods.
+ */
+static __inline int
+dev_key_alloc(struct ieee80211com *ic,
+	const struct ieee80211_key *key)
+{
+	return ic->ic_crypto.cs_key_alloc(ic, key);
+}
 
-static	void ieee80211_crc_init(void);
-static	uint32_t ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len);
+static __inline int
+dev_key_delete(struct ieee80211com *ic,
+	const struct ieee80211_key *key)
+{
+	return ic->ic_crypto.cs_key_delete(ic, key);
+}
 
+static __inline int
+dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
+	const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	return ic->ic_crypto.cs_key_set(ic, key, mac);
+}
+
+/*
+ * Setup crypto support.
+ */
 void
-ieee80211_crypto_attach(struct ifnet *ifp)
+ieee80211_crypto_attach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ieee80211_crypto_state *cs = &ic->ic_crypto;
+	int i;
 
+	/* NB: we assume everything is pre-zero'd */
+	cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
+	ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+	for (i = 0; i < IEEE80211_WEP_NKID; i++)
+		ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i],
+			IEEE80211_KEYIX_NONE);
 	/*
-	 * Setup crypto support.
+	 * Initialize the driver key support routines to noop entries.
+	 * This is useful especially for the cipher test modules.
 	 */
-	ieee80211_crc_init();
-	ic->ic_iv = arc4random();
+	cs->cs_key_alloc = null_key_alloc;
+	cs->cs_key_set = null_key_set;
+	cs->cs_key_delete = null_key_delete;
+	cs->cs_key_update_begin = null_key_update;
+	cs->cs_key_update_end = null_key_update;
 }
 
+/*
+ * Teardown crypto support.
+ */
 void
-ieee80211_crypto_detach(struct ifnet *ifp)
+ieee80211_crypto_detach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	ieee80211_crypto_delglobalkeys(ic);
+}
 
-	if (ic->ic_wep_ctx != NULL) {
-		free(ic->ic_wep_ctx, M_DEVBUF);
-		ic->ic_wep_ctx = NULL;
+/*
+ * Register a crypto cipher module.
+ */
+void
+ieee80211_crypto_register(const struct ieee80211_cipher *cip)
+{
+	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+		printf("%s: cipher %s has an invalid cipher index %u\n",
+			__func__, cip->ic_name, cip->ic_cipher);
+		return;
+	}
+	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+		printf("%s: cipher %s registered with a different template\n",
+			__func__, cip->ic_name);
+		return;
 	}
+	ciphers[cip->ic_cipher] = cip;
 }
 
-struct mbuf *
-ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
+/*
+ * Unregister a crypto cipher module.
+ */
+void
+ieee80211_crypto_unregister(const struct ieee80211_cipher *cip)
 {
-	struct ieee80211com *ic = (void *)ifp;
-	struct mbuf *m, *n, *n0;
-	struct ieee80211_frame *wh;
-	int i, left, len, moff, noff, kid;
-	uint32_t iv, crc;
-	uint8_t *ivp;
-	void *ctx;
-	uint8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
-	uint8_t crcbuf[IEEE80211_WEP_CRCLEN];
-
-	n0 = NULL;
-	if ((ctx = ic->ic_wep_ctx) == NULL) {
-		ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT | M_ZERO); /* XXX */
-		if (ctx == NULL) {
-			ic->ic_stats.is_crypto_nomem++;
-			goto fail;
-		}
-		ic->ic_wep_ctx = ctx;
+	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+		printf("%s: cipher %s has an invalid cipher index %u\n",
+			__func__, cip->ic_name, cip->ic_cipher);
+		return;
 	}
-	m = m0;
-	left = m->m_pkthdr.len;
-	MGET(n, MB_DONTWAIT, m->m_type);
-	n0 = n;
-	if (n == NULL) {
-		if (txflag)
-			ic->ic_stats.is_tx_nombuf++;
-		else
-			ic->ic_stats.is_rx_nombuf++;
-		goto fail;
-	}
-	M_MOVE_PKTHDR(n, m);
-	len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
-	if (txflag) {
-		n->m_pkthdr.len += len;
-	} else {
-		n->m_pkthdr.len -= len;
-		left -= len;
+	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+		printf("%s: cipher %s registered with a different template\n",
+			__func__, cip->ic_name);
+		return;
 	}
-	n->m_len = MHLEN;
-	if (n->m_pkthdr.len >= MINCLSIZE) {
-		MCLGET(n, MB_DONTWAIT);
-		if (n->m_flags & M_EXT)
-			n->m_len = n->m_ext.ext_size;
-	}
-	len = sizeof(struct ieee80211_frame);
-	memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
-	wh = mtod(n, struct ieee80211_frame *);
-	left -= len;
-	moff = len;
-	noff = len;
-	if (txflag) {
-		kid = ic->ic_wep_txkey;
-		wh->i_fc[1] |= IEEE80211_FC1_WEP;
-                iv = ic->ic_iv;
+	/* NB: don't complain about not being registered */
+	/* XXX disallow if references */
+	ciphers[cip->ic_cipher] = NULL;
+}
+
+int
+ieee80211_crypto_available(u_int cipher)
+{
+	return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL;
+}
+
+/* XXX well-known names! */
+static const char *cipher_modnames[] = {
+	"wlan_wep",	/* IEEE80211_CIPHER_WEP */
+	"wlan_tkip",	/* IEEE80211_CIPHER_TKIP */
+	"wlan_aes_ocb",	/* IEEE80211_CIPHER_AES_OCB */
+	"wlan_ccmp",	/* IEEE80211_CIPHER_AES_CCM */
+	"wlan_ckip",	/* IEEE80211_CIPHER_CKIP */
+};
+
+/*
+ * Establish a relationship between the specified key and cipher
+ * and, if necessary, allocate a hardware index from the driver.
+ * Note that when a fixed key index is required it must be specified
+ * and we blindly assign it w/o consulting the driver (XXX).
+ *
+ * This must be the first call applied to a key; all the other key
+ * routines assume wk_cipher is setup.
+ *
+ * Locking must be handled by the caller using:
+ *	ieee80211_key_update_begin(ic);
+ *	ieee80211_key_update_end(ic);
+ */
+int
+ieee80211_crypto_newkey(struct ieee80211com *ic,
+	int cipher, int flags, struct ieee80211_key *key)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	const struct ieee80211_cipher *cip;
+	void *keyctx;
+	int oflags;
+
+	/*
+	 * Validate cipher and set reference to cipher routines.
+	 */
+	if (cipher >= IEEE80211_CIPHER_MAX) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			"%s: invalid cipher %u\n", __func__, cipher);
+		ic->ic_stats.is_crypto_badcipher++;
+		return 0;
+	}
+	cip = ciphers[cipher];
+	if (cip == NULL) {
 		/*
-		 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
-		 * (B, 255, N) with 3 <= B < 8
+		 * Auto-load cipher module if we have a well-known name
+		 * for it.  It might be better to use string names rather
+		 * than numbers and craft a module name based on the cipher
+		 * name; e.g. wlan_cipher_<cipher-name>.
 		 */
-		if (iv >= 0x03ff00 &&
-		    (iv & 0xf8ff00) == 0x00ff00)
-			iv += 0x000100;
-		ic->ic_iv = iv + 1;
-		/* put iv in little endian to prepare 802.11i */
-		ivp = mtod(n, uint8_t *) + noff;
-		for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
-			ivp[i] = iv & 0xff;
-			iv >>= 8;
+		if (cipher < N(cipher_modnames)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				"%s: unregistered cipher %u, load module %s\n",
+				__func__, cipher, cipher_modnames[cipher]);
+			ieee80211_load_module(cipher_modnames[cipher]);
+			/*
+			 * If cipher module loaded it should immediately
+			 * call ieee80211_crypto_register which will fill
+			 * in the entry in the ciphers array.
+			 */
+			cip = ciphers[cipher];
 		}
-		ivp[IEEE80211_WEP_IVLEN] = kid << 6;	/* pad and keyid */
-		noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
-	} else {
-		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
-		ivp = mtod(m, uint8_t *) + moff;
-		kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
-		moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
-	}
-	memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
-	memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
-	    ic->ic_nw_keys[kid].wk_len);
-	arc4_setkey(ctx, keybuf,
-	    IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
-
-	/* encrypt with calculating CRC */
-	crc = ~0;
-	while (left > 0) {
-		len = m->m_len - moff;
-		if (len == 0) {
-			m = m->m_next;
-			moff = 0;
-			continue;
+		if (cip == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				"%s: unable to load cipher %u, module %s\n",
+				__func__, cipher,
+				cipher < N(cipher_modnames) ?
+					cipher_modnames[cipher] : "<unknown>");
+			ic->ic_stats.is_crypto_nocipher++;
+			return 0;
 		}
-		if (len > n->m_len - noff) {
-			len = n->m_len - noff;
-			if (len == 0) {
-				MGET(n->m_next, MB_DONTWAIT, n->m_type);
-				if (n->m_next == NULL) {
-					if (txflag)
-						ic->ic_stats.is_tx_nombuf++;
-					else
-						ic->ic_stats.is_rx_nombuf++;
-					goto fail;
-				}
-				n = n->m_next;
-				n->m_len = MLEN;
-				if (left >= MINCLSIZE) {
-					MCLGET(n, MB_DONTWAIT);
-					if (n->m_flags & M_EXT)
-						n->m_len = n->m_ext.ext_size;
-				}
-				noff = 0;
-				continue;
-			}
+	}
+
+	oflags = key->wk_flags;
+	flags &= IEEE80211_KEY_COMMON;
+	/*
+	 * If the hardware does not support the cipher then
+	 * fallback to a host-based implementation.
+	 */
+	if ((ic->ic_caps & (1<<cipher)) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: no h/w support for cipher %s, falling back to s/w\n",
+		    __func__, cip->ic_name);
+		flags |= IEEE80211_KEY_SWCRYPT;
+	}
+	/*
+	 * Hardware TKIP with software MIC is an important
+	 * combination; we handle it by flagging each key,
+	 * the cipher modules honor it.
+	 */
+	if (cipher == IEEE80211_CIPHER_TKIP &&
+	    (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: no h/w support for TKIP MIC, falling back to s/w\n",
+		    __func__);
+		flags |= IEEE80211_KEY_SWMIC;
+	}
+
+	/*
+	 * Bind cipher to key instance.  Note we do this
+	 * after checking the device capabilities so the
+	 * cipher module can optimize space usage based on
+	 * whether or not it needs to do the cipher work.
+	 */
+	if (key->wk_cipher != cip || key->wk_flags != flags) {
+again:
+		/*
+		 * Fillin the flags so cipher modules can see s/w
+		 * crypto requirements and potentially allocate
+		 * different state and/or attach different method
+		 * pointers.
+		 *
+		 * XXX this is not right when s/w crypto fallback
+		 *     fails and we try to restore previous state.
+		 */
+		key->wk_flags = flags;
+		keyctx = cip->ic_attach(ic, key);
+		if (keyctx == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				"%s: unable to attach cipher %s\n",
+				__func__, cip->ic_name);
+			key->wk_flags = oflags;	/* restore old flags */
+			ic->ic_stats.is_crypto_attachfail++;
+			return 0;
 		}
-		if (len > left)
-			len = left;
-		arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
-		    mtod(m, caddr_t) + moff, len);
-		if (txflag)
-			crc = ieee80211_crc_update(crc,
-			    mtod(m, uint8_t *) + moff, len);
-		else
-			crc = ieee80211_crc_update(crc,
-			    mtod(n, uint8_t *) + noff, len);
-		left -= len;
-		moff += len;
-		noff += len;
-	}
-	crc = ~crc;
-	if (txflag) {
-		*(uint32_t *)crcbuf = htole32(crc);
-		if (n->m_len >= noff + sizeof(crcbuf))
-			n->m_len = noff + sizeof(crcbuf);
-		else {
-			n->m_len = noff;
-			MGET(n->m_next, MB_DONTWAIT, n->m_type);
-			if (n->m_next == NULL) {
-				ic->ic_stats.is_tx_nombuf++;
-				goto fail;
+		cipher_detach(key);
+		key->wk_cipher = cip;		/* XXX refcnt? */
+		key->wk_private = keyctx;
+	}
+	/*
+	 * Commit to requested usage so driver can see the flags.
+	 */
+	key->wk_flags = flags;
+
+	/*
+	 * Ask the driver for a key index if we don't have one.
+	 * Note that entries in the global key table always have
+	 * an index; this means it's safe to call this routine
+	 * for these entries just to setup the reference to the
+	 * cipher template.  Note also that when using software
+	 * crypto we also call the driver to give us a key index.
+	 */
+	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+		key->wk_keyix = dev_key_alloc(ic, key);
+		if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+			/*
+			 * Driver has no room; fallback to doing crypto
+			 * in the host.  We change the flags and start the
+			 * procedure over.  If we get back here then there's
+			 * no hope and we bail.  Note that this can leave
+			 * the key in a inconsistent state if the caller
+			 * continues to use it.
+			 */
+			if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
+				ic->ic_stats.is_crypto_swfallback++;
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				    "%s: no h/w resources for cipher %s, "
+				    "falling back to s/w\n", __func__,
+				    cip->ic_name);
+				oflags = key->wk_flags;
+				flags |= IEEE80211_KEY_SWCRYPT;
+				if (cipher == IEEE80211_CIPHER_TKIP)
+					flags |= IEEE80211_KEY_SWMIC;
+				goto again;
 			}
-			n = n->m_next;
-			n->m_len = sizeof(crcbuf);
-			noff = 0;
+			ic->ic_stats.is_crypto_keyfail++;
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "%s: unable to setup cipher %s\n",
+			    __func__, cip->ic_name);
+			return 0;
 		}
-		arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
-		    sizeof(crcbuf));
-	} else {
-		n->m_len = noff;
-		for (noff = 0; noff < sizeof(crcbuf); noff += len) {
-			len = sizeof(crcbuf) - noff;
-			if (len > m->m_len - moff)
-				len = m->m_len - moff;
-			if (len > 0)
-				arc4_encrypt(ctx, crcbuf + noff,
-				    mtod(m, caddr_t) + moff, len);
-			m = m->m_next;
-			moff = 0;
-		}
-		if (crc != le32toh(*(uint32_t *)crcbuf)) {
-#ifdef IEEE80211_DEBUG
-			if (ieee80211_debug) {
-				if_printf(ifp, "decrypt CRC error\n");
-				if (ieee80211_debug > 1)
-					ieee80211_dump_pkt(n0->m_data,
-					    n0->m_len, -1, -1);
-			}
-#endif
-			ic->ic_stats.is_rx_decryptcrc++;
-			goto fail;
+	}
+	return 1;
+#undef N
+}
+
+/*
+ * Remove the key (no locking, for internal use).
+ */
+static int
+_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	u_int16_t keyix;
+
+	KASSERT(key->wk_cipher != NULL, ("No cipher!"));
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+	    "%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n",
+	    __func__, key->wk_cipher->ic_name,
+	    key->wk_keyix, key->wk_flags,
+	    key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+
+	keyix = key->wk_keyix;
+	if (keyix != IEEE80211_KEYIX_NONE) {
+		/*
+		 * Remove hardware entry.
+		 */
+		/* XXX key cache */
+		if (!dev_key_delete(ic, key)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "%s: driver did not delete key index %u\n",
+			    __func__, keyix);
+			ic->ic_stats.is_crypto_delkey++;
+			/* XXX recovery? */
 		}
 	}
-	m_freem(m0);
-	return n0;
+	cipher_detach(key);
+	memset(key, 0, sizeof(*key));
+	ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE);
+	return 1;
+}
+
+/*
+ * Remove the specified key.
+ */
+int
+ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	int status;
 
-  fail:
-	m_freem(m0);
-	m_freem(n0);
-	return NULL;
+	ieee80211_key_update_begin(ic);
+	status = _ieee80211_crypto_delkey(ic, key);
+	ieee80211_key_update_end(ic);
+	return status;
 }
 
 /*
- * CRC 32 -- routine from RFC 2083
+ * Clear the global key table.
+ */
+void
+ieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
+{
+	int i;
+
+	ieee80211_key_update_begin(ic);
+	for (i = 0; i < IEEE80211_WEP_NKID; i++)
+		(void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
+	ieee80211_key_update_end(ic);
+}
+
+/*
+ * Set the contents of the specified key.
+ *
+ * Locking must be handled by the caller using:
+ *	ieee80211_key_update_begin(ic);
+ *	ieee80211_key_update_end(ic);
  */
+int
+ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
+		const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+{
+	const struct ieee80211_cipher *cip = key->wk_cipher;
+
+	KASSERT(cip != NULL, ("No cipher!"));
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+	    "%s: %s keyix %u flags 0x%x mac %6D rsc %ju tsc %ju len %u\n",
+	    __func__, cip->ic_name, key->wk_keyix,
+	    key->wk_flags, (macaddr), ":",
+	    key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
 
-/* Table of CRCs of all 8-bit messages */
-static uint32_t ieee80211_crc_table[256];
+	/*
+	 * Give cipher a chance to validate key contents.
+	 * XXX should happen before modifying state.
+	 */
+	if (!cip->ic_setkey(key)) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: cipher %s rejected key index %u len %u flags 0x%x\n",
+		    __func__, cip->ic_name, key->wk_keyix,
+		    key->wk_keylen, key->wk_flags);
+		ic->ic_stats.is_crypto_setkey_cipher++;
+		return 0;
+	}
+	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+		/* XXX nothing allocated, should not happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: no key index; should not happen!\n", __func__);
+		ic->ic_stats.is_crypto_setkey_nokey++;
+		return 0;
+	}
+	return dev_key_set(ic, key, macaddr);
+}
 
-/* Make the table for a fast CRC. */
-static void
-ieee80211_crc_init(void)
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+struct ieee80211_key *
+ieee80211_crypto_encap(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m)
 {
-	uint32_t c;
-	int n, k;
+	struct ieee80211_key *k;
+	struct ieee80211_frame *wh;
+	const struct ieee80211_cipher *cip;
+	u_int8_t keyid;
 
-	for (n = 0; n < 256; n++) {
-		c = (uint32_t)n;
-		for (k = 0; k < 8; k++) {
-			if (c & 1)
-				c = 0xedb88320UL ^ (c >> 1);
-			else
-				c = c >> 1;
+	/*
+	 * Multicast traffic always uses the multicast key.
+	 * Otherwise if a unicast key is set we use that and
+	 * it is always key index 0.  When no unicast key is
+	 * set we fall back to the default transmit key.
+	 */
+	wh = mtod(m, struct ieee80211_frame *);
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "[%6D] no default transmit key (%s) deftxkey %u\n",
+			    (wh->i_addr1), ":", __func__,
+			    ic->ic_def_txkey);
+			ic->ic_stats.is_tx_nodefkey++;
+			return NULL;
 		}
-		ieee80211_crc_table[n] = c;
+		keyid = ic->ic_def_txkey;
+		k = &ic->ic_nw_keys[ic->ic_def_txkey];
+	} else {
+		keyid = 0;
+		k = &ni->ni_ucastkey;
 	}
+	cip = k->wk_cipher;
+	return (cip->ic_encap(k, m, keyid<<6) ? k : NULL);
 }
 
 /*
- * Update a running CRC with the bytes buf[0..len-1]--the CRC
- * should be initialized to all 1's, and the transmitted value
- * is the 1's complement of the final running CRC
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame that has the WEP/Privacy bit set.
  */
+struct ieee80211_key *
+ieee80211_crypto_decap(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m)
+{
+#define	IEEE80211_WEP_HDRLEN	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
+#define	IEEE80211_WEP_MINLEN \
+	(sizeof(struct ieee80211_frame) + ETHER_HDR_LEN + \
+	IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
+	struct ieee80211_key *k;
+	struct ieee80211_frame *wh;
+	const struct ieee80211_cipher *cip;
+	const u_int8_t *ivp;
+	u_int8_t keyid;
+	int hdrlen;
+
+	/* NB: this minimum size data frame could be bigger */
+	if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: WEP data frame too short, len %u\n",
+			__func__, m->m_pkthdr.len);
+		ic->ic_stats.is_rx_tooshort++;	/* XXX need unique stat? */
+		return NULL;
+	}
 
-static uint32_t
-ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len)
+	/*
+	 * Locate the key. If unicast and there is no unicast
+	 * key then we fall back to the key id in the header.
+	 * This assumes unicast keys are only configured when
+	 * the key id in the header is meaningless (typically 0).
+	 */
+	wh = mtod(m, struct ieee80211_frame *);
+	hdrlen = ieee80211_hdrsize(wh);
+	ivp = mtod(m, const u_int8_t *) + hdrlen;	/* XXX contig */
+	keyid = ivp[IEEE80211_WEP_IVLEN];
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none)
+		k = &ic->ic_nw_keys[keyid >> 6];
+	else
+		k = &ni->ni_ucastkey;
+
+	/*
+	 * Insure crypto header is contiguous for all decap work.
+	 */
+	cip = k->wk_cipher;
+	if (m->m_len < hdrlen + cip->ic_header &&
+	    (m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "[%6D] unable to pullup %s header\n",
+		    (wh->i_addr2), ":", cip->ic_name);
+		ic->ic_stats.is_rx_wepfail++;	/* XXX */
+		return 0;
+	}
+
+	return (cip->ic_decap(k, m) ? k : NULL);
+#undef IEEE80211_WEP_MINLEN
+#undef IEEE80211_WEP_HDRLEN
+}
+
+/*
+ * Append the specified data to the indicated mbuf chain,
+ * Extend the mbuf chain if the new data does not fit in
+ * existing space.
+ *
+ * Return 1 if able to complete the job; otherwise 0.
+ */
+int
+m_append(struct mbuf *m0, int len, c_caddr_t cp)
 {
-	uint8_t *endbuf;
+	struct mbuf *m, *n;
+	int remainder, space;
 
-	for (endbuf = buf + len; buf < endbuf; buf++)
-		crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
-	return crc;
+	for (m = m0; m->m_next != NULL; m = m->m_next)
+		;
+	remainder = len;
+	space = M_TRAILINGSPACE(m);
+	if (space > 0) {
+		/*
+		 * Copy into available space.
+		 */
+		if (space > remainder)
+			space = remainder;
+		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
+		m->m_len += space;
+		cp += space, remainder -= space;
+	}
+	while (remainder > 0) {
+		/*
+		 * Allocate a new mbuf; could check space
+		 * and allocate a cluster instead.
+		 */
+		n = m_get(MB_DONTWAIT, m->m_type);
+		if (n == NULL)
+			break;
+		n->m_len = min(MLEN, remainder);
+		bcopy(cp, mtod(n, caddr_t), n->m_len);
+		cp += n->m_len, remainder -= n->m_len;
+		m->m_next = n;
+		m = n;
+	}
+	if (m0->m_flags & M_PKTHDR)
+		m0->m_pkthdr.len += len - remainder;
+	return (remainder == 0);
 }
+
+
Index: src/sys/netproto/802_11/ieee80211_crypto.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_crypto.h,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_crypto.h
--- src/sys/netproto/802_11/ieee80211_crypto.h	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_crypto.h	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,24 +29,206 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.2 2003/06/27 05:13:52 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_CRYPTO_H_
-#define	_NETPROTO_802_11_IEEE80211_CRYPTO_H_
+#ifndef _NET80211_IEEE80211_CRYPTO_H_
+#define _NET80211_IEEE80211_CRYPTO_H_
 
 /*
  * 802.11 protocol crypto-related definitions.
  */
 #define	IEEE80211_KEYBUF_SIZE	16
+#define	IEEE80211_MICBUF_SIZE	(8+8)	/* space for both tx+rx keys */
 
+/*
+ * Old WEP-style key.  Deprecated.
+ */
 struct ieee80211_wepkey {
-	int			wk_len;
-	uint8_t			wk_key[IEEE80211_KEYBUF_SIZE];
+	u_int		wk_len;		/* key length in bytes */
+	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE];
+};
+
+struct ieee80211_cipher;
+
+/*
+ * Crypto key state.  There is sufficient room for all supported
+ * ciphers (see below).  The underlying ciphers are handled
+ * separately through loadable cipher modules that register with
+ * the generic crypto support.  A key has a reference to an instance
+ * of the cipher; any per-key state is hung off wk_private by the
+ * cipher when it is attached.  Ciphers are automatically called
+ * to detach and cleanup any such state when the key is deleted.
+ *
+ * The generic crypto support handles encap/decap of cipher-related
+ * frame contents for both hardware- and software-based implementations.
+ * A key requiring software crypto support is automatically flagged and
+ * the cipher is expected to honor this and do the necessary work.
+ * Ciphers such as TKIP may also support mixed hardware/software
+ * encrypt/decrypt and MIC processing.
+ */
+/* XXX need key index typedef */
+/* XXX pack better? */
+/* XXX 48-bit rsc/tsc */
+struct ieee80211_key {
+	u_int8_t	wk_keylen;	/* key length in bytes */
+	u_int8_t	wk_flags;
+#define	IEEE80211_KEY_XMIT	0x01	/* key used for xmit */
+#define	IEEE80211_KEY_RECV	0x02	/* key used for recv */
+#define	IEEE80211_KEY_GROUP	0x04	/* key used for WPA group operation */
+#define	IEEE80211_KEY_SWCRYPT	0x10	/* host-based encrypt/decrypt */
+#define	IEEE80211_KEY_SWMIC	0x20	/* host-based enmic/demic */
+	u_int16_t	wk_keyix;	/* key index */
+	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+#define	wk_txmic	wk_key+IEEE80211_KEYBUF_SIZE+0	/* XXX can't () right */
+#define	wk_rxmic	wk_key+IEEE80211_KEYBUF_SIZE+8	/* XXX can't () right */
+	u_int64_t	wk_keyrsc;	/* key receive sequence counter */
+	u_int64_t	wk_keytsc;	/* key transmit sequence counter */
+	const struct ieee80211_cipher *wk_cipher;
+	void		*wk_private;	/* private cipher state */
+};
+#define	IEEE80211_KEY_COMMON 		/* common flags passed in by apps */\
+	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP)
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering.  In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define	IEEE80211_CIPHER_WEP		0
+#define	IEEE80211_CIPHER_TKIP		1
+#define	IEEE80211_CIPHER_AES_OCB	2
+#define	IEEE80211_CIPHER_AES_CCM	3
+#define	IEEE80211_CIPHER_CKIP		5
+#define	IEEE80211_CIPHER_NONE		6	/* pseudo value */
+
+#define	IEEE80211_CIPHER_MAX		(IEEE80211_CIPHER_NONE+1)
+
+#define	IEEE80211_KEYIX_NONE	((u_int16_t) -1)
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+
+struct ieee80211com;
+struct ieee80211_node;
+struct mbuf;
+
+/*
+ * Crypto state kept in each ieee80211com.  Some of this
+ * can/should be shared when virtual AP's are supported.
+ *
+ * XXX save reference to ieee80211com to properly encapsulate state.
+ * XXX split out crypto capabilities from ic_caps
+ */
+struct ieee80211_crypto_state {
+	struct ieee80211_key	cs_nw_keys[IEEE80211_WEP_NKID];
+	u_int16_t		cs_def_txkey;	/* default/group tx key index */
+
+	int			(*cs_key_alloc)(struct ieee80211com *,
+					const struct ieee80211_key *);
+	int			(*cs_key_delete)(struct ieee80211com *, 
+					const struct ieee80211_key *);
+	int			(*cs_key_set)(struct ieee80211com *,
+					const struct ieee80211_key *,
+					const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	void			(*cs_key_update_begin)(struct ieee80211com *);
+	void			(*cs_key_update_end)(struct ieee80211com *);
 };
 
-void		ieee80211_crypto_attach(struct ifnet *);
-void		ieee80211_crypto_detach(struct ifnet *);
-struct mbuf	*ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int);
-#endif /* _NETPROTO_802_11_IEEE80211_CRYPTO_H_ */
+void	ieee80211_crypto_attach(struct ieee80211com *);
+void	ieee80211_crypto_detach(struct ieee80211com *);
+int	ieee80211_crypto_newkey(struct ieee80211com *,
+		int cipher, int flags, struct ieee80211_key *);
+int	ieee80211_crypto_delkey(struct ieee80211com *,
+		struct ieee80211_key *);
+int	ieee80211_crypto_setkey(struct ieee80211com *,
+		struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]);
+void	ieee80211_crypto_delglobalkeys(struct ieee80211com *);
+
+/*
+ * Template for a supported cipher.  Ciphers register with the
+ * crypto code and are typically loaded as separate modules
+ * (the null cipher is always present).
+ * XXX may need refcnts
+ */
+struct ieee80211_cipher {
+	const char *ic_name;		/* printable name */
+	u_int	ic_cipher;		/* IEEE80211_CIPHER_* */
+	u_int	ic_header;		/* size of privacy header (bytes) */
+	u_int	ic_trailer;		/* size of privacy trailer (bytes) */
+	u_int	ic_miclen;		/* size of mic trailer (bytes) */
+	void*	(*ic_attach)(struct ieee80211com *, struct ieee80211_key *);
+	void	(*ic_detach)(struct ieee80211_key *);
+	int	(*ic_setkey)(struct ieee80211_key *);
+	int	(*ic_encap)(struct ieee80211_key *, struct mbuf *,
+			u_int8_t keyid);
+	int	(*ic_decap)(struct ieee80211_key *, struct mbuf *);
+	int	(*ic_enmic)(struct ieee80211_key *, struct mbuf *);
+	int	(*ic_demic)(struct ieee80211_key *, struct mbuf *);
+};
+extern	const struct ieee80211_cipher ieee80211_cipher_none;
+
+void	ieee80211_crypto_register(const struct ieee80211_cipher *);
+void	ieee80211_crypto_unregister(const struct ieee80211_cipher *);
+int	ieee80211_crypto_available(u_int cipher);
+
+struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *,
+		struct ieee80211_node *, struct mbuf *);
+struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *,
+		struct ieee80211_node *, struct mbuf *);
+
+/*
+ * Check and remove any MIC.
+ */
+static __inline int
+ieee80211_crypto_demic(struct ieee80211com *ic __unused, struct ieee80211_key *k,
+	struct mbuf *m)
+{
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	return (cip->ic_miclen > 0 ? cip->ic_demic(k, m) : 1);
+}
+
+/*
+ * Add any MIC.
+ */
+static __inline int
+ieee80211_crypto_enmic(struct ieee80211com *ic __unused,
+	struct ieee80211_key *k, struct mbuf *m)
+{
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m) : 1);
+}
+
+/* 
+ * Reset key state to an unused state.  The crypto
+ * key allocation mechanism insures other state (e.g.
+ * key data) is properly setup before a key is used.
+ */
+static __inline void
+ieee80211_crypto_resetkey(struct ieee80211com *ic,
+	struct ieee80211_key *k, u_int16_t ix)
+{
+	k->wk_cipher = &ieee80211_cipher_none;;
+	k->wk_private = k->wk_cipher->ic_attach(ic, k);
+	k->wk_keyix = ix;
+	k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+}
+
+/*
+ * Crypt-related notification methods.
+ */
+void	ieee80211_notify_replay_failure(struct ieee80211com *,
+		const struct ieee80211_frame *, const struct ieee80211_key *,
+		u_int64_t rsc);
+void	ieee80211_notify_michael_failure(struct ieee80211com *,
+		const struct ieee80211_frame *, u_int keyix);
+
+/*
+ * Append the specified data to the indicated mbuf chain,
+ * Extend the mbuf chain if the new data does not fit in
+ * existing space.
+ *
+ * Return 1 if able to complete the job; otherwise 0.
+ */
+int m_append(struct mbuf *m0, int len, c_caddr_t cp);
+
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
Index: src/sys/netproto/802_11/ieee80211_input.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_input.c,v
retrieving revision 1.2
diff -u -r1.2 ieee80211_input.c
--- src/sys/netproto/802_11/ieee80211_input.c	26 Jan 2005 00:37:40 -0000	1.2
+++ src/sys/netproto/802_11/ieee80211_input.c	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,43 +28,93 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.21 2004/06/13 17:29:09 mlaier Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_input.c,v 1.2 2005/01/26 00:37:40 joerg Exp $
  */
 
-#include "opt_inet.h"
+#include <sys/cdefs.h>
 
 #include <sys/param.h>
-#include <sys/systm.h> 
+#include <sys/systm.h>
 #include <sys/mbuf.h>   
 #include <sys/malloc.h>
+#include <sys/endian.h>
 #include <sys/kernel.h>
+ 
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
+#include <sys/thread.h>
 
-#include <machine/atomic.h>
- 
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
 #include <net/ethernet.h>
 #include <net/if_llc.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/vlan/if_vlan_var.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
 #include <net/bpf.h>
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
+#ifdef IEEE80211_DEBUG
+#include <machine/stdarg.h>
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled.  This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211com *ic, int subtype)
+{
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_BEACON:
+		return (ic->ic_flags & IEEE80211_F_SCAN);
+	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+		return (ic->ic_opmode == IEEE80211_M_IBSS);
+	}
+	return 1;
+}
+
+/*
+ * Emit a debug message about discarding a frame or information
+ * element.  One format is for extracting the mac address from
+ * the frame header; the other is for when a header is not
+ * available or otherwise appropriate.
+ */
+#define	IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do {		\
+	if ((_ic)->ic_debug & (_m))					\
+		ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+} while (0)
+#define	IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do {	\
+	if ((_ic)->ic_debug & (_m))					\
+		ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+} while (0)
+#define	IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do {	\
+	if ((_ic)->ic_debug & (_m))					\
+		ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\
+} while (0)
+
+static const u_int8_t *ieee80211_getbssid(struct ieee80211com *,
+	const struct ieee80211_frame *);
+static void ieee80211_discard_frame(struct ieee80211com *,
+	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+static void ieee80211_discard_ie(struct ieee80211com *,
+	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+static void ieee80211_discard_mac(struct ieee80211com *,
+	const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type,
+	const char *fmt, ...);
+#else
+#define	IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...)
+#define	IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...)
+#define	IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...)
+#endif /* IEEE80211_DEBUG */
+
+static struct mbuf *ieee80211_defrag(struct ieee80211com *,
+	struct ieee80211_node *, struct mbuf *);
+static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *);
+static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
+static void ieee80211_recv_pspoll(struct ieee80211com *,
+	struct ieee80211_node *, struct mbuf *);
 
 /*
  * Process a received frame.  The node associated with the sender
@@ -76,22 +126,25 @@
  * mean ``better signal''.  The receive timestamp is currently not used
  * by the 802.11 layer.
  */
-void
-ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
-	int rssi, uint32_t rstamp)
+int
+ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
+	struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
 {
-	struct ieee80211com *ic = (void *)ifp;
+#define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
+#define	HAS_SEQ(type)	((type & 0x4) == 0)
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_frame *wh;
+	struct ieee80211_key *key;
 	struct ether_header *eh;
-	struct mbuf *m1;
-	int len;
-	uint8_t dir, type, subtype;
-	uint8_t *bssid;
-	uint16_t rxseq;
+	int len, hdrsize, off;
+	u_int8_t dir, type, subtype;
+	u_int8_t *bssid;
+	u_int16_t rxseq;
 
 	KASSERT(ni != NULL, ("null node"));
+	ni->ni_inact = ni->ni_inact_reload;
 
-#ifdef M_HASFCS
+#if notyet
 	/* trim CRC here so WEP can find its own CRC at the end of packet. */
 	if (m->m_flags & M_HASFCS) {
 		m_adj(m, -IEEE80211_CRC_LEN);
@@ -101,6 +154,7 @@
 	KASSERT(m->m_pkthdr.len >= sizeof(struct ieee80211_frame_min),
 		("frame length too short: %u", m->m_pkthdr.len));
 
+	type = -1;			/* undefined */
 	/*
 	 * In monitor mode, send everything directly to bpf.
 	 * XXX may want to include the CRC
@@ -108,38 +162,40 @@
 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
 		goto out;
 
+	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, NULL,
+		    "too short (1): len %u", m->m_pkthdr.len);
+		ic->ic_stats.is_rx_tooshort++;
+		goto out;
+	}
+	/*
+	 * Bit of a cheat here, we use a pointer for a 3-address
+	 * frame format but don't reference fields past outside
+	 * ieee80211_frame_min w/o first validating the data is
+	 * present.
+	 */
 	wh = mtod(m, struct ieee80211_frame *);
+
 	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
 	    IEEE80211_FC0_VERSION_0) {
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "receive packet with wrong version: %x\n",
-			    wh->i_fc[0]);
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
 		ic->ic_stats.is_rx_badversion++;
 		goto err;
 	}
 
 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
-	/*
-	 * NB: We are not yet prepared to handle control frames,
-	 *     but permitting drivers to send them to us allows
-	 *     them to go through bpf tapping at the 802.11 layer.
-	 */
-	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
-		/* XXX statistic */
-		IEEE80211_DPRINTF2(("%s: frame too short, len %u\n",
-			__func__, m->m_pkthdr.len));
-		ic->ic_stats.is_rx_tooshort++;
-		goto out;		/* XXX */
-	}
-	if (ic->ic_state != IEEE80211_S_SCAN) {
+	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
-			if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
+			bssid = wh->i_addr2;
+			if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
 				/* not interested in */
-				IEEE80211_DPRINTF2(("%s: discard frame from "
-					"bss %6D\n", __func__,
-					wh->i_addr2, ":"));
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    bssid, NULL, "%s", "not to bss");
 				ic->ic_stats.is_rx_wrongbss++;
 				goto out;
 			}
@@ -147,43 +203,118 @@
 		case IEEE80211_M_IBSS:
 		case IEEE80211_M_AHDEMO:
 		case IEEE80211_M_HOSTAP:
-			if (dir == IEEE80211_FC1_DIR_NODS)
-				bssid = wh->i_addr3;
-			else
+			if (dir != IEEE80211_FC1_DIR_NODS)
 				bssid = wh->i_addr1;
+			else if (type == IEEE80211_FC0_TYPE_CTL)
+				bssid = wh->i_addr1;
+			else {
+				if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+					IEEE80211_DISCARD_MAC(ic,
+					    IEEE80211_MSG_ANY, ni->ni_macaddr,
+					    NULL, "too short (2): len %u",
+					    m->m_pkthdr.len);
+					ic->ic_stats.is_rx_tooshort++;
+					goto out;
+				}
+				bssid = wh->i_addr3;
+			}
+			if (type != IEEE80211_FC0_TYPE_DATA)
+				break;
+			/*
+			 * Data frame, validate the bssid.
+			 */
 			if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
 			    !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
 				/* not interested in */
-				IEEE80211_DPRINTF2(("%s: discard frame from "
-					"bss %6D\n", __func__,
-					bssid, ":"));
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    bssid, NULL, "%s", "not to bss");
 				ic->ic_stats.is_rx_wrongbss++;
 				goto out;
 			}
+			/*
+			 * For adhoc mode we cons up a node when it doesn't
+			 * exist. This should probably done after an ACL check.
+			 */
+			if (ni == ic->ic_bss &&
+			    ic->ic_opmode != IEEE80211_M_HOSTAP) {
+				/*
+				 * Fake up a node for this newly
+				 * discovered member of the IBSS.
+				 */
+				ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
+						    wh->i_addr2);
+				if (ni == NULL) {
+					/* NB: stat kept for alloc failure */
+					goto err;
+				}
+			}
 			break;
-		case IEEE80211_M_MONITOR:
-			goto out;
 		default:
-			/* XXX catch bad values */
-			break;
+			goto out;
 		}
 		ni->ni_rssi = rssi;
 		ni->ni_rstamp = rstamp;
-		rxseq = ni->ni_rxseq;
-		ni->ni_rxseq =
-		    le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
-		/* TODO: fragment */
-		if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
-		    rxseq == ni->ni_rxseq) {
-			/* duplicate, silently discarded */
-			ic->ic_stats.is_rx_dup++; /* XXX per-station stat */
-			goto out;
+		if (HAS_SEQ(type)) {
+			u_int8_t tid;
+			if (IEEE80211_QOS_HAS_SEQ(wh)) {
+				tid = ((struct ieee80211_qosframe *)wh)->
+					i_qos[0] & IEEE80211_QOS_TID;
+				if (tid >= WME_AC_VI)
+					ic->ic_wme.wme_hipri_traffic++;
+				tid++;
+			} else
+				tid = 0;
+			rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+			if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+			    SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+				/* duplicate, discard */
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    bssid, "duplicate",
+				    "seqno <%u,%u> fragno <%u,%u> tid %u",
+				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+				    ni->ni_rxseqs[tid] >>
+					IEEE80211_SEQ_SEQ_SHIFT,
+				    rxseq & IEEE80211_SEQ_FRAG_MASK,
+				    ni->ni_rxseqs[tid] &
+					IEEE80211_SEQ_FRAG_MASK,
+				    tid);
+				ic->ic_stats.is_rx_dup++;
+				IEEE80211_NODE_STAT(ni, rx_dup);
+				goto out;
+			}
+			ni->ni_rxseqs[tid] = rxseq;
 		}
-		ni->ni_inact = 0;
 	}
 
 	switch (type) {
 	case IEEE80211_FC0_TYPE_DATA:
+		hdrsize = ieee80211_hdrsize(wh);
+		if (ic->ic_flags & IEEE80211_F_DATAPAD)
+			hdrsize = roundup(hdrsize, sizeof(u_int32_t));
+		if (m->m_len < hdrsize &&
+		    (m = m_pullup(m, hdrsize)) == NULL) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+			    ni->ni_macaddr, NULL,
+			    "data too short: expecting %u", hdrsize);
+			ic->ic_stats.is_rx_tooshort++;
+			goto out;		/* XXX */
+		}
+		if (subtype & IEEE80211_FC0_SUBTYPE_QOS) {
+			/* XXX discard if node w/o IEEE80211_NODE_QOS? */
+			/*
+			 * Strip QoS control and any padding so only a
+			 * stock 802.11 header is at the front.
+			 */
+			/* XXX 4-address QoS frame */
+			off = hdrsize - sizeof(struct ieee80211_frame);
+			ovbcopy(mtod(m, u_int8_t *), mtod(m, u_int8_t *) + off,
+				hdrsize - off);
+			m_adj(m, off);
+			wh = mtod(m, struct ieee80211_frame *);
+			wh->i_fc[0] &= ~IEEE80211_FC0_SUBTYPE_QOS;
+		} else {
+			/* XXX copy up for 4-address frames w/ padding */
+		}
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
 			if (dir != IEEE80211_FC1_DIR_FROMDS) {
@@ -199,6 +330,8 @@
 				 * It should be silently discarded for
 				 * SIMPLEX interface.
 				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, NULL, "%s", "multicast echo");
 				ic->ic_stats.is_rx_mcastecho++;
 				goto out;
 			}
@@ -209,6 +342,7 @@
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
+			/* XXX no power-save support */
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (dir != IEEE80211_FC1_DIR_TODS) {
@@ -217,63 +351,157 @@
 			}
 			/* check if source STA is associated */
 			if (ni == ic->ic_bss) {
-				IEEE80211_DPRINTF(("%s: data from unknown src "
-					"%6D\n", __func__,
-					wh->i_addr2, ":"));
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "data", "%s", "unknown src");
 				/* NB: caller deals with reference */
-				ni = ieee80211_dup_bss(ic, wh->i_addr2);
+				ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
 				if (ni != NULL) {
 					IEEE80211_SEND_MGMT(ic, ni,
 					    IEEE80211_FC0_SUBTYPE_DEAUTH,
 					    IEEE80211_REASON_NOT_AUTHED);
-					ieee80211_free_node(ic, ni);
+					ieee80211_free_node(ni);
 				}
 				ic->ic_stats.is_rx_notassoc++;
 				goto err;
 			}
 			if (ni->ni_associd == 0) {
-				IEEE80211_DPRINTF(("ieee80211_input: "
-				    "data from unassoc src %6D\n",
-				    wh->i_addr2, ":"));
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "data", "%s", "unassoc src");
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_DISASSOC,
 				    IEEE80211_REASON_NOT_ASSOCED);
-				ieee80211_unref_node(&ni);
 				ic->ic_stats.is_rx_notassoc++;
 				goto err;
 			}
+
+			/*
+			 * Check for power save state change.
+			 */
+			if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+			    (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
+				ieee80211_node_pwrsave(ni,
+					wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
 			break;
-		case IEEE80211_M_MONITOR:
-			break;
+		default:
+			/* XXX here to keep compiler happy */
+			goto out;
 		}
+
+		/*
+		 * Handle privacy requirements.  Note that we
+		 * must not be preempted from here until after
+		 * we (potentially) call ieee80211_crypto_demic;
+		 * otherwise we may violate assumptions in the
+		 * crypto cipher modules used to do delayed update
+		 * of replay sequence numbers.
+		 */
 		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
-			if (ic->ic_flags & IEEE80211_F_WEPON) {
-				m = ieee80211_wep_crypt(ifp, m, 0);
-				if (m == NULL) {
-					ic->ic_stats.is_rx_wepfail++;
-					goto err;
-				}
-				wh = mtod(m, struct ieee80211_frame *);
-			} else {
-				ic->ic_stats.is_rx_nowep++;
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				/*
+				 * Discard encrypted frames when privacy is off.
+				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "WEP", "%s", "PRIVACY off");
+				ic->ic_stats.is_rx_noprivacy++;
+				IEEE80211_NODE_STAT(ni, rx_noprivacy);
 				goto out;
 			}
+			key = ieee80211_crypto_decap(ic, ni, m);
+			if (key == NULL) {
+				/* NB: stats+msgs handled in crypto_decap */
+				IEEE80211_NODE_STAT(ni, rx_wepfail);
+				goto out;
+			}
+			wh = mtod(m, struct ieee80211_frame *);
+			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+		} else {
+			key = NULL;
 		}
+
+		/*
+		 * Next up, any fragmentation.
+		 */
+		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+			m = ieee80211_defrag(ic, ni, m);
+			if (m == NULL) {
+				/* Fragment dropped or frame not complete yet */
+				goto out;
+			}
+		}
+		wh = NULL;		/* no longer valid, catch any uses */
+
+		/*
+		 * Next strip any MSDU crypto bits.
+		 */
+		if (key != NULL && !ieee80211_crypto_demic(ic, key, m)) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+			    ni->ni_macaddr, "data", "%s", "demic error");
+			IEEE80211_NODE_STAT(ni, rx_demicfail);
+			goto out;
+		}
+
 		/* copy to listener after decrypt */
-		if (ic->ic_rawbpf != NULL)
+		if (ic->ic_rawbpf)
 			bpf_mtap(ic->ic_rawbpf, m);
 
-		m = ieee80211_decap(ifp, m);
+		/*
+		 * Finally, strip the 802.11 header.
+		 */
+		m = ieee80211_decap(ic, m);
 		if (m == NULL) {
+			/* don't count Null data frames as errors */
+			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
+				goto out;
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+			    ni->ni_macaddr, "data", "%s", "decap error");
 			ic->ic_stats.is_rx_decap++;
+			IEEE80211_NODE_STAT(ni, rx_decap);
 			goto err;
 		}
+		eh = mtod(m, struct ether_header *);
+		if (!ieee80211_node_is_authorized(ni)) {
+			/*
+			 * Deny any non-PAE frames received prior to
+			 * authorization.  For open/shared-key
+			 * authentication the port is mark authorized
+			 * after authentication completes.  For 802.1x
+			 * the port is not marked authorized by the
+			 * authenticator until the handshake has completed.
+			 */
+			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    eh->ether_shost, "data",
+				    "unauthorized port: ether type 0x%x len %u",
+				    eh->ether_type, m->m_pkthdr.len);
+				ic->ic_stats.is_rx_unauth++;
+				IEEE80211_NODE_STAT(ni, rx_unauth);
+				goto err;
+			}
+		} else {
+			/*
+			 * When denying unencrypted frames, discard
+			 * any non-PAE frames received without encryption.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
+			    key == NULL &&
+			    eh->ether_type != htons(ETHERTYPE_PAE)) {
+				/*
+				 * Drop unencrypted frames.
+				 */
+				ic->ic_stats.is_rx_unencrypted++;
+				IEEE80211_NODE_STAT(ni, rx_unencrypted);
+				goto out;
+			}
+		}
 		ifp->if_ipackets++;
+		IEEE80211_NODE_STAT(ni, rx_data);
+		IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
 
 		/* perform as a bridge within the AP */
-		m1 = NULL;
-		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-			eh = mtod(m, struct ether_header *);
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		    (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
+			struct mbuf *m1 = NULL;
+
 			if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
 				m1 = m_copypacket(m, MB_DONTWAIT);
 				if (m1 == NULL)
@@ -281,13 +509,19 @@
 				else
 					m1->m_flags |= M_MCAST;
 			} else {
-				ni = ieee80211_find_node(ic, eh->ether_dhost);
-				if (ni != NULL) {
-					if (ni->ni_associd != 0) {
+				/* XXX this dups work done in ieee80211_encap */
+				/* check if destination is associated */
+				struct ieee80211_node *ni1 =
+				    ieee80211_find_node(&ic->ic_sta,
+							eh->ether_dhost);
+				if (ni1 != NULL) {
+					/* XXX check if authorized */
+					if (ni1->ni_associd != 0) {
 						m1 = m;
 						m = NULL;
 					}
-					ieee80211_free_node(ic, ni);
+					/* XXX statistic? */
+					ieee80211_free_node(ni1);
 				}
 			}
 			if (m1 != NULL) {
@@ -298,97 +532,206 @@
 				ifp->if_obytes += len;
 			}
 		}
-		if (m != NULL)
+		if (m != NULL) {
+			if (ni->ni_vlan != 0) {
+#if notyet
+				/* attach vlan tag */
+				/* XXX goto err? */
+				VLAN_INPUT_TAG(ifp, m, ni->ni_vlan, goto out);
+#endif
+			}
 			(*ifp->if_input)(ifp, m);
-		return;
+		}
+		return IEEE80211_FC0_TYPE_DATA;
 
 	case IEEE80211_FC0_TYPE_MGT:
+		IEEE80211_NODE_STAT(ni, rx_mgmt);
 		if (dir != IEEE80211_FC1_DIR_NODS) {
 			ic->ic_stats.is_rx_wrongdir++;
 			goto err;
 		}
-		if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
-			ic->ic_stats.is_rx_ahdemo_mgt++;
+		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+			    ni->ni_macaddr, "mgt", "too short: len %u",
+			    m->m_pkthdr.len);
+			ic->ic_stats.is_rx_tooshort++;
 			goto out;
 		}
-		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
-
-		/* drop frames without interest */
-		if (ic->ic_state == IEEE80211_S_SCAN) {
-			if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
-			    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
-				ic->ic_stats.is_rx_mgtdiscard++;
+#ifdef IEEE80211_DEBUG
+		if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
+		    ieee80211_msg_dumppkts(ic)) {
+			if_printf(ic->ic_ifp, "received %s from %6D rssi %d\n",
+			    ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    (wh->i_addr2), ":", rssi);
+		}
+#endif
+		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+				/*
+				 * Only shared key auth frames with a challenge
+				 * should be encrypted, discard all others.
+				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, ieee80211_mgt_subtype_name[subtype >>
+					IEEE80211_FC0_SUBTYPE_SHIFT],
+				    "%s", "WEP set but not permitted");
+				ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
 				goto out;
 			}
-		} else {
-			if (ic->ic_opmode != IEEE80211_M_IBSS &&
-			    subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
-				ic->ic_stats.is_rx_mgtdiscard++;
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				/*
+				 * Discard encrypted frames when privacy is off.
+				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "mgt", "%s", "WEP set but PRIVACY off");
+				ic->ic_stats.is_rx_noprivacy++;
 				goto out;
 			}
-		}
-
-		if (ifp->if_flags & IFF_DEBUG) {
-			/* avoid to print too many frames */
-			int doprint = 0;
-
-			switch (subtype) {
-			case IEEE80211_FC0_SUBTYPE_BEACON:
-				if (ic->ic_state == IEEE80211_S_SCAN)
-					doprint = 1;
-				break;
-			case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
-				if (ic->ic_opmode == IEEE80211_M_IBSS)
-					doprint = 1;
-				break;
-			default:
-				doprint = 1;
-				break;
+			key = ieee80211_crypto_decap(ic, ni, m);
+			if (key == NULL) {
+				/* NB: stats+msgs handled in crypto_decap */
+				goto out;
 			}
-#ifdef IEEE80211_DEBUG
-			doprint += ieee80211_debug;
-#endif
-			if (doprint)
-				if_printf(ifp, "received %s from %6D rssi %d\n",
-				    ieee80211_mgt_subtype_name[subtype
-				    >> IEEE80211_FC0_SUBTYPE_SHIFT],
-				    wh->i_addr2, ":", rssi);
+			wh = mtod(m, struct ieee80211_frame *);
+			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
 		}
-		if (ic->ic_rawbpf != NULL)
+		if (ic->ic_rawbpf)
 			bpf_mtap(ic->ic_rawbpf, m);
 		(*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
 		m_freem(m);
-		return;
+		return type;
 
 	case IEEE80211_FC0_TYPE_CTL:
+		IEEE80211_NODE_STAT(ni, rx_ctrl);
 		ic->ic_stats.is_rx_ctl++;
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			switch (subtype) {
+			case IEEE80211_FC0_SUBTYPE_PS_POLL:
+				ieee80211_recv_pspoll(ic, ni, m);
+				break;
+			}
+		}
 		goto out;
 	default:
-		IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, type));
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+		    wh, NULL, "bad frame type 0x%x", type);
 		/* should not come here */
 		break;
 	}
-  err:
+err:
 	ifp->if_ierrors++;
-  out:
+out:
 	if (m != NULL) {
-		if (ic->ic_rawbpf != NULL)
+		if (ic->ic_rawbpf)
 			bpf_mtap(ic->ic_rawbpf, m);
 		m_freem(m);
 	}
+	return type;
+#undef SEQ_LEQ
 }
 
-struct mbuf *
-ieee80211_decap(struct ifnet *ifp, struct mbuf *m)
+/*
+ * This function reassemble fragments.
+ */
+static struct mbuf *
+ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct mbuf *m)
 {
+	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+	struct ieee80211_frame *lwh;
+	u_int16_t rxseq;
+	u_int8_t fragno;
+	u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
+	struct mbuf *mfrag;
+	IEEE80211_LOCK_INFO;
+
+	KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
+
+	rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+	fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
+
+	/* Quick way out, if there's nothing to defragment */
+	if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
+		return m;
+
+	/*
+	 * Remove frag to insure it doesn't get reaped by timer.
+	 */
+	if (ni->ni_table == NULL) {
+		/*
+		 * Should never happen.  If the node is orphaned (not in
+		 * the table) then input packets should not reach here.
+		 * Otherwise, a concurrent request that yanks the table
+		 * should be blocked by other interlocking and/or by first
+		 * shutting the driver down.  Regardless, be defensive
+		 * here and just bail
+		 */
+		/* XXX need msg+stat */
+		m_freem(m);
+		return NULL;
+	}
+	IEEE80211_NODE_LOCK(ni->ni_table);
+	mfrag = ni->ni_rxfrag[0];
+	ni->ni_rxfrag[0] = NULL;
+	IEEE80211_NODE_UNLOCK(ni->ni_table);
+
+	/*
+	 * Validate new fragment is in order and
+	 * related to the previous ones.
+	 */
+	if (mfrag != NULL) {
+		u_int16_t last_rxseq;
+
+		lwh = mtod(mfrag, struct ieee80211_frame *);
+		last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq);
+		/* NB: check seq # and frag together */
+		if (rxseq != last_rxseq+1 ||
+		    !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
+		    !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
+			/*
+			 * Unrelated fragment or no space for it,
+			 * clear current fragments.
+			 */
+			m_freem(mfrag);
+			mfrag = NULL;
+		}
+	}
+
+ 	if (mfrag == NULL) {
+		if (fragno != 0) {		/* !first fragment, discard */
+			IEEE80211_NODE_STAT(ni, rx_defrag);
+			m_freem(m);
+			return NULL;
+		}
+		mfrag = m;
+	} else {				/* concatenate */
+		m_cat(mfrag, m);
+		/* NB: m_cat doesn't update the packet header */
+		mfrag->m_pkthdr.len += m->m_pkthdr.len;
+		/* track last seqnum and fragno */
+		lwh = mtod(mfrag, struct ieee80211_frame *);
+		*(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq;
+	}
+	if (more_frag) {			/* more to come, save */
+		ni->ni_rxfragstamp = ticks;
+		ni->ni_rxfrag[0] = mfrag;
+		mfrag = NULL;
+	}
+	return mfrag;
+}
+
+static struct mbuf *
+ieee80211_decap(struct ieee80211com *ic, struct mbuf *m)
+{
+	struct ieee80211_frame wh;	/* NB: QoS stripped above */
 	struct ether_header *eh;
-	struct ieee80211_frame wh;
 	struct llc *llc;
 
-	if (m->m_len < sizeof(wh) + sizeof(*llc)) {
-		m = m_pullup(m, sizeof(wh) + sizeof(*llc));
-		if (m == NULL)
-			return NULL;
+	if (m->m_len < sizeof(wh) + sizeof(*llc) &&
+	    (m = m_pullup(m, sizeof(wh) + sizeof(*llc))) == NULL) {
+		/* XXX stat, msg */
+		return NULL;
 	}
 	memcpy(&wh, mtod(m, caddr_t), sizeof(wh));
 	llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh));
@@ -416,12 +759,13 @@
 		break;
 	case IEEE80211_FC1_DIR_DSTODS:
 		/* not yet supported */
-		IEEE80211_DPRINTF(("%s: DS to DS\n", __func__));
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+		    &wh, "data", "%s", "DS to DS not supported");
 		m_freem(m);
 		return NULL;
 	}
 #ifdef ALIGNED_POINTER
-	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
+	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) {
 		struct mbuf *n, *n0, **np;
 		caddr_t newdata;
 		int off, pktlen;
@@ -470,106 +814,899 @@
 		m_freem(m);
 		m = n0;
 	}
-#endif /* ALIGNED_POINTER */
-	if (llc != NULL) {
-		eh = mtod(m, struct ether_header *);
-		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
+#endif /* ALIGNED_POINTER */
+	if (llc != NULL) {
+		eh = mtod(m, struct ether_header *);
+		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
+	}
+	return m;
+}
+
+/*
+ * Install received rate set information in the node's state block.
+ */
+static int
+ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
+	u_int8_t *rates, u_int8_t *xrates, int flags)
+{
+	struct ieee80211_rateset *rs = &ni->ni_rates;
+
+	memset(rs, 0, sizeof(*rs));
+	rs->rs_nrates = rates[1];
+	memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
+	if (xrates != NULL) {
+		u_int8_t nxrates;
+		/*
+		 * Tack on 11g extended supported rate element.
+		 */
+		nxrates = xrates[1];
+		if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
+			nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
+			     "[%6D] extended rate set too large;"
+			     " only using %u of %u rates\n",
+			     (ni->ni_macaddr), ":", nxrates, xrates[1]);
+			ic->ic_stats.is_rx_rstoobig++;
+		}
+		memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
+		rs->rs_nrates += nxrates;
+	}
+	return ieee80211_fix_rate(ic, ni, flags);
+}
+
+static void
+ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
+    struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq,
+    u_int16_t status)
+{
+
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_IBSS:
+		if (ic->ic_state != IEEE80211_S_RUN ||
+		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		ieee80211_new_state(ic, IEEE80211_S_AUTH,
+		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+		break;
+
+	case IEEE80211_M_AHDEMO:
+		/* should not come here */
+		break;
+
+	case IEEE80211_M_HOSTAP:
+		if (ic->ic_state != IEEE80211_S_RUN ||
+		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		/* always accept open authentication requests */
+		if (ni == ic->ic_bss) {
+			ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
+			if (ni == NULL)
+				return;
+		} else
+			(void) ieee80211_ref_node(ni);
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+		    "[%6D] station authenticated (open)\n",
+		    (ni->ni_macaddr), ":");
+		break;
+
+	case IEEE80211_M_STA:
+		if (ic->ic_state != IEEE80211_S_AUTH ||
+		    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		if (status != 0) {
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+			    "[%6D] open auth failed (reason %d)\n",
+			    (ni->ni_macaddr), ":", status);
+			/* XXX can this happen? */
+			if (ni != ic->ic_bss)
+				ni->ni_fails++;
+			ic->ic_stats.is_rx_auth_fail++;
+			return;
+		}
+		ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+		break;
+	case IEEE80211_M_MONITOR:
+		break;
+	}
+}
+
+static int
+alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	if (ni->ni_challenge == NULL)
+		MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN,
+		    M_DEVBUF, M_NOWAIT);
+	if (ni->ni_challenge == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+		    "[%6D] shared key challenge alloc failed\n",
+		    (ni->ni_macaddr), ":");
+		/* XXX statistic */
+	}
+	return (ni->ni_challenge != NULL);
+}
+
+/* XXX TODO: add statistics */
+static void
+ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
+    u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi,
+    u_int32_t rstamp, u_int16_t seq, u_int16_t status)
+{
+	u_int8_t *challenge;
+	int allocbs, estatus;
+
+	/*
+	 * NB: this can happen as we allow pre-shared key
+	 * authentication to be enabled w/o wep being turned
+	 * on so that configuration of these can be done
+	 * in any order.  It may be better to enforce the
+	 * ordering in which case this check would just be
+	 * for sanity/consistency.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "shared key auth",
+		    "%s", " PRIVACY is disabled");
+		estatus = IEEE80211_STATUS_ALG;
+		goto bad;
+	}
+	/*
+	 * Pre-shared key authentication is evil; accept
+	 * it only if explicitly configured (it is supported
+	 * mainly for compatibility with clients like OS X).
+	 */
+	if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+	    ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "shared key auth",
+		    "bad sta auth mode %u", ni->ni_authmode);
+		ic->ic_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
+		estatus = IEEE80211_STATUS_ALG;
+		goto bad;
+	}
+
+	challenge = NULL;
+	if (frm + 1 < efrm) {
+		if ((frm[1] + 2) > (efrm - frm)) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "ie %d/%d too long",
+			    frm[0], (frm[1] + 2) - (efrm - frm));
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+		if (*frm == IEEE80211_ELEMID_CHALLENGE)
+			challenge = frm;
+		frm += frm[1] + 2;
+	}
+	switch (seq) {
+	case IEEE80211_AUTH_SHARED_CHALLENGE:
+	case IEEE80211_AUTH_SHARED_RESPONSE:
+		if (challenge == NULL) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "%s", "no challenge");
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "bad challenge len %d", challenge[1]);
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+	default:
+		break;
+	}
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_MONITOR:
+	case IEEE80211_M_AHDEMO:
+	case IEEE80211_M_IBSS:
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "shared key auth",
+		    "bad operating mode %u", ic->ic_opmode);
+		return;
+	case IEEE80211_M_HOSTAP:
+		if (ic->ic_state != IEEE80211_S_RUN) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "bad state %u", ic->ic_state);
+			estatus = IEEE80211_STATUS_ALG;	/* XXX */
+			goto bad;
+		}
+		switch (seq) {
+		case IEEE80211_AUTH_SHARED_REQUEST:
+			if (ni == ic->ic_bss) {
+				ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
+				if (ni == NULL) {
+					/* NB: no way to return an error */
+					return;
+				}
+				allocbs = 1;
+			} else {
+				(void) ieee80211_ref_node(ni);
+				allocbs = 0;
+			}
+			ni->ni_rssi = rssi;
+			ni->ni_rstamp = rstamp;
+			if (!alloc_challenge(ic, ni)) {
+				/* NB: don't return error so they rexmit */
+				return;
+			}
+			get_random_bytes(ni->ni_challenge,
+				IEEE80211_CHALLENGE_LEN);
+			IEEE80211_DPRINTF(ic,
+				IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+				"[%6D] shared key %sauth request\n",
+				(ni->ni_macaddr), ":",
+				allocbs ? "" : "re");
+			break;
+		case IEEE80211_AUTH_SHARED_RESPONSE:
+			if (ni == ic->ic_bss) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+				    ni->ni_macaddr, "shared key response",
+				    "%s", "unknown station");
+				/* NB: don't send a response */
+				return;
+			}
+			if (ni->ni_challenge == NULL) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+				    ni->ni_macaddr, "shared key response",
+				    "%s", "no challenge recorded");
+				ic->ic_stats.is_rx_bad_auth++;
+				estatus = IEEE80211_STATUS_CHALLENGE;
+				goto bad;
+			}
+			if (memcmp(ni->ni_challenge, &challenge[2],
+			           challenge[1]) != 0) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+				    ni->ni_macaddr, "shared key response",
+				    "%s", "challenge mismatch");
+				ic->ic_stats.is_rx_auth_fail++;
+				estatus = IEEE80211_STATUS_CHALLENGE;
+				goto bad;
+			}
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+			    "[%6D] station authenticated (shared key)\n",
+			    (ni->ni_macaddr), ":");
+			break;
+		default:
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "bad seq %d", seq);
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_SEQUENCE;
+			goto bad;
+		}
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+		break;
+
+	case IEEE80211_M_STA:
+		if (ic->ic_state != IEEE80211_S_AUTH)
+			return;
+		switch (seq) {
+		case IEEE80211_AUTH_SHARED_PASS:
+			if (ni->ni_challenge != NULL) {
+				FREE(ni->ni_challenge, M_DEVBUF);
+				ni->ni_challenge = NULL;
+			}
+			if (status != 0) {
+				IEEE80211_DPRINTF(ic,
+				    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+				    "[%6D] shared key auth failed (reason %d)\n",
+				    (ieee80211_getbssid(ic, wh)), ":",
+				    status);
+				/* XXX can this happen? */
+				if (ni != ic->ic_bss)
+					ni->ni_fails++;
+				ic->ic_stats.is_rx_auth_fail++;
+				return;
+			}
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			break;
+		case IEEE80211_AUTH_SHARED_CHALLENGE:
+			if (!alloc_challenge(ic, ni))
+				return;
+			/* XXX could optimize by passing recvd challenge */
+			memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
+			IEEE80211_SEND_MGMT(ic, ni,
+				IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+			break;
+		default:
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH,
+			    wh, "shared key auth", "bad seq %d", seq);
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		break;
+	}
+	return;
+bad:
+	/*
+	 * Send an error response; but only when operating as an AP.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		/* XXX hack to workaround calling convention */
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH,
+			(seq + 1) | (estatus<<16));
+	}
+}
+
+/* Verify the existence and length of __elem or get out. */
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {			\
+	if ((__elem) == NULL) {						\
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
+		    wh, ieee80211_mgt_subtype_name[subtype >>		\
+			IEEE80211_FC0_SUBTYPE_SHIFT],			\
+		    "%s", "no " #__elem );				\
+		ic->ic_stats.is_rx_elem_missing++;			\
+		return;							\
+	}								\
+	if ((__elem)[1] > (__maxlen)) {					\
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
+		    wh, ieee80211_mgt_subtype_name[subtype >>		\
+			IEEE80211_FC0_SUBTYPE_SHIFT],			\
+		    "bad " #__elem " len %d", (__elem)[1]);		\
+		ic->ic_stats.is_rx_elem_toobig++;			\
+		return;							\
+	}								\
+} while (0)
+
+#define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
+	if ((_len) < (_minlen)) {					\
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
+		    wh, ieee80211_mgt_subtype_name[subtype >>		\
+			IEEE80211_FC0_SUBTYPE_SHIFT],			\
+		    "%s", "ie too short");				\
+		ic->ic_stats.is_rx_elem_toosmall++;			\
+		return;							\
+	}								\
+} while (0)
+
+#ifdef IEEE80211_DEBUG
+static void
+ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
+	u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid)
+{
+	printf("[%6D] discard %s frame, ssid mismatch: ",
+		(mac), ":", tag);
+	ieee80211_print_essid(ssid + 2, ssid[1]);
+	printf("\n");
+}
+
+#define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
+	if ((_ssid)[1] != 0 &&						\
+	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
+		if (ieee80211_msg_input(ic))				\
+			ieee80211_ssid_mismatch(ic, 			\
+			    ieee80211_mgt_subtype_name[subtype >>	\
+				IEEE80211_FC0_SUBTYPE_SHIFT],		\
+				wh->i_addr2, _ssid);			\
+		ic->ic_stats.is_rx_ssidmismatch++;			\
+		return;							\
+	}								\
+} while (0)
+#else /* !IEEE80211_DEBUG */
+#define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
+	if ((_ssid)[1] != 0 &&						\
+	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
+		ic->ic_stats.is_rx_ssidmismatch++;			\
+		return;							\
+	}								\
+} while (0)
+#endif /* !IEEE80211_DEBUG */
+
+/* unalligned little endian access */     
+#define LE_READ_2(p)					\
+	((u_int16_t)					\
+	 ((((const u_int8_t *)(p))[0]      ) |		\
+	  (((const u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)					\
+	((u_int32_t)					\
+	 ((((const u_int8_t *)(p))[0]      ) |		\
+	  (((const u_int8_t *)(p))[1] <<  8) |		\
+	  (((const u_int8_t *)(p))[2] << 16) |		\
+	  (((const u_int8_t *)(p))[3] << 24)))
+
+static int __inline
+iswpaoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+iswmeoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static int __inline
+iswmeparam(const u_int8_t *frm)
+{
+	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+		frm[6] == WME_PARAM_OUI_SUBTYPE;
+}
+
+static int __inline
+iswmeinfo(const u_int8_t *frm)
+{
+	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+		frm[6] == WME_INFO_OUI_SUBTYPE;
+}
+
+static int __inline
+isatherosoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+/*
+ * Convert a WPA cipher selector OUI to an internal
+ * cipher algorithm.  Where appropriate we also
+ * record any key length.
+ */
+static int
+wpa_cipher(u_int8_t *sel, u_int8_t *keylen)
+{
+#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case WPA_SEL(WPA_CSE_NULL):
+		return IEEE80211_CIPHER_NONE;
+	case WPA_SEL(WPA_CSE_WEP40):
+		if (keylen)
+			*keylen = 40 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case WPA_SEL(WPA_CSE_WEP104):
+		if (keylen)
+			*keylen = 104 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case WPA_SEL(WPA_CSE_TKIP):
+		return IEEE80211_CIPHER_TKIP;
+	case WPA_SEL(WPA_CSE_CCMP):
+		return IEEE80211_CIPHER_AES_CCM;
+	}
+	return 32;		/* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert a WPA key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+wpa_keymgmt(u_int8_t *sel)
+{
+#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
+		return WPA_ASE_8021X_UNSPEC;
+	case WPA_SEL(WPA_ASE_8021X_PSK):
+		return WPA_ASE_8021X_PSK;
+	case WPA_SEL(WPA_ASE_NONE):
+		return WPA_ASE_NONE;
+	}
+	return 0;		/* NB: so is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Parse a WPA information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm,
+	struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+	u_int8_t len = frm[1];
+	u_int32_t w;
+	int n;
+
+	/*
+	 * Check the length once for fixed parts: OUI, type,
+	 * version, mcast cipher, and 2 selector counts.
+	 * Other, variable-length data, must be checked separately.
+	 */
+	KASSERT(ic->ic_flags & IEEE80211_F_WPA1,
+		("not WPA, flags 0x%x", ic->ic_flags));
+	if (len < 14) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "too short, len %u", len);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 6, len -= 4;		/* NB: len is payload only */
+	/* NB: iswapoui already validated the OUI and type */
+	w = LE_READ_2(frm);
+	if (w != WPA_VERSION) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "bad version %u", w);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2, len -= 2;
+
+	/* multicast/group cipher */
+	w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+	if (w != rsn->rsn_mcastcipher) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "mcast cipher mismatch; got %u, expected %u",
+		    w, rsn->rsn_mcastcipher);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 4, len -= 4;
+
+	/* unicast ciphers */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4+2) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "ucast cipher data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_ucastcipherset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "%s", "ucast cipher set empty");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & (1<<IEEE80211_CIPHER_TKIP))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+	else
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+	/* key management algorithms */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "key mgmt alg data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= wpa_keymgmt(frm);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_keymgmtset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "%s", "no acceptable key mgmt alg");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & WPA_ASE_8021X_UNSPEC)
+		rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
+	else
+		rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+	if (len > 2)		/* optional capabilities */
+		rsn->rsn_caps = LE_READ_2(frm);
+
+	return 0;
+}
+
+/*
+ * Convert an RSN cipher selector OUI to an internal
+ * cipher algorithm.  Where appropriate we also
+ * record any key length.
+ */
+static int
+rsn_cipher(u_int8_t *sel, u_int8_t *keylen)
+{
+#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case RSN_SEL(RSN_CSE_NULL):
+		return IEEE80211_CIPHER_NONE;
+	case RSN_SEL(RSN_CSE_WEP40):
+		if (keylen)
+			*keylen = 40 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case RSN_SEL(RSN_CSE_WEP104):
+		if (keylen)
+			*keylen = 104 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case RSN_SEL(RSN_CSE_TKIP):
+		return IEEE80211_CIPHER_TKIP;
+	case RSN_SEL(RSN_CSE_CCMP):
+		return IEEE80211_CIPHER_AES_CCM;
+	case RSN_SEL(RSN_CSE_WRAP):
+		return IEEE80211_CIPHER_AES_OCB;
+	}
+	return 32;		/* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert an RSN key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+rsn_keymgmt(u_int8_t *sel)
+{
+#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
+		return RSN_ASE_8021X_UNSPEC;
+	case RSN_SEL(RSN_ASE_8021X_PSK):
+		return RSN_ASE_8021X_PSK;
+	case RSN_SEL(RSN_ASE_NONE):
+		return RSN_ASE_NONE;
+	}
+	return 0;		/* NB: so is discarded */
+#undef RSN_SEL
+}
+
+/*
+ * Parse a WPA/RSN information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm,
+	struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+	u_int8_t len = frm[1];
+	u_int32_t w;
+	int n;
+
+	/*
+	 * Check the length once for fixed parts: 
+	 * version, mcast cipher, and 2 selector counts.
+	 * Other, variable-length data, must be checked separately.
+	 */
+	KASSERT(ic->ic_flags & IEEE80211_F_WPA2,
+		("not RSN, flags 0x%x", ic->ic_flags));
+	if (len < 10) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "too short, len %u", len);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2;
+	w = LE_READ_2(frm);
+	if (w != RSN_VERSION) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "bad version %u", w);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2, len -= 2;
+
+	/* multicast/group cipher */
+	w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+	if (w != rsn->rsn_mcastcipher) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "mcast cipher mismatch; got %u, expected %u",
+		    w, rsn->rsn_mcastcipher);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 4, len -= 4;
+
+	/* unicast ciphers */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4+2) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "ucast cipher data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_ucastcipherset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "%s", "ucast cipher set empty");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & (1<<IEEE80211_CIPHER_TKIP))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+	else
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+	/* key management algorithms */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4) {
+		IEEE80211_DISCARD_IE(ic, 
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "key mgmt alg data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= rsn_keymgmt(frm);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_keymgmtset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "%s", "no acceptable key mgmt alg");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & RSN_ASE_8021X_UNSPEC)
+		rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
+	else
+		rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
+
+	/* optional RSN capabilities */
+	if (len > 2)
+		rsn->rsn_caps = LE_READ_2(frm);
+	/* XXXPMKID */
+
+	return 0;
+}
+
+static int
+ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm,
+	const struct ieee80211_frame *wh)
+{
+#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	u_int len = frm[1], qosinfo;
+	int i;
+
+	if (len < sizeof(struct ieee80211_wme_param)-2) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
+		    wh, "WME", "too short, len %u", len);
+		return -1;
+	}
+	qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
+	qosinfo &= WME_QOSINFO_COUNT;
+	/* XXX do proper check for wraparound */
+	if (qosinfo == wme->wme_wmeChanParams.cap_info)
+		return 0;
+	frm += __offsetof(struct ieee80211_wme_param, params_acParams);
+	for (i = 0; i < WME_NUM_AC; i++) {
+		struct wmeParams *wmep =
+			&wme->wme_wmeChanParams.cap_wmeParams[i];
+		/* NB: ACI not used */
+		wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
+		wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
+		wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
+		wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
+		wmep->wmep_txopLimit = LE_READ_2(frm+2);
+		frm += 4;
 	}
-	return m;
+	wme->wme_wmeChanParams.cap_info = qosinfo;
+	return 1;
+#undef MS
 }
 
-/*
- * Install received rate set information in the node's state block.
- */
-static int
-ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
-	uint8_t *rates, uint8_t *xrates, int flags)
+static void
+ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie)
 {
-	struct ieee80211_rateset *rs = &ni->ni_rates;
+	u_int ielen = ie[1]+2;
+	/*
+	 * Record information element for later use.
+	 */
+	if (*iep == NULL || (*iep)[1] != ie[1]) {
+		if (*iep != NULL)
+			FREE(*iep, M_DEVBUF);
+		MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT);
+	}
+	if (*iep != NULL)
+		memcpy(*iep, ie, ielen);
+	/* XXX note failure */
+}
 
-	memset(rs, 0, sizeof(*rs));
-	rs->rs_nrates = rates[1];
-	memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
-	if (xrates != NULL) {
-		uint8_t nxrates;
-		/*
-		 * Tack on 11g extended supported rate element.
-		 */
-		nxrates = xrates[1];
-		if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
-			nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
-			IEEE80211_DPRINTF(("%s: extended rate set too large;"
-				" only using %u of %u rates\n",
-				__func__, nxrates, xrates[1]));
-			ic->ic_stats.is_rx_rstoobig++;
+#ifdef IEEE80211_DEBUG
+static void
+dump_probe_beacon(u_int8_t subtype, int isnew,
+	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	u_int8_t chan, u_int8_t bchan, u_int16_t capinfo, u_int16_t bintval,
+	u_int8_t erp, u_int8_t *ssid, u_int8_t *country)
+{
+	printf("[%6D] %s%s on chan %u (bss chan %u) ",
+	    (mac), ":", isnew ? "new " : "",
+	    ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
+	    chan, bchan);
+	ieee80211_print_essid(ssid + 2, ssid[1]);
+	printf("\n");
+
+	if (isnew) {
+		printf("[%6D] caps 0x%x bintval %u erp 0x%x", 
+			(mac), ":", capinfo, bintval, erp);
+		if (country) {
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+			printf(" country info %*D", country[1], country+2, " ");
+#else
+			int i;
+			printf(" country info");
+			for (i = 0; i < country[1]; i++)
+				printf(" %02x", country[i+2]);
+#endif
 		}
-		memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
-		rs->rs_nrates += nxrates;
+		printf("\n");
 	}
-	return ieee80211_fix_rate(ic, ni, flags);
 }
-
-/* Verify the existence and length of __elem or get out. */
-#define	IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {			\
-	if ((__elem) == NULL) {						\
-		IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n",	\
-			__func__, ieee80211_mgt_subtype_name[subtype >>	\
-				IEEE80211_FC0_SUBTYPE_SHIFT]));		\
-		ic->ic_stats.is_rx_elem_missing++;			\
-		return;							\
-	}								\
-	if ((__elem)[1] > (__maxlen)) {					\
-		IEEE80211_DPRINTF(("%s: bad " #__elem " len %d in %s "	\
-			"frame from %6D\n", __func__, (__elem)[1],	\
-			ieee80211_mgt_subtype_name[subtype >>		\
-				IEEE80211_FC0_SUBTYPE_SHIFT],		\
-			wh->i_addr2, ":"));				\
-		ic->ic_stats.is_rx_elem_toobig++;			\
-		return;							\
-	}								\
-} while (0)
-
-#define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
-	if ((_len) < (_minlen)) {					\
-		IEEE80211_DPRINTF(("%s: %s frame too short from %6D\n",	\
-			__func__,					\
-			ieee80211_mgt_subtype_name[subtype >>		\
-				IEEE80211_FC0_SUBTYPE_SHIFT],		\
-			wh->i_addr2, ":"));				\
-		ic->ic_stats.is_rx_elem_toosmall++;			\
-		return;							\
-	}								\
-} while (0)
+#endif /* IEEE80211_DEBUG */
 
 void
 ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
 	struct ieee80211_node *ni,
-	int subtype, int rssi, uint32_t rstamp)
+	int subtype, int rssi, u_int32_t rstamp)
 {
-	struct ifnet *ifp = &ic->ic_if;
+#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
 	struct ieee80211_frame *wh;
-	uint8_t *frm, *efrm;
-	uint8_t *ssid, *rates, *xrates;
-	int reassoc, resp, newassoc, allocbs;
+	u_int8_t *frm, *efrm;
+	u_int8_t *ssid, *rates, *xrates, *wpa, *wme;
+	int reassoc, resp, allocbs;
 
 	wh = mtod(m0, struct ieee80211_frame *);
-	frm = (uint8_t *)&wh[1];
-	efrm = mtod(m0, uint8_t *) + m0->m_len;
+	frm = (u_int8_t *)&wh[1];
+	efrm = mtod(m0, u_int8_t *) + m0->m_len;
 	switch (subtype) {
 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
 	case IEEE80211_FC0_SUBTYPE_BEACON: {
-		uint8_t *tstamp, *bintval, *capinfo, *country;
-		uint8_t chan, bchan, fhindex, erp;
-		uint16_t fhdwell;
-		int isprobe;
+		u_int8_t *tstamp, *country;
+		u_int8_t chan, bchan, fhindex, erp;
+		u_int16_t capinfo, bintval, timoff;
+		u_int16_t fhdwell;
 
-		if (ic->ic_opmode != IEEE80211_M_IBSS &&
-		    ic->ic_state != IEEE80211_S_SCAN) {
-			/* XXX: may be useful for background scan */
+		/*
+		 * We process beacon/probe response frames:
+		 *    o when scanning, or
+		 *    o station mode when associated (to collect state
+		 *      updates such as 802.11g slot time), or
+		 *    o adhoc mode (to discover neighbors)
+		 * Frames otherwise received are discarded.
+		 */ 
+		if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
+		      (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) ||
+		       ic->ic_opmode == IEEE80211_M_IBSS)) {
+			ic->ic_stats.is_rx_mgtdiscard++;
 			return;
 		}
-		isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP);
-
 		/*
 		 * beacon/probe response frame format
 		 *	[8] time stamp
@@ -581,17 +1718,20 @@
 		 *	[tlv] parameter set (FH/DS)
 		 *	[tlv] erp information
 		 *	[tlv] extended supported rates
+		 *	[tlv] WME
+		 *	[tlv] WPA or RSN
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
-		tstamp  = frm;	frm += 8;
-		bintval = frm;	frm += 2;
-		capinfo = frm;	frm += 2;
-		ssid = rates = xrates = country = NULL;
+		tstamp  = frm;				frm += 8;
+		bintval = le16toh(*(u_int16_t *)frm);	frm += 2;
+		capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
+		ssid = rates = xrates = country = wpa = wme = NULL;
 		bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
 		chan = bchan;
 		fhdwell = 0;
 		fhindex = 0;
 		erp = 0;
+		timoff = 0;
 		while (frm < efrm) {
 			switch (*frm) {
 			case IEEE80211_ELEMID_SSID:
@@ -605,7 +1745,7 @@
 				break;
 			case IEEE80211_ELEMID_FHPARMS:
 				if (ic->ic_phytype == IEEE80211_T_FH) {
-					fhdwell = (frm[3] << 8) | frm[2];
+					fhdwell = LE_READ_2(&frm[2]);
 					chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
 					fhindex = frm[6];
 				}
@@ -619,6 +1759,8 @@
 					chan = frm[2];
 				break;
 			case IEEE80211_ELEMID_TIM:
+				/* XXX ATIM? */
+				timoff = frm - mtod(m0, u_int8_t *);
 				break;
 			case IEEE80211_ELEMID_IBSSPARMS:
 				break;
@@ -627,17 +1769,28 @@
 				break;
 			case IEEE80211_ELEMID_ERP:
 				if (frm[1] != 1) {
-					IEEE80211_DPRINTF(("%s: invalid ERP "
-						"element; length %u, expecting "
-						"1\n", __func__, frm[1]));
+					IEEE80211_DISCARD_IE(ic,
+					    IEEE80211_MSG_ELEMID, wh, "ERP",
+					    "bad len %u", frm[1]);
 					ic->ic_stats.is_rx_elem_toobig++;
 					break;
 				}
 				erp = frm[2];
 				break;
+			case IEEE80211_ELEMID_RSN:
+				wpa = frm;
+				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswpaoui(frm))
+					wpa = frm;
+				else if (iswmeparam(frm) || iswmeinfo(frm))
+					wme = frm;
+				/* XXX Atheros OUI support */
+				break;
 			default:
-				IEEE80211_DPRINTF2(("%s: element id %u/len %u "
-					"ignored\n", __func__, *frm, frm[1]));
+				IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
+				    wh, "unhandled",
+				    "id %u, len %u", *frm, frm[1]);
 				ic->ic_stats.is_rx_elem_unknown++;
 				break;
 			}
@@ -650,10 +1803,10 @@
 		    chan > IEEE80211_CHAN_MAX ||
 #endif
 		    isclr(ic->ic_chan_active, chan)) {
-			IEEE80211_DPRINTF(("%s: ignore %s with invalid channel "
-				"%u\n", __func__,
-				isprobe ? "probe response" : "beacon",
-				chan));
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "invalid channel %u", chan);
 			ic->ic_stats.is_rx_badchan++;
 			return;
 		}
@@ -668,108 +1821,156 @@
 			 *     the rssi value should be correct even for
 			 *     different hop pattern in FH.
 			 */
-			IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked "
-				"for channel %u\n", __func__,
-				isprobe ? "probe response" : "beacon",
-				bchan, chan));
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "for off-channel %u", chan);
 			ic->ic_stats.is_rx_chanmismatch++;
 			return;
 		}
 
 		/*
-		 * Use mac and channel for lookup so we collect all
-		 * potential AP's when scanning.  Otherwise we may
-		 * see the same AP on multiple channels and will only
-		 * record the last one.  We could filter APs here based
-		 * on rssi, etc. but leave that to the end of the scan
-		 * so we can keep the selection criteria in one spot.
-		 * This may result in a bloat of the scanned AP list but
-		 * it shouldn't be too much.
+		 * Count frame now that we know it's to be processed.
 		 */
-		ni = ieee80211_lookup_node(ic, wh->i_addr2,
-				&ic->ic_channels[chan]);
-#ifdef IEEE80211_DEBUG
-		if (ieee80211_debug &&
-		    (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) {
-			printf("%s: %s%s on chan %u (bss chan %u) ",
-			    __func__, (ni == NULL ? "new " : ""),
-			    isprobe ? "probe response" : "beacon",
-			    chan, bchan);
-			ieee80211_print_essid(ssid + 2, ssid[1]);
-			printf(" from %6D\n", wh->i_addr2, ":");
-			printf("%s: caps 0x%x bintval %u erp 0x%x\n",
-				__func__, le16toh(*(uint16_t *)capinfo),
-				le16toh(*(uint16_t *)bintval), erp);
-			if (country)
-				printf("%s: country info %*D\n",
-					__func__, country[1], country+2, " ");
+		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+			ic->ic_stats.is_rx_beacon++;		/* XXX remove */
+			IEEE80211_NODE_STAT(ni, rx_beacons);
+		} else
+			IEEE80211_NODE_STAT(ni, rx_proberesp);
+
+		/*
+		 * When operating in station mode, check for state updates.
+		 * Be careful to ignore beacons received while doing a
+		 * background scan.  We consider only 11g/WMM stuff right now.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_STA &&
+		    ni->ni_associd != 0 &&
+		    ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
+		     IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
+			if (ni->ni_erp != erp) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "[%6D] erp change: was 0x%x, now 0x%x\n",
+				    (wh->i_addr2), ":",
+				    ni->ni_erp, erp);
+				if (erp & IEEE80211_ERP_USE_PROTECTION)
+					ic->ic_flags |= IEEE80211_F_USEPROT;
+				else
+					ic->ic_flags &= ~IEEE80211_F_USEPROT;
+				ni->ni_erp = erp;
+				/* XXX statistic */
+			}
+			if ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "[%6D] capabilities change: before 0x%x,"
+				     " now 0x%x\n",
+				     (wh->i_addr2), ":",
+				     ni->ni_capinfo, capinfo);
+				/*
+				 * NB: we assume short preamble doesn't
+				 *     change dynamically
+				 */
+				ieee80211_set_shortslottime(ic,
+					ic->ic_curmode == IEEE80211_MODE_11A ||
+					(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+				ni->ni_capinfo = capinfo;
+				/* XXX statistic */
+			}
+			if (wme != NULL &&
+			    ieee80211_parse_wmeparams(ic, wme, wh) > 0)
+				ieee80211_wme_updateparams(ic);
+			/* NB: don't need the rest of this */
+			if ((ic->ic_flags & IEEE80211_F_SCAN) == 0)
+				return;
 		}
+
+		if (ni == ic->ic_bss) {
+#ifdef IEEE80211_DEBUG
+			if (ieee80211_msg_scan(ic))
+				dump_probe_beacon(subtype, 1,
+				    wh->i_addr2, chan, bchan, capinfo,
+				    bintval, erp, ssid, country);
 #endif
-		if (ni == NULL) {
-			ni = ieee80211_alloc_node(ic, wh->i_addr2);
+			/*
+			 * Create a new entry.  If scanning the entry goes
+			 * in the scan cache.  Otherwise, be particular when
+			 * operating in adhoc mode--only take nodes marked
+			 * as ibss participants so we don't populate our
+			 * neighbor table with unintersting sta's.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+				if ((capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+					return;
+				ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
+						wh->i_addr2);
+			} else
+				ni = ieee80211_dup_bss(&ic->ic_scan, wh->i_addr2);
 			if (ni == NULL)
 				return;
 			ni->ni_esslen = ssid[1];
 			memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
 			memcpy(ni->ni_essid, ssid + 2, ssid[1]);
-			allocbs = 1;
-		} else if (ssid[1] != 0 && isprobe) {
+		} else if (ssid[1] != 0 &&
+		    (ISPROBE(subtype) || ni->ni_esslen == 0)) {
 			/*
-			 * Update ESSID at probe response to adopt hidden AP by
-			 * Lucent/Cisco, which announces null ESSID in beacon.
+			 * Update ESSID at probe response to adopt
+			 * hidden AP by Lucent/Cisco, which announces
+			 * null ESSID in beacon.
 			 */
+#ifdef IEEE80211_DEBUG
+			if (ieee80211_msg_scan(ic) ||
+			    ieee80211_msg_debug(ic))
+				dump_probe_beacon(subtype, 0,
+				    wh->i_addr2, chan, bchan, capinfo,
+				    bintval, erp, ssid, country);
+#endif
 			ni->ni_esslen = ssid[1];
 			memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
 			memcpy(ni->ni_essid, ssid + 2, ssid[1]);
-			allocbs = 0;
-		} else
-			allocbs = 0;
+		}
+		ni->ni_scangen = ic->ic_scan.nt_scangen;
 		IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
 		ni->ni_rssi = rssi;
 		ni->ni_rstamp = rstamp;
-		memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
-		ni->ni_intval = le16toh(*(uint16_t *)bintval);
-		ni->ni_capinfo = le16toh(*(uint16_t *)capinfo);
-		/* XXX validate channel # */
+		memcpy(ni->ni_tstamp.data, tstamp, sizeof(ni->ni_tstamp));
+		ni->ni_intval = bintval;
+		ni->ni_capinfo = capinfo;
 		ni->ni_chan = &ic->ic_channels[chan];
 		ni->ni_fhdwell = fhdwell;
 		ni->ni_fhindex = fhindex;
 		ni->ni_erp = erp;
+		/*
+		 * Record the byte offset from the mac header to
+		 * the start of the TIM information element for
+		 * use by hardware and/or to speedup software
+		 * processing of beacon frames.
+		 */
+		ni->ni_timoff = timoff;
+		/*
+		 * Record optional information elements that might be
+		 * used by applications or drivers.
+		 */
+		if (wme != NULL)
+			ieee80211_saveie(&ni->ni_wme_ie, wme);
+		if (wpa != NULL)
+			ieee80211_saveie(&ni->ni_wpa_ie, wpa);
 		/* NB: must be after ni_chan is setup */
 		ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
-		/*
-		 * When scanning we record results (nodes) with a zero
-		 * refcnt.  Otherwise we want to hold the reference for
-		 * ibss neighbors so the nodes don't get released prematurely.
-		 * Anything else can be discarded (XXX and should be handled
-		 * above so we don't do so much work). 
-		 */
-		if (ic->ic_state == IEEE80211_S_SCAN)
-			ieee80211_unref_node(&ni);	/* NB: do not free */
-		else if (ic->ic_opmode == IEEE80211_M_IBSS &&
-		    allocbs && isprobe) {
-			/*
-			 * Fake an association so the driver can setup it's
-			 * private state.  The rate set has been setup above;
-			 * there is no handshake as in ap/station operation.
-			 */
-			if (ic->ic_newassoc)
-				(*ic->ic_newassoc)(ic, ni, 1);
-			/* NB: hold reference */
-		} else {
-			/* XXX optimize to avoid work done above */
-			ieee80211_free_node(ic, ni);
-		}
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ: {
-		uint8_t rate;
+		u_int8_t rate;
 
-		if (ic->ic_opmode == IEEE80211_M_STA)
+		if (ic->ic_opmode == IEEE80211_M_STA ||
+		    ic->ic_state != IEEE80211_S_RUN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
 			return;
-		if (ic->ic_state != IEEE80211_S_RUN)
+		}
+		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+			/* frame must be directed */
+			ic->ic_stats.is_rx_mgtdiscard++;	/* XXX stat */
 			return;
+		}
 
 		/*
 		 * prreq frame format
@@ -794,48 +1995,58 @@
 		}
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
-		if (ssid[1] != 0 &&
-		    (ssid[1] != ic->ic_bss->ni_esslen ||
-		    memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) {
-#ifdef IEEE80211_DEBUG
-			if (ieee80211_debug) {
-				printf("%s: ssid unmatch ", __func__);
-				ieee80211_print_essid(ssid + 2, ssid[1]);
-				printf(" from %6D\n", wh->i_addr2, ":");
-			}
-#endif
-			ic->ic_stats.is_rx_ssidmismatch++;
+		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+		if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "%s", "no ssid with ssid suppression enabled");
+			ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/
 			return;
 		}
 
 		if (ni == ic->ic_bss) {
-			ni = ieee80211_dup_bss(ic, wh->i_addr2);
+			if (ic->ic_opmode == IEEE80211_M_IBSS) {
+				/*
+				 * XXX Cannot tell if the sender is operating
+				 * in ibss mode.  But we need a new node to
+				 * send the response so blindly add them to the
+				 * neighbor table.
+				 */
+				ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
+					wh->i_addr2);
+			} else
+				ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
 			if (ni == NULL)
 				return;
-			IEEE80211_DPRINTF(("%s: new req from %6D\n",
-				__func__, wh->i_addr2, ":"));
 			allocbs = 1;
 		} else
 			allocbs = 0;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] recv probe req\n", (wh->i_addr2), ":");
 		ni->ni_rssi = rssi;
 		ni->ni_rstamp = rstamp;
 		rate = ieee80211_setup_rates(ic, ni, rates, xrates,
-				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
-				| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+			  IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
+			| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
 		if (rate & IEEE80211_RATE_BASIC) {
-			IEEE80211_DPRINTF(("%s: rate negotiation failed: %6D\n",
-				__func__, wh->i_addr2, ":"));
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "%s", "recv'd rate set invalid");
 		} else {
 			IEEE80211_SEND_MGMT(ic, ni,
 				IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
 		}
-		if (allocbs)
-			ieee80211_free_node(ic, ni);
+		if (allocbs && ic->ic_opmode != IEEE80211_M_IBSS) {
+			/* reclaim immediately */
+			ieee80211_free_node(ni);
+		}
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_AUTH: {
-		uint16_t algo, seq, status;
+		u_int16_t algo, seq, status;
 		/*
 		 * auth frame format
 		 *	[2] algorithm
@@ -844,97 +2055,66 @@
 		 *	[tlv*] challenge
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
-		algo   = le16toh(*(uint16_t *)frm);
-		seq    = le16toh(*(uint16_t *)(frm + 2));
-		status = le16toh(*(uint16_t *)(frm + 4));
-		if (algo != IEEE80211_AUTH_ALG_OPEN) {
-			/* TODO: shared key auth */
-			IEEE80211_DPRINTF(("%s: unsupported auth %d from %6D\n",
-				__func__, algo, wh->i_addr2, ":"));
-			ic->ic_stats.is_rx_auth_unsupported++;
+		algo   = le16toh(*(u_int16_t *)frm);
+		seq    = le16toh(*(u_int16_t *)(frm + 2));
+		status = le16toh(*(u_int16_t *)(frm + 4));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+		    "[%6D] recv auth frame with algorithm %d seq %d\n",
+		    (wh->i_addr2), ":", algo, seq);
+		/*
+		 * Consult the ACL policy module if setup.
+		 */
+		if (ic->ic_acl != NULL &&
+		    !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL,
+			    wh, "auth", "%s", "disallowed by ACL");
+			ic->ic_stats.is_rx_acl++;
 			return;
 		}
-		switch (ic->ic_opmode) {
-		case IEEE80211_M_IBSS:
-			if (ic->ic_state != IEEE80211_S_RUN || seq != 1) {
-				IEEE80211_DPRINTF(("%s: discard auth from %6D; "
-					"state %u, seq %u\n", __func__,
-					wh->i_addr2, ":",
-					ic->ic_state, seq));
-				ic->ic_stats.is_rx_bad_auth++;
-				break;
-			}
-			ieee80211_new_state(ic, IEEE80211_S_AUTH,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
-			break;
-
-		case IEEE80211_M_AHDEMO:
-			/* should not come here */
-			break;
-
-		case IEEE80211_M_HOSTAP:
-			if (ic->ic_state != IEEE80211_S_RUN || seq != 1) {
-				IEEE80211_DPRINTF(("%s: discard auth from %6D; "
-					"state %u, seq %u\n", __func__,
-					wh->i_addr2, ":",
-					ic->ic_state, seq));
-				ic->ic_stats.is_rx_bad_auth++;
-				break;
-			}
-			if (ni == ic->ic_bss) {
-				ni = ieee80211_alloc_node(ic, wh->i_addr2);
-				if (ni == NULL)
-					return;
-				IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
-				ni->ni_rssi = rssi;
-				ni->ni_rstamp = rstamp;
-				ni->ni_chan = ic->ic_bss->ni_chan;
-				allocbs = 1;
-			} else
-				allocbs = 0;
-			IEEE80211_SEND_MGMT(ic, ni,
-				IEEE80211_FC0_SUBTYPE_AUTH, 2);
-			if (ifp->if_flags & IFF_DEBUG)
-				if_printf(ifp, "station %6D %s authenticated\n",
-				    ni->ni_macaddr, ":", 
-				    (allocbs ? "newly" : "already"));
-			break;
-
-		case IEEE80211_M_STA:
-			if (ic->ic_state != IEEE80211_S_AUTH || seq != 2) {
-				IEEE80211_DPRINTF(("%s: discard auth from %6D; "
-					"state %u, seq %u\n", __func__,
-					wh->i_addr2, ":",
-					ic->ic_state, seq));
-				ic->ic_stats.is_rx_bad_auth++;
-				break;
-			}
-			if (status != 0) {
-				if_printf(&ic->ic_if,
-				    "authentication failed (reason %d) for %6D\n",
-				    status,
-				    wh->i_addr3, ":");
-				if (ni != ic->ic_bss)
-					ni->ni_fails++;
-				ic->ic_stats.is_rx_auth_fail++;
-				return;
+		if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+			IEEE80211_DISCARD(ic,
+			    IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+			    wh, "auth", "%s", "TKIP countermeasures enabled");
+			ic->ic_stats.is_rx_auth_countermeasures++;
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				IEEE80211_SEND_MGMT(ic, ni,
+					IEEE80211_FC0_SUBTYPE_AUTH,
+					IEEE80211_REASON_MIC_FAILURE);
 			}
-			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
-			break;
-		case IEEE80211_M_MONITOR:
-			break;
+			return;
 		}
+		if (algo == IEEE80211_AUTH_ALG_SHARED)
+			ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
+			    rstamp, seq, status);
+		else if (algo == IEEE80211_AUTH_ALG_OPEN)
+			ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq,
+			    status);
+		else {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+			    wh, "auth", "unsupported alg %d", algo);
+			ic->ic_stats.is_rx_auth_unsupported++;
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				/* XXX not right */
+				IEEE80211_SEND_MGMT(ic, ni,
+					IEEE80211_FC0_SUBTYPE_AUTH,
+					(seq+1) | (IEEE80211_STATUS_ALG<<16));
+			}
+			return;
+		} 
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
-		uint16_t capinfo, bintval;
+		u_int16_t capinfo, bintval;
+		struct ieee80211_rsnparms rsn;
+		u_int8_t reason;
 
 		if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
-		    (ic->ic_state != IEEE80211_S_RUN))
+		    ic->ic_state != IEEE80211_S_RUN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
 			return;
+		}
 
 		if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
 			reassoc = 1;
@@ -951,19 +2131,22 @@
 		 *	[tlv] ssid
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WPA or RSN
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
 		if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
-			IEEE80211_DPRINTF(("%s: ignore other bss from %6D\n",
-				__func__, wh->i_addr2, ":"));
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "%s", "wrong bssid");
 			ic->ic_stats.is_rx_assoc_bss++;
 			return;
 		}
-		capinfo = le16toh(*(uint16_t *)frm);	frm += 2;
-		bintval = le16toh(*(uint16_t *)frm);	frm += 2;
+		capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
+		bintval = le16toh(*(u_int16_t *)frm);	frm += 2;
 		if (reassoc)
 			frm += 6;	/* ignore current AP info */
-		ssid = rates = xrates = NULL;
+		ssid = rates = xrates = wpa = wme = NULL;
 		while (frm < efrm) {
 			switch (*frm) {
 			case IEEE80211_ELEMID_SSID:
@@ -975,48 +2158,102 @@
 			case IEEE80211_ELEMID_XRATES:
 				xrates = frm;
 				break;
+			/* XXX verify only one of RSN and WPA ie's? */
+			case IEEE80211_ELEMID_RSN:
+				wpa = frm;
+				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswpaoui(frm)) {
+					if (ic->ic_flags & IEEE80211_F_WPA1)
+						wpa = frm;
+				} else if (iswmeinfo(frm))
+					wme = frm;
+				/* XXX Atheros OUI support */
+				break;
 			}
 			frm += frm[1] + 2;
 		}
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
-		if (ssid[1] != ic->ic_bss->ni_esslen ||
-		    memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) {
-#ifdef IEEE80211_DEBUG
-			if (ieee80211_debug) {
-				printf("%s: ssid unmatch ", __func__);
-				ieee80211_print_essid(ssid + 2, ssid[1]);
-				printf(" from %6D\n", wh->i_addr2, ":");
-			}
-#endif
-			ic->ic_stats.is_rx_ssidmismatch++;
-			return;
-		}
+		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+
 		if (ni == ic->ic_bss) {
-			IEEE80211_DPRINTF(("%s: not authenticated for %6D\n",
-				__func__, wh->i_addr2, ":"));
-			ni = ieee80211_dup_bss(ic, wh->i_addr2);
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			    "[%6D] deny %s request, sta not authenticated\n",
+			    (wh->i_addr2), ":",
+			    reassoc ? "reassoc" : "assoc");
+			ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
 			if (ni != NULL) {
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_DEAUTH,
 				    IEEE80211_REASON_ASSOC_NOT_AUTHED);
-				ieee80211_free_node(ic, ni);
+				ieee80211_free_node(ni);
 			}
 			ic->ic_stats.is_rx_assoc_notauth++;
 			return;
 		}
-		/* XXX per-node cipher suite */
+		/* assert right associstion security credentials */
+		if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+			    "[%6D] no WPA/RSN IE in association request\n",
+			    (wh->i_addr2), ":");
+			IEEE80211_SEND_MGMT(ic, ni,
+			    IEEE80211_FC0_SUBTYPE_DEAUTH,
+			    IEEE80211_REASON_RSN_REQUIRED);
+			ieee80211_node_leave(ic, ni);
+			/* XXX distinguish WPA/RSN? */
+			ic->ic_stats.is_rx_assoc_badwpaie++;
+			return;	
+		}
+		if (wpa != NULL) {
+			/*
+			 * Parse WPA information element.  Note that
+			 * we initialize the param block from the node
+			 * state so that information in the IE overrides
+			 * our defaults.  The resulting parameters are
+			 * installed below after the association is assured.
+			 */
+			rsn = ni->ni_rsn;
+			if (wpa[0] != IEEE80211_ELEMID_RSN)
+				reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh);
+			else
+				reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh);
+			if (reason != 0) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+				ieee80211_node_leave(ic, ni);
+				/* XXX distinguish WPA/RSN? */
+				ic->ic_stats.is_rx_assoc_badwpaie++;
+				return;
+			}
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+			    "[%6D] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
+			    (wh->i_addr2), ":",
+			    wpa[0] != IEEE80211_ELEMID_RSN ?  "WPA" : "RSN",
+			    rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen,
+			    rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen,
+			    rsn.rsn_keymgmt, rsn.rsn_caps);
+		}
+		/* discard challenge after association */
+		if (ni->ni_challenge != NULL) {
+			FREE(ni->ni_challenge, M_DEVBUF);
+			ni->ni_challenge = NULL;
+		}
 		/* XXX some stations use the privacy bit for handling APs
 		       that suport both encrypted and unencrypted traffic */
+		/* NB: PRIVACY flag bits are assumed to match */
 		if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||
-		    (capinfo & IEEE80211_CAPINFO_PRIVACY) !=
-		    ((ic->ic_flags & IEEE80211_F_WEPON) ?
-		     IEEE80211_CAPINFO_PRIVACY : 0)) {
-			IEEE80211_DPRINTF(("%s: capability mismatch %x for %6D\n",
-				__func__, capinfo, wh->i_addr2, ":"));
-			ni->ni_associd = 0;
+		    (capinfo & IEEE80211_CAPINFO_PRIVACY) ^
+		    (ic->ic_flags & IEEE80211_F_PRIVACY)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			    "[%6D] deny %s request, capability mismatch 0x%x\n",
+			    (wh->i_addr2), ":",
+			    reassoc ? "reassoc" : "assoc", capinfo);
 			IEEE80211_SEND_MGMT(ic, ni, resp,
 				IEEE80211_STATUS_CAPINFO);
+			ieee80211_node_leave(ic, ni);
 			ic->ic_stats.is_rx_assoc_capmismatch++;
 			return;
 		}
@@ -1024,11 +2261,13 @@
 				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
 				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
 		if (ni->ni_rates.rs_nrates == 0) {
-			IEEE80211_DPRINTF(("%s: rate unmatch for %6D\n",
-				__func__, wh->i_addr2, ":"));
-			ni->ni_associd = 0;
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			    "[%6D] deny %s request, rate set mismatch\n",
+			    (wh->i_addr2), ":",
+			    reassoc ? "reassoc" : "assoc");
 			IEEE80211_SEND_MGMT(ic, ni, resp,
 				IEEE80211_STATUS_BASIC_RATE);
+			ieee80211_node_leave(ic, ni);
 			ic->ic_stats.is_rx_assoc_norate++;
 			return;
 		}
@@ -1039,33 +2278,51 @@
 		ni->ni_chan = ic->ic_bss->ni_chan;
 		ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
 		ni->ni_fhindex = ic->ic_bss->ni_fhindex;
-		if (ni->ni_associd == 0) {
-			/* XXX handle rollover at 2007 */
-			/* XXX guarantee uniqueness */
-			ni->ni_associd = 0xc000 | ic->ic_bss->ni_associd++;
-			newassoc = 1;
-		} else
-			newassoc = 0;
-		/* XXX for 11g must turn off short slot time if long
-	           slot time sta associates */
-		IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "station %6D %s associated\n",
-			    ni->ni_macaddr, ":",
-			    (newassoc ? "newly" : "already"));
-		/* give driver a chance to setup state like ni_txrate */
-		if (ic->ic_newassoc)
-			(*ic->ic_newassoc)(ic, ni, newassoc);
+		if (wpa != NULL) {
+			/*
+			 * Record WPA/RSN parameters for station, mark
+			 * node as using WPA and record information element
+			 * for applications that require it.
+			 */
+			ni->ni_rsn = rsn;
+			ieee80211_saveie(&ni->ni_wpa_ie, wpa);
+		} else if (ni->ni_wpa_ie != NULL) {
+			/*
+			 * Flush any state from a previous association.
+			 */
+			FREE(ni->ni_wpa_ie, M_DEVBUF);
+			ni->ni_wpa_ie = NULL;
+		}
+		if (wme != NULL) {
+			/*
+			 * Record WME parameters for station, mark node
+			 * as capable of QoS and record information
+			 * element for applications that require it.
+			 */
+			ieee80211_saveie(&ni->ni_wme_ie, wme);
+			ni->ni_flags |= IEEE80211_NODE_QOS;
+		} else if (ni->ni_wme_ie != NULL) {
+			/*
+			 * Flush any state from a previous association.
+			 */
+			FREE(ni->ni_wme_ie, M_DEVBUF);
+			ni->ni_wme_ie = NULL;
+			ni->ni_flags &= ~IEEE80211_NODE_QOS;
+		}
+		ieee80211_node_join(ic, ni, resp);
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
-		uint16_t status;
+		u_int16_t capinfo, associd;
+		u_int16_t status;
 
 		if (ic->ic_opmode != IEEE80211_M_STA ||
-		    ic->ic_state != IEEE80211_S_ASSOC)
+		    ic->ic_state != IEEE80211_S_ASSOC) {
+			ic->ic_stats.is_rx_mgtdiscard++;
 			return;
+		}
 
 		/*
 		 * asresp frame format
@@ -1074,26 +2331,28 @@
 		 *	[2] association ID
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WME
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
 		ni = ic->ic_bss;
-		ni->ni_capinfo = le16toh(*(uint16_t *)frm);
+		capinfo = le16toh(*(u_int16_t *)frm);
 		frm += 2;
-
-		status = le16toh(*(uint16_t *)frm);
+		status = le16toh(*(u_int16_t *)frm);
 		frm += 2;
 		if (status != 0) {
-			if_printf(ifp, "association failed (reason %d) for %6D\n",
-			    status, wh->i_addr3, ":");
-			if (ni != ic->ic_bss)
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "[%6D] %sassoc failed (reason %d)\n",
+			    (wh->i_addr2), ":",
+			    ISREASSOC(subtype) ?  "re" : "", status);
+			if (ni != ic->ic_bss)	/* XXX never true? */
 				ni->ni_fails++;
-			ic->ic_stats.is_rx_auth_fail++;
+			ic->ic_stats.is_rx_auth_fail++;	/* XXX */
 			return;
 		}
-		ni->ni_associd = le16toh(*(uint16_t *)frm);
+		associd = le16toh(*(u_int16_t *)frm);
 		frm += 2;
 
-		rates = xrates = NULL;
+		rates = xrates = wpa = wme = NULL;
 		while (frm < efrm) {
 			switch (*frm) {
 			case IEEE80211_ELEMID_RATES:
@@ -1102,6 +2361,11 @@
 			case IEEE80211_ELEMID_XRATES:
 				xrates = frm;
 				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswmeoui(frm))
+					wme = frm;
+				/* XXX Atheros OUI support */
+				break;
 			}
 			frm += frm[1] + 2;
 		}
@@ -1110,21 +2374,79 @@
 		ieee80211_setup_rates(ic, ni, rates, xrates,
 				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
 				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
-		if (ni->ni_rates.rs_nrates != 0)
-			ieee80211_new_state(ic, IEEE80211_S_RUN,
-				wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+		if (ni->ni_rates.rs_nrates == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "[%6D] %sassoc failed (rate set mismatch)\n",
+			    (wh->i_addr2), ":",
+			    ISREASSOC(subtype) ?  "re" : "");
+			if (ni != ic->ic_bss)	/* XXX never true? */
+				ni->ni_fails++;
+			ic->ic_stats.is_rx_assoc_norate++;
+			return;
+		}
+
+		ni->ni_capinfo = capinfo;
+		ni->ni_associd = associd;
+		if (wme != NULL &&
+		    ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
+			ni->ni_flags |= IEEE80211_NODE_QOS;
+			ieee80211_wme_updateparams(ic);
+		} else
+			ni->ni_flags &= ~IEEE80211_NODE_QOS;
+		/*
+		 * Configure state now that we are associated.
+		 *
+		 * XXX may need different/additional driver callbacks?
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_11A ||
+		    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+		} else {
+			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+			ic->ic_flags |= IEEE80211_F_USEBARKER;
+		}
+		ieee80211_set_shortslottime(ic,
+			ic->ic_curmode == IEEE80211_MODE_11A ||
+			(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+		/*
+		 * Honor ERP protection.
+		 *
+		 * NB: ni_erp should zero for non-11g operation.
+		 * XXX check ic_curmode anyway?
+		 */
+		if (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)
+			ic->ic_flags |= IEEE80211_F_USEPROT;
+		else
+			ic->ic_flags &= ~IEEE80211_F_USEPROT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] %sassoc success: %s preamble, %s slot time%s%s\n",
+		    (wh->i_addr2), ":",
+		    ISREASSOC(subtype) ? "re" : "",
+		    ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+		    ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+		    ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
+		    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : ""
+		);
+		ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_DEAUTH: {
-		uint16_t reason;
+		u_int16_t reason;
+
+		if (ic->ic_state == IEEE80211_S_SCAN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
 		/*
 		 * deauth frame format
 		 *	[2] reason
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
-		reason = le16toh(*(uint16_t *)frm);
+		reason = le16toh(*(u_int16_t *)frm);
 		ic->ic_stats.is_rx_deauth++;
+		IEEE80211_NODE_STAT(ni, rx_deauth);
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
 			ieee80211_new_state(ic, IEEE80211_S_AUTH,
@@ -1132,29 +2454,36 @@
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (ni != ic->ic_bss) {
-				if (ifp->if_flags & IFF_DEBUG)
-					if_printf(ifp, "station %6D deauthenticated"
-					    " by peer (reason %d)\n",
-					    ni->ni_macaddr, ":", reason);
-				/* node will be free'd on return */
-				ieee80211_unref_node(&ni);
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				    "station %6D deauthenticated by peer "
+				    "(reason %d)\n",
+				    (ni->ni_macaddr), ":", reason);
+				ieee80211_node_leave(ic, ni);
 			}
 			break;
 		default:
+			ic->ic_stats.is_rx_mgtdiscard++;
 			break;
 		}
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
-		uint16_t reason;
+		u_int16_t reason;
+
+		if (ic->ic_state != IEEE80211_S_RUN &&
+		    ic->ic_state != IEEE80211_S_AUTH) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
 		/*
 		 * disassoc frame format
 		 *	[2] reason
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
-		reason = le16toh(*(uint16_t *)frm);
+		reason = le16toh(*(u_int16_t *)frm);
 		ic->ic_stats.is_rx_disassoc++;
+		IEEE80211_NODE_STAT(ni, rx_disassoc);
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
 			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
@@ -1162,25 +2491,227 @@
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (ni != ic->ic_bss) {
-				if (ifp->if_flags & IFF_DEBUG)
-					if_printf(ifp, "station %6D disassociated"
-					    " by peer (reason %d)\n",
-					    ni->ni_macaddr, ":", reason);
-				ni->ni_associd = 0;
-				/* XXX node reclaimed how? */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "[%6D] sta disassociated by peer (reason %d)\n",
+				    (ni->ni_macaddr), ":", reason);
+				ieee80211_node_leave(ic, ni);
 			}
 			break;
 		default:
+			ic->ic_stats.is_rx_mgtdiscard++;
 			break;
 		}
 		break;
 	}
 	default:
-		IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not "
-			"handled\n", __func__, subtype));
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+		     wh, "mgt", "subtype 0x%x not handled", subtype);
 		ic->ic_stats.is_rx_badsubtype++;
 		break;
 	}
+#undef ISREASSOC
+#undef ISPROBE
 }
 #undef IEEE80211_VERIFY_LENGTH
 #undef IEEE80211_VERIFY_ELEMENT
+
+/*
+ * Handle station power-save state change.
+ */
+static void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+
+	if (enable) {
+		if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+			ic->ic_ps_sta++;
+		ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] power save mode on, %u sta's in ps mode\n",
+		    (ni->ni_macaddr), ":", ic->ic_ps_sta);
+		return;
+	}
+
+	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
+		ic->ic_ps_sta--;
+	ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+	    "[%6D] power save mode off, %u sta's in ps mode\n",
+	    (ni->ni_macaddr), ":", ic->ic_ps_sta);
+	/* XXX if no stations in ps mode, flush mc frames */
+
+	/*
+	 * Flush queued unicast frames.
+	 */
+	if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
+		ic->ic_set_tim(ic, ni, 0);	/* just in case */
+		return;
+	}
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+	    "[%6D] flush ps queue, %u packets queued\n",
+	    (ni->ni_macaddr), ":", IEEE80211_NODE_SAVEQ_QLEN(ni));
+	for (;;) {
+		int qlen;
+
+		IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+		if (m == NULL)
+			break;
+		/* 
+		 * If this is the last packet, turn off the TIM bit.
+		 * If there are more packets, set the more packets bit
+		 * in the packet dispatched to the station.
+		 */
+		if (qlen != 0) {
+			struct ieee80211_frame_min *wh =
+				mtod(m, struct ieee80211_frame_min *);
+			wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+		}
+		/* XXX need different driver interface */
+		/* XXX bypasses q max */
+		IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+	}
+}
+
+/*
+ * Process a received ps-poll frame.
+ */
+static void
+ieee80211_recv_pspoll(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m0)
+{
+	struct ieee80211_frame_min *wh;
+	struct mbuf *m;
+	u_int16_t aid;
+	int qlen;
+
+	wh = mtod(m0, struct ieee80211_frame_min *);
+	if (ni->ni_associd == 0) {
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+		    (struct ieee80211_frame *) wh, "ps-poll",
+		    "%s", "unassociated station");
+		ic->ic_stats.is_ps_unassoc++;
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+			IEEE80211_REASON_NOT_ASSOCED);
+		return;
+	}
+
+	aid = le16toh(*(u_int16_t *)wh->i_dur);
+	if (aid != ni->ni_associd) {
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+		    (struct ieee80211_frame *) wh, "ps-poll",
+		    "aid mismatch: sta aid 0x%x poll aid 0x%x",
+		    ni->ni_associd, aid);
+		ic->ic_stats.is_ps_badaid++;
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+			IEEE80211_REASON_NOT_ASSOCED);
+		return;
+	}
+
+	/* Okay, take the first queued packet and put it out... */
+	IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+	if (m == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] recv ps-poll, but queue empty\n",
+		    (wh->i_addr2), ":");
+		ieee80211_send_nulldata(ic, ni);
+		ic->ic_stats.is_ps_qempty++;	/* XXX node stat */
+		ic->ic_set_tim(ic, ni, 0);	/* just in case */
+		return;
+	}
+	/* 
+	 * If there are more packets, set the more packets bit
+	 * in the packet dispatched to the station; otherwise
+	 * turn off the TIM bit.
+	 */
+	if (qlen != 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] recv ps-poll, send packet, %u still queued\n",
+		    (ni->ni_macaddr), ":", qlen);
+		wh = mtod(m, struct ieee80211_frame_min *);
+		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+	} else {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] recv ps-poll, send packet, queue empty\n",
+		    (ni->ni_macaddr), ":");
+		ic->ic_set_tim(ic, ni, 0);
+	}
+	m->m_flags |= M_PWR_SAV;		/* bypass PS handling */
+	IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+}
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Debugging support.
+ */
+
+/*
+ * Return the bssid of a frame.
+ */
+static const u_int8_t *
+ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
+{
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		return wh->i_addr2;
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
+		return wh->i_addr1;
+	if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+		return wh->i_addr1;
+	return wh->i_addr3;
+}
+
+static void
+ieee80211_discard_frame(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh,
+	const char *type, const char *fmt, ...)
+{
+	__va_list ap;
+
+	printf("[%6D] discard ", (ieee80211_getbssid(ic, wh)), ":");
+	if (type != NULL)
+		printf(" %s frame, ", type);
+	else
+		printf(" frame, ");
+	__va_start(ap, fmt);
+	vprintf(fmt, ap);
+	__va_end(ap);
+	printf("\n");
+}
+
+static void
+ieee80211_discard_ie(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh,
+	const char *type, const char *fmt, ...)
+{
+	__va_list ap;
+
+	printf("[%6D] discard ", (ieee80211_getbssid(ic, wh)),":");
+	if (type != NULL)
+		printf(" %s information element, ", type);
+	else
+		printf(" information element, ");
+	__va_start(ap, fmt);
+	vprintf(fmt, ap);
+	__va_end(ap);
+	printf("\n");
+}
+
+static void
+ieee80211_discard_mac(struct ieee80211com *ic,
+	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	const char *type, const char *fmt, ...)
+{
+	__va_list ap;
+
+	printf("[%6D] discard ", (mac), ":");
+	if (type != NULL)
+		printf(" %s frame, ", type);
+	else
+		printf(" frame, ");
+	__va_start(ap, fmt);
+	vprintf(fmt, ap);
+	__va_end(ap);
+	printf("\n");
+}
+#endif /* IEEE80211_DEBUG */
Index: src/sys/netproto/802_11/ieee80211_ioctl.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_ioctl.c,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_ioctl.c
--- src/sys/netproto/802_11/ieee80211_ioctl.c	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_ioctl.c	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,11 +28,10 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.13 2004/03/30 22:57:57 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_ioctl.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 /*
  * IEEE 802.11 ioctl support (FreeBSD-specific)
  */
@@ -43,11 +42,10 @@
 #include <sys/endian.h>
 #include <sys/param.h>
 #include <sys/kernel.h>
-#include <sys/proc.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
 #include <sys/systm.h>
-#include <sys/thread.h>
+#include <sys/proc.h>
  
 #include <net/if.h>
 #include <net/if_arp.h>
@@ -69,26 +67,129 @@
 
 #include <netproto/802_11/if_wavelan_ieee.h>
 
+#define	IS_UP(_ic) \
+	(((_ic)->ic_ifp->if_flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+#define	IS_UP_AUTO(_ic) \
+	(IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
+
 /*
  * XXX
  * Wireless LAN specific configuration interface, which is compatible
  * with wicontrol(8).
  */
 
+struct wi_read_ap_args {
+	int	i;		/* result count */
+	struct wi_apinfo *ap;	/* current entry in result buffer */
+	caddr_t	max;		/* result buffer bound */
+};
+
+static void
+wi_read_ap_result(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct wi_read_ap_args *sa = arg;
+	struct wi_apinfo *ap = sa->ap;
+	struct ieee80211_rateset *rs;
+	int j;
+
+	if ((caddr_t)(ap + 1) > sa->max)
+		return;
+	memset(ap, 0, sizeof(struct wi_apinfo));
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
+		ap->namelen = ic->ic_des_esslen;
+		if (ic->ic_des_esslen)
+			memcpy(ap->name, ic->ic_des_essid,
+			    ic->ic_des_esslen);
+	} else {
+		IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
+		ap->namelen = ni->ni_esslen;
+		if (ni->ni_esslen)
+			memcpy(ap->name, ni->ni_essid,
+			    ni->ni_esslen);
+	}
+	ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
+	ap->signal = ic->ic_node_getrssi(ni);
+	ap->capinfo = ni->ni_capinfo;
+	ap->interval = ni->ni_intval;
+	rs = &ni->ni_rates;
+	for (j = 0; j < rs->rs_nrates; j++) {
+		if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
+			ap->rate = (rs->rs_rates[j] &
+			    IEEE80211_RATE_VAL) * 5; /* XXX */
+		}
+	}
+	sa->i++;
+	sa->ap++;
+}
+
+struct wi_read_prism2_args {
+	int	i;		/* result count */
+	struct wi_scan_res *res;/* current entry in result buffer */
+	caddr_t	max;		/* result buffer bound */
+};
+
+static void
+wi_read_prism2_result(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct wi_read_prism2_args *sa = arg;
+	struct wi_scan_res *res = sa->res;
+
+	if ((caddr_t)(res + 1) > sa->max)
+		return;
+	res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
+	res->wi_noise = 0;
+	res->wi_signal = ic->ic_node_getrssi(ni);
+	IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
+	res->wi_interval = ni->ni_intval;
+	res->wi_capinfo = ni->ni_capinfo;
+	res->wi_ssid_len = ni->ni_esslen;
+	memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
+	/* NB: assumes wi_srates holds <= ni->ni_rates */
+	memcpy(res->wi_srates, ni->ni_rates.rs_rates,
+		sizeof(res->wi_srates));
+	if (ni->ni_rates.rs_nrates < 10)
+		res->wi_srates[ni->ni_rates.rs_nrates] = 0;
+	res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
+	res->wi_rsvd = 0;
+
+	sa->i++;
+	sa->res++;
+}
+
+struct wi_read_sigcache_args {
+	int	i;		/* result count */
+	struct wi_sigcache *wsc;/* current entry in result buffer */
+	caddr_t	max;		/* result buffer bound */
+};
+
+static void
+wi_read_sigcache(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct wi_read_sigcache_args *sa = arg;
+	struct wi_sigcache *wsc = sa->wsc;
+
+	if ((caddr_t)(wsc + 1) > sa->max)
+		return;
+	memset(wsc, 0, sizeof(struct wi_sigcache));
+	IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr);
+	wsc->signal = ic->ic_node_getrssi(ni);
+
+	sa->wsc++;
+	sa->i++;
+}
+
 int
-ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data, struct ucred *cr)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	int i, j, error;
 	struct ifreq *ifr = (struct ifreq *)data;
 	struct wi_req wreq;
 	struct wi_ltv_keys *keys;
-	struct wi_apinfo *ap;
-	struct ieee80211_node *ni;
-	struct ieee80211_rateset *rs;
-	struct wi_sigcache wsc;
-	struct wi_scan_p2_hdr *p2;
-	struct wi_scan_res *res;
 
 	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
 	if (error)
@@ -139,7 +240,7 @@
 			i = 0;
 		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++)
 			if (isset(ic->ic_chan_active, i)) {
-				setbit((uint8_t *)wreq.wi_val, j);
+				setbit((u_int8_t *)wreq.wi_val, j);
 				wreq.wi_len = j / 16 + 1;
 			}
 		break;
@@ -155,8 +256,7 @@
 		break;
 	case WI_RID_COMMS_QUALITY:
 		wreq.wi_val[0] = 0;				/* quality */
-		wreq.wi_val[1] =
-			htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss));
+		wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss));
 		wreq.wi_val[2] = 0;				/* noise */
 		wreq.wi_len = 3;
 		break;
@@ -201,7 +301,7 @@
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_ROAMING_MODE:
-		wreq.wi_val[0] = htole16(1);	/* enabled ... not supported */
+		wreq.wi_val[0] = htole16(ic->ic_roaming);	/* XXX map */
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_SYSTEM_SCALE:
@@ -222,8 +322,7 @@
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_WEP_AVAIL:
-		wreq.wi_val[0] =
-		    htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
+		wreq.wi_val[0] = htole16(1);	/* always available */
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_CNFAUTHMODE:
@@ -232,11 +331,11 @@
 		break;
 	case WI_RID_ENCRYPTION:
 		wreq.wi_val[0] =
-		    htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0);
+		    htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0);
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_TX_CRYPT_KEY:
-		wreq.wi_val[0] = htole16(ic->ic_wep_txkey);
+		wreq.wi_val[0] = htole16(ic->ic_def_txkey);
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_DEFLT_CRYPT_KEYS:
@@ -250,124 +349,71 @@
 		}
 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
 			keys->wi_keys[i].wi_keylen =
-			    htole16(ic->ic_nw_keys[i].wk_len);
+			    htole16(ic->ic_nw_keys[i].wk_keylen);
 			memcpy(keys->wi_keys[i].wi_keydat,
-			    ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
+			    ic->ic_nw_keys[i].wk_key,
+			    ic->ic_nw_keys[i].wk_keylen);
 		}
 		wreq.wi_len = sizeof(*keys) / 2;
 		break;
 	case WI_RID_MAX_DATALEN:
-		wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN);	/* TODO: frag */
+		wreq.wi_val[0] = htole16(ic->ic_fragthreshold);
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_IFACE_STATS:
 		/* XXX: should be implemented in lower drivers */
 		break;
 	case WI_RID_READ_APS:
-		if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
-			/*
-			 * Don't return results until active scan completes.
-			 */
-			if (ic->ic_state == IEEE80211_S_SCAN &&
-			    (ic->ic_flags & IEEE80211_F_ASCAN)) {
-				error = EINPROGRESS;
-				break;
-			}
-		}
-		i = 0;
-		ap = (void *)((char *)wreq.wi_val + sizeof(i));
-		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-			if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
-				break;
-			memset(ap, 0, sizeof(*ap));
-			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
-				ap->namelen = ic->ic_des_esslen;
-				if (ic->ic_des_esslen)
-					memcpy(ap->name, ic->ic_des_essid,
-					    ic->ic_des_esslen);
-			} else {
-				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
-				ap->namelen = ni->ni_esslen;
-				if (ni->ni_esslen)
-					memcpy(ap->name, ni->ni_essid,
-					    ni->ni_esslen);
-			}
-			ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
-			ap->signal = (*ic->ic_node_getrssi)(ic, ni);
-			ap->capinfo = ni->ni_capinfo;
-			ap->interval = ni->ni_intval;
-			rs = &ni->ni_rates;
-			for (j = 0; j < rs->rs_nrates; j++) {
-				if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
-					ap->rate = (rs->rs_rates[j] &
-					    IEEE80211_RATE_VAL) * 5; /* XXX */
-				}
-			}
-			i++;
-			ap++;
-		}
-		memcpy(wreq.wi_val, &i, sizeof(i));
-		wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
+		/*
+		 * Don't return results until active scan completes.
+		 */
+		if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) {
+			struct wi_read_ap_args args;
+
+			args.i = 0;
+			args.ap = (void *)((char *)wreq.wi_val + sizeof(i));
+			args.max = (void *)(&wreq + 1);
+			ieee80211_iterate_nodes(&ic->ic_scan,
+				wi_read_ap_result, &args);
+			memcpy(wreq.wi_val, &args.i, sizeof(args.i));
+			wreq.wi_len = (sizeof(int) +
+				sizeof(struct wi_apinfo) * args.i) / 2;
+		} else
+			error = EINPROGRESS;
 		break;
 	case WI_RID_PRISM2:
-		wreq.wi_val[0] = 1;	/* XXX lie so SCAN_RES can give rates */
-		wreq.wi_len = sizeof(uint16_t) / 2;
+		/* NB: we lie so WI_RID_SCAN_RES can include rates */
+		wreq.wi_val[0] = 1;
+		wreq.wi_len = sizeof(u_int16_t) / 2;
 		break;
 	case WI_RID_SCAN_RES:			/* compatibility interface */
-		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
-		    ic->ic_state == IEEE80211_S_SCAN &&
-		    (ic->ic_flags & IEEE80211_F_ASCAN)) {
+		if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) {
+			struct wi_read_prism2_args args;
+			struct wi_scan_p2_hdr *p2;
+
+			/* NB: use Prism2 format so we can include rate info */
+			p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
+			args.i = 0;
+			args.res = (void *)&p2[1];
+			args.max = (void *)(&wreq + 1);
+			ieee80211_iterate_nodes(&ic->ic_scan,
+				wi_read_prism2_result, &args);
+			p2->wi_rsvd = 0;
+			p2->wi_reason = args.i;
+			wreq.wi_len = (sizeof(*p2) +
+				sizeof(struct wi_scan_res) * args.i) / 2;
+		} else
 			error = EINPROGRESS;
-			break;
-		}
-		/* NB: we use the Prism2 format so we can return rate info */
-		p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
-		res = (void *)&p2[1];
-		i = 0;
-		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-			if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
-				break;
-			res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
-			res->wi_noise = 0;
-			res->wi_signal = (*ic->ic_node_getrssi)(ic, ni);
-			IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
-			res->wi_interval = ni->ni_intval;
-			res->wi_capinfo = ni->ni_capinfo;
-			res->wi_ssid_len = ni->ni_esslen;
-			memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
-			/* NB: assumes wi_srates holds <= ni->ni_rates */
-			memcpy(res->wi_srates, ni->ni_rates.rs_rates,
-				sizeof(res->wi_srates));
-			if (ni->ni_rates.rs_nrates < 10)
-				res->wi_srates[ni->ni_rates.rs_nrates] = 0;
-			res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
-			res->wi_rsvd = 0;
-			res++, i++;
-		}
-		p2->wi_rsvd = 0;
-		p2->wi_reason = i;
-		wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
-		break;
-	case WI_RID_READ_CACHE:
-		i = 0;
-		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-			if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
-				break;
-			IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
-			memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
-			wsc.signal = (*ic->ic_node_getrssi)(ic, ni);
-			wsc.noise = 0;
-			wsc.quality = 0;
-			memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
-			    &wsc, sizeof(wsc));
-			i++;
-		}
-		wreq.wi_len = sizeof(wsc) * i / 2;
 		break;
-	case WI_RID_SCAN_APS:
-		error = EINVAL;
+	case WI_RID_READ_CACHE: {
+		struct wi_read_sigcache_args args;
+		args.i = 0;
+		args.wsc = (struct wi_sigcache *) wreq.wi_val;
+		args.max = (void *)(&wreq + 1);
+		ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args);
+		wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2;
 		break;
+	}
 	default:
 		error = EINVAL;
 		break;
@@ -399,11 +445,20 @@
  * the active list as the place to start the scan.
  */
 static int
-ieee80211_setupscan(struct ieee80211com *ic)
+ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[])
 {
-	u_char *chanlist = ic->ic_chan_active;
 	int i;
 
+	/*
+	 * XXX don't permit a scan to be started unless we
+	 * know the device is ready.  For the moment this means
+	 * the device is marked up as this is the required to
+	 * initialize the hardware.  It would be better to permit
+	 * scanning prior to being up but that'll require some
+	 * changes to the infrastructure.
+	 */
+	if (!IS_UP(ic))
+		return EINVAL;
 	if (ic->ic_ibss_chan == NULL ||
 	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
 		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
@@ -418,21 +473,21 @@
 	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
 	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
 		ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
 	/*
-	 * XXX don't permit a scan to be started unless we
-	 * know the device is ready.  For the moment this means
-	 * the device is marked up as this is the required to
-	 * initialize the hardware.  It would be better to permit
-	 * scanning prior to being up but that'll require some
-	 * changes to the infrastructure.
+	 * We force the state to INIT before calling ieee80211_new_state
+	 * to get ieee80211_begin_scan called.  We really want to scan w/o
+	 * altering the current state but that's not possible right now.
 	 */
-	return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET;
+	/* XXX handle proberequest case */
+	ic->ic_state = IEEE80211_S_INIT;	/* XXX bypass state machine */
+	return 0;
 }
 
 int
-ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
+ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	int i, j, len, error, rate;
 	struct ifreq *ifr = (struct ifreq *)data;
 	struct wi_ltv_keys *keys;
@@ -472,7 +527,9 @@
 		    isclr(ic->ic_chan_active, i))
 			return EINVAL;
 		ic->ic_ibss_chan = &ic->ic_channels[i];
-		if (ic->ic_flags & IEEE80211_F_SIBSS)
+		if (ic->ic_opmode == IEEE80211_M_MONITOR)
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		else
 			error = ENETRESET;
 		break;
 	case WI_RID_CURRENT_CHAN:
@@ -518,7 +575,7 @@
 		}
 		if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
 			ic->ic_opmode = le16toh(wreq.wi_val[0]);
-			error = ENETRESET;
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		}
 		break;
 #if 0
@@ -566,7 +623,7 @@
 		return EINVAL;
 	setrate:
 		ic->ic_fixed_rate = i;
-		error = ENETRESET;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_CUR_TX_RATE:
 		return EPERM;
@@ -586,14 +643,14 @@
 				ic->ic_flags |= IEEE80211_F_IBSSON;
 				if (ic->ic_opmode == IEEE80211_M_IBSS &&
 				    ic->ic_state == IEEE80211_S_SCAN)
-					error = ENETRESET;
+					error = IS_UP_AUTO(ic) ? ENETRESET : 0;
 			}
 		} else {
 			if (ic->ic_flags & IEEE80211_F_IBSSON) {
 				ic->ic_flags &= ~IEEE80211_F_IBSSON;
 				if (ic->ic_flags & IEEE80211_F_SIBSS) {
 					ic->ic_flags &= ~IEEE80211_F_SIBSS;
-					error = ENETRESET;
+					error = IS_UP_AUTO(ic) ? ENETRESET : 0;
 				}
 			}
 		}
@@ -607,8 +664,10 @@
 	case WI_RID_ROAMING_MODE:
 		if (len != 2)
 			return EINVAL;
-		if (le16toh(wreq.wi_val[0]) != 1)
+		i = le16toh(wreq.wi_val[0]);
+		if (i > IEEE80211_ROAMING_MANUAL)
 			return EINVAL;		/* not supported */
+		ic->ic_roaming = i;
 		break;
 	case WI_RID_SYSTEM_SCALE:
 		if (len != 2)
@@ -624,12 +683,12 @@
 				return EINVAL;
 			if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
 				ic->ic_flags |= IEEE80211_F_PMGTON;
-				error = ENETRESET;
+				error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 			}
 		} else {
 			if (ic->ic_flags & IEEE80211_F_PMGTON) {
 				ic->ic_flags &= ~IEEE80211_F_PMGTON;
-				error = ENETRESET;
+				error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 			}
 		}
 		break;
@@ -638,7 +697,7 @@
 			return EINVAL;
 		ic->ic_lintval = le16toh(wreq.wi_val[0]);
 		if (ic->ic_flags & IEEE80211_F_PMGTON)
-			error = ENETRESET;
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_CUR_BEACON_INT:
 		return EPERM;
@@ -647,8 +706,11 @@
 	case WI_RID_CNFAUTHMODE:
 		if (len != 2)
 			return EINVAL;
-		if (le16toh(wreq.wi_val[0]) != 1)
-			return EINVAL;		/* TODO: shared key auth */
+		i = le16toh(wreq.wi_val[0]);
+		if (i > IEEE80211_AUTH_WPA)
+			return EINVAL;
+		ic->ic_bss->ni_authmode = i;		/* XXX ENETRESET? */
+		error = ENETRESET;
 		break;
 	case WI_RID_ENCRYPTION:
 		if (len != 2)
@@ -656,13 +718,13 @@
 		if (wreq.wi_val[0] != 0) {
 			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
 				return EINVAL;
-			if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
-				ic->ic_flags |= IEEE80211_F_WEPON;
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				ic->ic_flags |= IEEE80211_F_PRIVACY;
 				error = ENETRESET;
 			}
 		} else {
-			if (ic->ic_flags & IEEE80211_F_WEPON) {
-				ic->ic_flags &= ~IEEE80211_F_WEPON;
+			if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+				ic->ic_flags &= ~IEEE80211_F_PRIVACY;
 				error = ENETRESET;
 			}
 		}
@@ -673,7 +735,8 @@
 		i = le16toh(wreq.wi_val[0]);
 		if (i >= IEEE80211_WEP_NKID)
 			return EINVAL;
-		ic->ic_wep_txkey = i;
+		ic->ic_def_txkey = i;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_DEFLT_CRYPT_KEYS:
 		if (len != sizeof(struct wi_ltv_keys))
@@ -683,15 +746,20 @@
 			len = le16toh(keys->wi_keys[i].wi_keylen);
 			if (len != 0 && len < IEEE80211_WEP_KEYLEN)
 				return EINVAL;
-			if (len > sizeof(ic->ic_nw_keys[i].wk_key))
+			if (len > IEEE80211_KEYBUF_SIZE)
 				return EINVAL;
 		}
-		memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+			struct ieee80211_key *k = &ic->ic_nw_keys[i];
+
 			len = le16toh(keys->wi_keys[i].wi_keylen);
-			ic->ic_nw_keys[i].wk_len = len;
-			memcpy(ic->ic_nw_keys[i].wk_key,
-			    keys->wi_keys[i].wi_keydat, len);
+			k->wk_keylen = len;
+			k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+			memset(k->wk_key, 0, sizeof(k->wk_key));
+			memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len);
+#if 0
+			k->wk_type = IEEE80211_CIPHER_WEP;
+#endif
 		}
 		error = ENETRESET;
 		break;
@@ -701,10 +769,8 @@
 		len = le16toh(wreq.wi_val[0]);
 		if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
 			return EINVAL;
-		if (len != IEEE80211_MAX_LEN)
-			return EINVAL;		/* TODO: fragment */
 		ic->ic_fragthreshold = len;
-		error = ENETRESET;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_IFACE_STATS:
 		error = EPERM;
@@ -712,7 +778,7 @@
 	case WI_RID_SCAN_REQ:			/* XXX wicontrol */
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
 			break;
-		error = ieee80211_setupscan(ic);
+		error = ieee80211_setupscan(ic, ic->ic_chan_avail);
 		if (error == 0)
 			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
 		break;
@@ -734,7 +800,7 @@
 		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
 			if ((j / 8) >= len)
 				break;
-			if (isclr((uint8_t *)wreq.wi_val, j))
+			if (isclr((u_int8_t *)wreq.wi_val, j))
 				continue;
 			if (isclr(ic->ic_chan_active, i)) {
 				if (wreq.wi_type != WI_RID_CHANNEL_LIST)
@@ -744,9 +810,7 @@
 			}
 			setbit(chanlist, i);
 		}
-		memcpy(ic->ic_chan_active, chanlist,
-		    sizeof(ic->ic_chan_active));
-		error = ieee80211_setupscan(ic);
+		error = ieee80211_setupscan(ic, chanlist);
 		if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
 			/* NB: ignore error from ieee80211_setupscan */
 			error = ENETRESET;
@@ -757,311 +821,1534 @@
 		error = EINVAL;
 		break;
 	}
+	if (error == ENETRESET && !IS_UP_AUTO(ic))
+		error = 0;
 	return error;
 }
 
-int
-ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+static struct ieee80211_channel *
+getcurchan(struct ieee80211com *ic)
+{
+	switch (ic->ic_state) {
+	case IEEE80211_S_INIT:
+	case IEEE80211_S_SCAN:
+		return ic->ic_des_chan;
+	default:
+		return ic->ic_ibss_chan;
+	}
+}
+
+static int
+cap2cipher(int flag)
+{
+	switch (flag) {
+	case IEEE80211_C_WEP:		return IEEE80211_CIPHER_WEP;
+	case IEEE80211_C_AES:		return IEEE80211_CIPHER_AES_OCB;
+	case IEEE80211_C_AES_CCM:	return IEEE80211_CIPHER_AES_CCM;
+	case IEEE80211_C_CKIP:		return IEEE80211_CIPHER_CKIP;
+	case IEEE80211_C_TKIP:		return IEEE80211_CIPHER_TKIP;
+	}
+	return -1;
+}
+
+static int
+ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq, struct ucred *cr)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_key ik;
+	struct ieee80211_key *wk;
+	const struct ieee80211_cipher *cip;
+	u_int kid;
+	int error;
+
+	if (ireq->i_len != sizeof(ik))
+		return EINVAL;
+	error = copyin(ireq->i_data, &ik, sizeof(ik));
+	if (error)
+		return error;
+	kid = ik.ik_keyix;
+	if (kid == IEEE80211_KEYIX_NONE) {
+		ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+		if (ni == NULL)
+			return EINVAL;		/* XXX */
+		wk = &ni->ni_ucastkey;
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		wk = &ic->ic_nw_keys[kid];
+		IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
+		ni = NULL;
+	}
+	cip = wk->wk_cipher;
+	ik.ik_type = cip->ic_cipher;
+	ik.ik_keylen = wk->wk_keylen;
+	ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
+	if (wk->wk_keyix == ic->ic_def_txkey)
+		ik.ik_flags |= IEEE80211_KEY_DEFAULT;
+	if (suser_cred(cr, NULL_CRED_OKAY) == 0) {
+		/* NB: only root can read key data */
+		ik.ik_keyrsc = wk->wk_keyrsc;
+		ik.ik_keytsc = wk->wk_keytsc;
+		memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
+		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
+			memcpy(ik.ik_keydata+wk->wk_keylen,
+				wk->wk_key + IEEE80211_KEYBUF_SIZE,
+				IEEE80211_MICBUF_SIZE);
+			ik.ik_keylen += IEEE80211_MICBUF_SIZE;
+		}
+	} else {
+		ik.ik_keyrsc = 0;
+		ik.ik_keytsc = 0;
+		memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
+	}
+	if (ni != NULL)
+		ieee80211_free_node(ni);
+	return copyout(&ik, ireq->i_data, sizeof(ik));
+}
+
+static int
+ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+
+	if (sizeof(ic->ic_chan_active) > ireq->i_len)
+		ireq->i_len = sizeof(ic->ic_chan_active);
+	return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_chaninfo chans;	/* XXX off stack? */
+	int i, space;
+
+	/*
+	 * Since channel 0 is not available for DS, channel 1
+	 * is assigned to LSB on WaveLAN.
+	 */
+	if (ic->ic_phytype == IEEE80211_T_DS)
+		i = 1;
+	else
+		i = 0;
+	memset(&chans, 0, sizeof(chans));
+	for (; i <= IEEE80211_CHAN_MAX; i++)
+		if (isset(ic->ic_chan_avail, i)) {
+			struct ieee80211_channel *c = &ic->ic_channels[i];
+			chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq;
+			chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags;
+			chans.ic_nchans++;
+		}
+	space = __offsetof(struct ieee80211req_chaninfo,
+			ic_chans[chans.ic_nchans]);
+	if (space > ireq->i_len)
+		space = ireq->i_len;
+	return copyout(&chans, ireq->i_data, space);
+}
+
+static int
+ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_wpaie wpaie;
+	int error;
+
+	if (ireq->i_len < IEEE80211_ADDR_LEN)
+		return EINVAL;
+	error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
+	if (ni->ni_wpa_ie != NULL) {
+		int ielen = ni->ni_wpa_ie[1] + 2;
+		if (ielen > sizeof(wpaie.wpa_ie))
+			ielen = sizeof(wpaie.wpa_ie);
+		memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
+	}
+	ieee80211_free_node(ni);
+	if (ireq->i_len > sizeof(wpaie))
+		ireq->i_len = sizeof(wpaie);
+	return copyout(&wpaie, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	u_int8_t macaddr[IEEE80211_ADDR_LEN];
+	const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
+	int error;
+
+	if (ireq->i_len < off)
+		return EINVAL;
+	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
+		ireq->i_len = sizeof(struct ieee80211req_sta_stats);
+	/* NB: copy out only the statistics */
+	error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off,
+			ireq->i_len - off);
+	ieee80211_free_node(ni);
+	return error;
+}
+
+static void
+get_scan_result(struct ieee80211req_scan_result *sr,
+	const struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	memset(sr, 0, sizeof(*sr));
+	sr->isr_ssid_len = ni->ni_esslen;
+	if (ni->ni_wpa_ie != NULL)
+		sr->isr_ie_len += 2+ni->ni_wpa_ie[1];
+	if (ni->ni_wme_ie != NULL)
+		sr->isr_ie_len += 2+ni->ni_wme_ie[1];
+	sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len;
+	sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t));
+	if (ni->ni_chan != IEEE80211_CHAN_ANYC) {
+		sr->isr_freq = ni->ni_chan->ic_freq;
+		sr->isr_flags = ni->ni_chan->ic_flags;
+	}
+	sr->isr_rssi = ic->ic_node_getrssi(ni);
+	sr->isr_intval = ni->ni_intval;
+	sr->isr_capinfo = ni->ni_capinfo;
+	sr->isr_erp = ni->ni_erp;
+	IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid);
+	sr->isr_nrates = ni->ni_rates.rs_nrates;
+	if (sr->isr_nrates > 15)
+		sr->isr_nrates = 15;
+	memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates);
+}
+
+static int
+ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	union {
+		struct ieee80211req_scan_result res;
+		char data[512];		/* XXX shrink? */
+	} u;
+	struct ieee80211req_scan_result *sr = &u.res;
+	struct ieee80211_node_table *nt;
+	struct ieee80211_node *ni;
+	int error, space;
+	u_int8_t *p, *cp;
+
+	p = ireq->i_data;
+	space = ireq->i_len;
+	error = 0;
+	/* XXX locking */
+	nt =  &ic->ic_scan;
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+		/* NB: skip pre-scan node state */ 
+		if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+			continue;
+		get_scan_result(sr, ni);
+		if (sr->isr_len > sizeof(u))
+			continue;		/* XXX */
+		if (space < sr->isr_len)
+			break;
+		cp = (u_int8_t *)(sr+1);
+		memcpy(cp, ni->ni_essid, ni->ni_esslen);
+		cp += ni->ni_esslen;
+		if (ni->ni_wpa_ie != NULL) {
+			memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]);
+			cp += 2+ni->ni_wpa_ie[1];
+		}
+		if (ni->ni_wme_ie != NULL) {
+			memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]);
+			cp += 2+ni->ni_wme_ie[1];
+		}
+		error = copyout(sr, p, sr->isr_len);
+		if (error)
+			break;
+		p += sr->isr_len;
+		space -= sr->isr_len;
+	}
+	ireq->i_len -= space;
+	return error;
+}
+
+static void
+get_sta_info(struct ieee80211req_sta_info *si, const struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	si->isi_ie_len = 0;
+	if (ni->ni_wpa_ie != NULL)
+		si->isi_ie_len += 2+ni->ni_wpa_ie[1];
+	if (ni->ni_wme_ie != NULL)
+		si->isi_ie_len += 2+ni->ni_wme_ie[1];
+	si->isi_len = sizeof(*si) + si->isi_ie_len, sizeof(u_int32_t);
+	si->isi_len = roundup(si->isi_len, sizeof(u_int32_t));
+	si->isi_freq = ni->ni_chan->ic_freq;
+	si->isi_flags = ni->ni_chan->ic_flags;
+	si->isi_state = ni->ni_flags;
+	si->isi_authmode = ni->ni_authmode;
+	si->isi_rssi = ic->ic_node_getrssi(ni);
+	si->isi_capinfo = ni->ni_capinfo;
+	si->isi_erp = ni->ni_erp;
+	IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
+	si->isi_nrates = ni->ni_rates.rs_nrates;
+	if (si->isi_nrates > 15)
+		si->isi_nrates = 15;
+	memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
+	si->isi_txrate = ni->ni_txrate;
+	si->isi_associd = ni->ni_associd;
+	si->isi_txpower = ni->ni_txpower;
+	si->isi_vlan = ni->ni_vlan;
+	if (ni->ni_flags & IEEE80211_NODE_QOS) {
+		memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
+		memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
+	} else {
+		si->isi_txseqs[0] = ni->ni_txseqs[0];
+		si->isi_rxseqs[0] = ni->ni_rxseqs[0];
+	}
+	if (ic->ic_opmode == IEEE80211_M_IBSS || ni->ni_associd != 0)
+		si->isi_inact = ic->ic_inact_run;
+	else if (ieee80211_node_is_authorized(ni))
+		si->isi_inact = ic->ic_inact_auth;
+	else
+		si->isi_inact = ic->ic_inact_init;
+	si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
+}
+
+static int
+ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	union {
+		struct ieee80211req_sta_info info;
+		char data[512];		/* XXX shrink? */
+	} u;
+	struct ieee80211req_sta_info *si = &u.info;
+	struct ieee80211_node_table *nt;
+	struct ieee80211_node *ni;
+	int error, space;
+	u_int8_t *p, *cp;
+
+	nt = &ic->ic_sta;
+	p = ireq->i_data;
+	space = ireq->i_len;
+	error = 0;
+	/* XXX locking */
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+		get_sta_info(si, ni);
+		if (si->isi_len > sizeof(u))
+			continue;		/* XXX */
+		if (space < si->isi_len)
+			break;
+		cp = (u_int8_t *)(si+1);
+		if (ni->ni_wpa_ie != NULL) {
+			memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]);
+			cp += 2+ni->ni_wpa_ie[1];
+		}
+		if (ni->ni_wme_ie != NULL) {
+			memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]);
+			cp += 2+ni->ni_wme_ie[1];
+		}
+		error = copyout(si, p, si->isi_len);
+		if (error)
+			break;
+		p += si->isi_len;
+		space -= si->isi_len;
+	}
+	ireq->i_len -= space;
+	return error;
+}
+
+static int
+ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_sta_txpow txpow;
+	int error;
+
+	if (ireq->i_len != sizeof(txpow))
+		return EINVAL;
+	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	txpow.it_txpow = ni->ni_txpower;
+	error = copyout(&txpow, ireq->i_data, sizeof(txpow));
+	ieee80211_free_node(ni);
+	return error;
+}
+
+static int
+ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	struct wmeParams *wmep;
+	int ac;
+
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		return EINVAL;
+
+	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+	if (ac >= WME_NUM_AC)
+		ac = WME_AC_BE;
+	if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+	else
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+		ireq->i_val = wmep->wmep_logcwmin;
+		break;
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+		ireq->i_val = wmep->wmep_logcwmax;
+		break;
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+		ireq->i_val = wmep->wmep_aifsn;
+		break;
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+		ireq->i_val = wmep->wmep_txopLimit;
+		break;
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+		ireq->i_val = wmep->wmep_acm;
+		break;
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+		ireq->i_val = !wmep->wmep_noackPolicy;
+		break;
+	}
+	return 0;
+}
+
+/*
+ * When building the kernel with -O2 on the i386 architecture, gcc
+ * seems to want to inline this function into ieee80211_ioctl()
+ * (which is the only routine that calls it). When this happens,
+ * ieee80211_ioctl() ends up consuming an additional 2K of stack
+ * space. (Exactly why it needs so much is unclear.) The problem
+ * is that it's possible for ieee80211_ioctl() to invoke other
+ * routines (including driver init functions) which could then find
+ * themselves perilously close to exhausting the stack.
+ *
+ * To avoid this, we deliberately prevent gcc from inlining this
+ * routine. Another way to avoid this is to use less agressive
+ * optimization when compiling this file (i.e. -O instead of -O2)
+ * but special-casing the compilation of this one module in the
+ * build system would be awkward.
+ */
+#ifdef __GNUC__
+__attribute__ ((noinline))
+#endif
+static int
+ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq, struct ucred *cr)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
 	int error = 0;
-	u_int kid, len;
-	struct ieee80211req *ireq;
-	struct ifreq *ifr;
-	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
+	u_int kid, len, m;
+	u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
 	char tmpssid[IEEE80211_NWID_LEN];
-	struct ieee80211_channel *chan;
-	struct ifaddr *ifa;			/* XXX */
 
-	switch (cmd) {
-	case SIOCSIFMEDIA:
-	case SIOCGIFMEDIA:
-		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
-				&ic->ic_media, cmd);
-		break;
-	case SIOCG80211:
-		ireq = (struct ieee80211req *) data;
-		switch (ireq->i_type) {
-		case IEEE80211_IOC_SSID:
-			switch (ic->ic_state) {
-			case IEEE80211_S_INIT:
-			case IEEE80211_S_SCAN:
-				ireq->i_len = ic->ic_des_esslen;
-				memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
-				break;
-			default:
-				ireq->i_len = ic->ic_bss->ni_esslen;
-				memcpy(tmpssid, ic->ic_bss->ni_essid,
-					ireq->i_len);
-				break;
-			}
-			error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_SSID:
+		switch (ic->ic_state) {
+		case IEEE80211_S_INIT:
+		case IEEE80211_S_SCAN:
+			ireq->i_len = ic->ic_des_esslen;
+			memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
+			break;
+		default:
+			ireq->i_len = ic->ic_bss->ni_esslen;
+			memcpy(tmpssid, ic->ic_bss->ni_essid,
+				ireq->i_len);
 			break;
-		case IEEE80211_IOC_NUMSSIDS:
+		}
+		error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+		break;
+	case IEEE80211_IOC_NUMSSIDS:
+		ireq->i_val = 1;
+		break;
+	case IEEE80211_IOC_WEP:
+		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+			ireq->i_val = IEEE80211_WEP_OFF;
+		else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+			ireq->i_val = IEEE80211_WEP_ON;
+		else
+			ireq->i_val = IEEE80211_WEP_MIXED;
+		break;
+	case IEEE80211_IOC_WEPKEY:
+		kid = (u_int) ireq->i_val;
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
+		/* NB: only root can read WEP keys */
+		if (suser_cred(cr, NULL_CRED_OKAY) == 0) {
+			bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+		} else {
+			bzero(tmpkey, len);
+		}
+		ireq->i_len = len;
+		error = copyout(tmpkey, ireq->i_data, len);
+		break;
+	case IEEE80211_IOC_NUMWEPKEYS:
+		ireq->i_val = IEEE80211_WEP_NKID;
+		break;
+	case IEEE80211_IOC_WEPTXKEY:
+		ireq->i_val = ic->ic_def_txkey;
+		break;
+	case IEEE80211_IOC_AUTHMODE:
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			ireq->i_val = IEEE80211_AUTH_WPA;
+		else
+			ireq->i_val = ic->ic_bss->ni_authmode;
+		break;
+	case IEEE80211_IOC_CHANNEL:
+		ireq->i_val = ieee80211_chan2ieee(ic, getcurchan(ic));
+		break;
+	case IEEE80211_IOC_POWERSAVE:
+		if (ic->ic_flags & IEEE80211_F_PMGTON)
+			ireq->i_val = IEEE80211_POWERSAVE_ON;
+		else
+			ireq->i_val = IEEE80211_POWERSAVE_OFF;
+		break;
+	case IEEE80211_IOC_POWERSAVESLEEP:
+		ireq->i_val = ic->ic_lintval;
+		break;
+	case IEEE80211_IOC_RTSTHRESHOLD:
+		ireq->i_val = ic->ic_rtsthreshold;
+		break;
+	case IEEE80211_IOC_PROTMODE:
+		ireq->i_val = ic->ic_protmode;
+		break;
+	case IEEE80211_IOC_TXPOWER:
+		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+			return EINVAL;
+		ireq->i_val = ic->ic_txpowlimit;
+		break;
+	case IEEE80211_IOC_MCASTCIPHER:
+		ireq->i_val = rsn->rsn_mcastcipher;
+		break;
+	case IEEE80211_IOC_MCASTKEYLEN:
+		ireq->i_val = rsn->rsn_mcastkeylen;
+		break;
+	case IEEE80211_IOC_UCASTCIPHERS:
+		ireq->i_val = 0;
+		for (m = 0x1; m != 0; m <<= 1)
+			if (rsn->rsn_ucastcipherset & m)
+				ireq->i_val |= 1<<cap2cipher(m);
+		break;
+	case IEEE80211_IOC_UCASTCIPHER:
+		ireq->i_val = rsn->rsn_ucastcipher;
+		break;
+	case IEEE80211_IOC_UCASTKEYLEN:
+		ireq->i_val = rsn->rsn_ucastkeylen;
+		break;
+	case IEEE80211_IOC_KEYMGTALGS:
+		ireq->i_val = rsn->rsn_keymgmtset;
+		break;
+	case IEEE80211_IOC_RSNCAPS:
+		ireq->i_val = rsn->rsn_caps;
+		break;
+	case IEEE80211_IOC_WPA:
+		switch (ic->ic_flags & IEEE80211_F_WPA) {
+		case IEEE80211_F_WPA1:
 			ireq->i_val = 1;
 			break;
-		case IEEE80211_IOC_WEP:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
-				ireq->i_val = IEEE80211_WEP_NOSUP; 
-			} else {
-				if (ic->ic_flags & IEEE80211_F_WEPON) {
-					ireq->i_val =
-					    IEEE80211_WEP_MIXED;
-				} else {
-					ireq->i_val =
-					    IEEE80211_WEP_OFF;
-				}
-			}
+		case IEEE80211_F_WPA2:
+			ireq->i_val = 2;
 			break;
-		case IEEE80211_IOC_WEPKEY:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
-				error = EINVAL;
-				break;
-			}
-			kid = (u_int) ireq->i_val;
-			if (kid >= IEEE80211_WEP_NKID) {
-				error = EINVAL;
-				break;
-			}
-			len = (u_int) ic->ic_nw_keys[kid].wk_len;
-			/* NB: only root can read WEP keys */
-			if (suser_cred(cr, NULL_CRED_OKAY) == 0) {
-				bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
-			} else {
-				bzero(tmpkey, len);
-			}
-			ireq->i_len = len;
-			error = copyout(tmpkey, ireq->i_data, len);
+		case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
+			ireq->i_val = 3;
 			break;
-		case IEEE80211_IOC_NUMWEPKEYS:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
-				error = EINVAL;
-			else
-				ireq->i_val = IEEE80211_WEP_NKID;
+		default:
+			ireq->i_val = 0;
 			break;
-		case IEEE80211_IOC_WEPTXKEY:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
-				error = EINVAL;
-			else
-				ireq->i_val = ic->ic_wep_txkey;
-			break;
-		case IEEE80211_IOC_AUTHMODE:
-			ireq->i_val = IEEE80211_AUTH_OPEN;
+		}
+		break;
+	case IEEE80211_IOC_CHANLIST:
+		error = ieee80211_ioctl_getchanlist(ic, ireq);
+		break;
+	case IEEE80211_IOC_ROAMING:
+		ireq->i_val = ic->ic_roaming;
+		break;
+	case IEEE80211_IOC_PRIVACY:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+		break;
+	case IEEE80211_IOC_DROPUNENCRYPTED:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
+		break;
+	case IEEE80211_IOC_COUNTERMEASURES:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
+		break;
+	case IEEE80211_IOC_DRIVER_CAPS:
+		ireq->i_val = ic->ic_caps>>16;
+		ireq->i_len = ic->ic_caps&0xffff;
+		break;
+	case IEEE80211_IOC_WME:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
+		break;
+	case IEEE80211_IOC_HIDESSID:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
+		break;
+	case IEEE80211_IOC_APBRIDGE:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
+		break;
+	case IEEE80211_IOC_OPTIE:
+		if (ic->ic_opt_ie == NULL)
+			return EINVAL;
+		/* NB: truncate, caller can check length */
+		if (ireq->i_len > ic->ic_opt_ie_len)
+			ireq->i_len = ic->ic_opt_ie_len;
+		error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
+		break;
+	case IEEE80211_IOC_WPAKEY:
+		error = ieee80211_ioctl_getkey(ic, ireq, cr);
+		break;
+	case IEEE80211_IOC_CHANINFO:
+		error = ieee80211_ioctl_getchaninfo(ic, ireq);
+		break;
+	case IEEE80211_IOC_BSSID:
+		if (ireq->i_len != IEEE80211_ADDR_LEN)
+			return EINVAL;
+		error = copyout(ic->ic_state == IEEE80211_S_RUN ?
+					ic->ic_bss->ni_bssid :
+					ic->ic_des_bssid,
+				ireq->i_data, ireq->i_len);
+		break;
+	case IEEE80211_IOC_WPAIE:
+		error = ieee80211_ioctl_getwpaie(ic, ireq);
+		break;
+	case IEEE80211_IOC_SCAN_RESULTS:
+		error = ieee80211_ioctl_getscanresults(ic, ireq);
+		break;
+	case IEEE80211_IOC_STA_STATS:
+		error = ieee80211_ioctl_getstastats(ic, ireq);
+		break;
+	case IEEE80211_IOC_TXPOWMAX:
+		ireq->i_val = ic->ic_bss->ni_txpower;
+		break;
+	case IEEE80211_IOC_STA_TXPOW:
+		error = ieee80211_ioctl_getstatxpow(ic, ireq);
+		break;
+	case IEEE80211_IOC_STA_INFO:
+		error = ieee80211_ioctl_getstainfo(ic, ireq);
+		break;
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
+		error = ieee80211_ioctl_getwmeparam(ic, ireq);
+		break;
+	case IEEE80211_IOC_DTIM_PERIOD:
+		ireq->i_val = ic->ic_dtim_period;
+		break;
+	case IEEE80211_IOC_BEACON_INTERVAL:
+		/* NB: get from ic_bss for station mode */
+		ireq->i_val = ic->ic_bss->ni_intval;
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+	return error;
+}
+
+static int
+ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	int error;
+	void *ie;
+
+	/*
+	 * NB: Doing this for ap operation could be useful (e.g. for
+	 *     WPA and/or WME) except that it typically is worthless
+	 *     without being able to intervene when processing
+	 *     association response frames--so disallow it for now.
+	 */
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		return EINVAL;
+	if (ireq->i_len > IEEE80211_MAX_OPT_IE)
+		return EINVAL;
+	/* NB: data.length is validated by the wireless extensions code */
+	MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_WAITOK);
+	if (ie == NULL)
+		return ENOMEM;
+	error = copyin(ireq->i_data, ie, ireq->i_len);
+	/* XXX sanity check data? */
+	if (ic->ic_opt_ie != NULL)
+		FREE(ic->ic_opt_ie, M_DEVBUF);
+	ic->ic_opt_ie = ie;
+	ic->ic_opt_ie_len = ireq->i_len;
+	return 0;
+}
+
+static int
+ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_key ik;
+	struct ieee80211_node *ni;
+	struct ieee80211_key *wk;
+	u_int16_t kid;
+	int error;
+
+	if (ireq->i_len != sizeof(ik))
+		return EINVAL;
+	error = copyin(ireq->i_data, &ik, sizeof(ik));
+	if (error)
+		return error;
+	/* NB: cipher support is verified by ieee80211_crypt_newkey */
+	/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
+	if (ik.ik_keylen > sizeof(ik.ik_keydata))
+		return E2BIG;
+	kid = ik.ik_keyix;
+	if (kid == IEEE80211_KEYIX_NONE) {
+		/* XXX unicast keys currently must be tx/rx */
+		if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
+			return EINVAL;
+		if (ic->ic_opmode == IEEE80211_M_STA) {
+			ni = ic->ic_bss;
+			if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid))
+				return EADDRNOTAVAIL;
+		} else {
+			ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+			if (ni == NULL)
+				return ENOENT;
+		}
+		wk = &ni->ni_ucastkey;
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		wk = &ic->ic_nw_keys[kid];
+		ni = NULL;
+		/* XXX auto-add group key flag until applications are updated */
+		if ((ik.ik_flags & IEEE80211_KEY_XMIT) == 0)	/* XXX */
+			ik.ik_flags |= IEEE80211_KEY_GROUP;	/* XXX */
+	}
+	error = 0;
+	ieee80211_key_update_begin(ic);
+	if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
+		wk->wk_keylen = ik.ik_keylen;
+		/* NB: MIC presence is implied by cipher type */
+		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
+			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
+		wk->wk_keyrsc = ik.ik_keyrsc;
+		wk->wk_keytsc = 0;			/* new key, reset */
+		memset(wk->wk_key, 0, sizeof(wk->wk_key));
+		memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
+		if (!ieee80211_crypto_setkey(ic, wk,
+		    ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
+			error = EIO;
+		else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
+			ic->ic_def_txkey = kid;
+	} else
+		error = ENXIO;
+	ieee80211_key_update_end(ic);
+	if (ni != NULL)
+		ieee80211_free_node(ni);
+	return error;
+}
+
+static int
+ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_del_key dk;
+	int kid, error;
+
+	if (ireq->i_len != sizeof(dk))
+		return EINVAL;
+	error = copyin(ireq->i_data, &dk, sizeof(dk));
+	if (error)
+		return error;
+	kid = dk.idk_keyix;
+	/* XXX u_int8_t -> u_int16_t */
+	if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) {
+		struct ieee80211_node *ni;
+
+		ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
+		if (ni == NULL)
+			return EINVAL;		/* XXX */
+		/* XXX error return */
+		ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+		ieee80211_free_node(ni);
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		/* XXX error return */
+		ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+	}
+	return 0;
+}
+
+static void
+domlme(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211req_mlme *mlme = arg;
+
+	if (ni->ni_associd != 0) {
+		IEEE80211_SEND_MGMT(ic, ni,
+			mlme->im_op == IEEE80211_MLME_DEAUTH ?
+				IEEE80211_FC0_SUBTYPE_DEAUTH :
+				IEEE80211_FC0_SUBTYPE_DISASSOC,
+			mlme->im_reason);
+	}
+	ieee80211_node_leave(ic, ni);
+}
+
+static int
+ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_mlme mlme;
+	struct ieee80211_node *ni;
+	int error;
+
+	if (ireq->i_len != sizeof(mlme))
+		return EINVAL;
+	error = copyin(ireq->i_data, &mlme, sizeof(mlme));
+	if (error)
+		return error;
+	switch (mlme.im_op) {
+	case IEEE80211_MLME_ASSOC:
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			return EINVAL;
+		/* XXX must be in S_SCAN state? */
+
+		if (ic->ic_des_esslen != 0) {
+			/*
+			 * Desired ssid specified; must match both bssid and
+			 * ssid to distinguish ap advertising multiple ssid's.
+			 */
+			ni = ieee80211_find_node_with_ssid(&ic->ic_scan,
+				mlme.im_macaddr,
+				ic->ic_des_esslen, ic->ic_des_essid);
+		} else {
+			/*
+			 * Normal case; just match bssid.
+			 */
+			ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr);
+		}
+		if (ni == NULL)
+			return EINVAL;
+		if (!ieee80211_sta_join(ic, ni)) {
+			ieee80211_free_node(ni);
+			return EINVAL;
+		}
+		break;
+	case IEEE80211_MLME_DISASSOC:
+	case IEEE80211_MLME_DEAUTH:
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			/* XXX not quite right */
+			ieee80211_new_state(ic, IEEE80211_S_INIT,
+				mlme.im_reason);
 			break;
-		case IEEE80211_IOC_CHANNEL:
-			switch (ic->ic_state) {
-			case IEEE80211_S_INIT:
-			case IEEE80211_S_SCAN:
-				if (ic->ic_opmode == IEEE80211_M_STA)
-					chan = ic->ic_des_chan;
-				else
-					chan = ic->ic_ibss_chan;
-				break;
-			default:
-				chan = ic->ic_bss->ni_chan;
-				break;
+		case IEEE80211_M_HOSTAP:
+			/* NB: the broadcast address means do 'em all */
+			if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
+				if ((ni = ieee80211_find_node(&ic->ic_sta,
+						mlme.im_macaddr)) == NULL)
+					return EINVAL;
+				domlme(&mlme, ni);
+				ieee80211_free_node(ni);
+			} else {
+				ieee80211_iterate_nodes(&ic->ic_sta,
+						domlme, &mlme);
 			}
-			ireq->i_val = ieee80211_chan2ieee(ic, chan);
 			break;
-		case IEEE80211_IOC_POWERSAVE:
-			if (ic->ic_flags & IEEE80211_F_PMGTON)
-				ireq->i_val = IEEE80211_POWERSAVE_ON;
-			else
-				ireq->i_val = IEEE80211_POWERSAVE_OFF;
+		default:
+			return EINVAL;
+		}
+		break;
+	case IEEE80211_MLME_AUTHORIZE:
+	case IEEE80211_MLME_UNAUTHORIZE:
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+			return EINVAL;
+		ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
+		if (ni == NULL)
+			return EINVAL;
+		if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
+			ieee80211_node_authorize(ic, ni);
+		else
+			ieee80211_node_unauthorize(ic, ni);
+		ieee80211_free_node(ni);
+		break;
+	default:
+		return EINVAL;
+	}
+	return 0;
+}
+
+static int
+ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	u_int8_t mac[IEEE80211_ADDR_LEN];
+	const struct ieee80211_aclator *acl = ic->ic_acl;
+	int error;
+
+	if (ireq->i_len != sizeof(mac))
+		return EINVAL;
+	error = copyin(ireq->i_data, mac, ireq->i_len);
+	if (error)
+		return error;
+	if (acl == NULL) {
+		acl = ieee80211_aclator_get("mac");
+		if (acl == NULL || !acl->iac_attach(ic))
+			return EINVAL;
+		ic->ic_acl = acl;
+	}
+	if (ireq->i_type == IEEE80211_IOC_ADDMAC)
+		acl->iac_add(ic, mac);
+	else
+		acl->iac_remove(ic, mac);
+	return 0;
+}
+
+static int
+ieee80211_ioctl_maccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	const struct ieee80211_aclator *acl = ic->ic_acl;
+
+	switch (ireq->i_val) {
+	case IEEE80211_MACCMD_POLICY_OPEN:
+	case IEEE80211_MACCMD_POLICY_ALLOW:
+	case IEEE80211_MACCMD_POLICY_DENY:
+		if (acl == NULL) {
+			acl = ieee80211_aclator_get("mac");
+			if (acl == NULL || !acl->iac_attach(ic))
+				return EINVAL;
+			ic->ic_acl = acl;
+		}
+		acl->iac_setpolicy(ic, ireq->i_val);
+		break;
+	case IEEE80211_MACCMD_FLUSH:
+		if (acl != NULL)
+			acl->iac_flush(ic);
+		/* NB: silently ignore when not in use */
+		break;
+	case IEEE80211_MACCMD_DETACH:
+		if (acl != NULL) {
+			ic->ic_acl = NULL;
+			acl->iac_detach(ic);
+		}
+		break;
+	default:
+		return EINVAL;
+	}
+	return 0;
+}
+
+static int
+ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_chanlist list;
+	u_char chanlist[IEEE80211_CHAN_BYTES];
+	int i, j, error;
+
+	if (ireq->i_len != sizeof(list))
+		return EINVAL;
+	error = copyin(ireq->i_data, &list, sizeof(list));
+	if (error)
+		return error;
+	memset(chanlist, 0, sizeof(chanlist));
+	/*
+	 * Since channel 0 is not available for DS, channel 1
+	 * is assigned to LSB on WaveLAN.
+	 */
+	if (ic->ic_phytype == IEEE80211_T_DS)
+		i = 1;
+	else
+		i = 0;
+	for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
+		/*
+		 * NB: silently discard unavailable channels so users
+		 *     can specify 1-255 to get all available channels.
+		 */
+		if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i))
+			setbit(chanlist, i);
+	}
+	if (ic->ic_ibss_chan == NULL ||
+	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
+		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+			if (isset(chanlist, i)) {
+				ic->ic_ibss_chan = &ic->ic_channels[i];
+				goto found;
+			}
+		return EINVAL;			/* no active channels */
+found:
+		;
+	}
+	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
+	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
+	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
+		ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+	return IS_UP_AUTO(ic) ? ENETRESET : 0;
+}
+
+static int
+ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_sta_txpow txpow;
+	int error;
+
+	if (ireq->i_len != sizeof(txpow))
+		return EINVAL;
+	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	ni->ni_txpower = txpow.it_txpow;
+	ieee80211_free_node(ni);
+	return error;
+}
+
+static int
+ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	struct wmeParams *wmep, *chanp;
+	int isbss, ac;
+
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		return EINVAL;
+
+	isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
+	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+	if (ac >= WME_NUM_AC)
+		ac = WME_AC_BE;
+	if (isbss) {
+		chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+	} else {
+		chanp = &wme->wme_chanParams.cap_wmeParams[ac];
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+	}
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+		if (isbss) {
+			wmep->wmep_logcwmin = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_logcwmin = ireq->i_val;
+		} else {
+			wmep->wmep_logcwmin = chanp->wmep_logcwmin =
+				ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+		if (isbss) {
+			wmep->wmep_logcwmax = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_logcwmax = ireq->i_val;
+		} else {
+			wmep->wmep_logcwmax = chanp->wmep_logcwmax =
+				ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+		if (isbss) {
+			wmep->wmep_aifsn = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_aifsn = ireq->i_val;
+		} else {
+			wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+		if (isbss) {
+			wmep->wmep_txopLimit = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_txopLimit = ireq->i_val;
+		} else {
+			wmep->wmep_txopLimit = chanp->wmep_txopLimit =
+				ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+		wmep->wmep_acm = ireq->i_val;
+		if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+			chanp->wmep_acm = ireq->i_val;
+		break;
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
+		wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
+			(ireq->i_val) == 0;
+		break;
+	}
+	ieee80211_wme_updateparams(ic);
+	return 0;
+}
+
+static int
+cipher2cap(int cipher)
+{
+	switch (cipher) {
+	case IEEE80211_CIPHER_WEP:	return IEEE80211_C_WEP;
+	case IEEE80211_CIPHER_AES_OCB:	return IEEE80211_C_AES;
+	case IEEE80211_CIPHER_AES_CCM:	return IEEE80211_C_AES_CCM;
+	case IEEE80211_CIPHER_CKIP:	return IEEE80211_C_CKIP;
+	case IEEE80211_CIPHER_TKIP:	return IEEE80211_C_TKIP;
+	}
+	return 0;
+}
+
+static int
+ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+{
+	static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	int error;
+	const struct ieee80211_authenticator *auth;
+	u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
+	char tmpssid[IEEE80211_NWID_LEN];
+	u_int8_t tmpbssid[IEEE80211_ADDR_LEN];
+	struct ieee80211_key *k;
+	int j, caps;
+	u_int kid;
+
+	error = 0;
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_SSID:
+		if (ireq->i_val != 0 ||
+		    ireq->i_len > IEEE80211_NWID_LEN)
+			return EINVAL;
+		error = copyin(ireq->i_data, tmpssid, ireq->i_len);
+		if (error)
 			break;
-		case IEEE80211_IOC_POWERSAVESLEEP:
-			ireq->i_val = ic->ic_lintval;
+		memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
+		ic->ic_des_esslen = ireq->i_len;
+		memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_WEP:
+		switch (ireq->i_val) {
+		case IEEE80211_WEP_OFF:
+			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+			break;
+		case IEEE80211_WEP_ON:
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			ic->ic_flags |= IEEE80211_F_DROPUNENC;
+			break;
+		case IEEE80211_WEP_MIXED:
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
 			break;
-		case IEEE80211_IOC_RTSTHRESHOLD:
-			ireq->i_val = ic->ic_rtsthreshold;
+		}
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_WEPKEY:
+		kid = (u_int) ireq->i_val;
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		k = &ic->ic_nw_keys[kid];
+		if (ireq->i_len == 0) {
+			/* zero-len =>'s delete any existing key */
+			(void) ieee80211_crypto_delkey(ic, k);
 			break;
-		case IEEE80211_IOC_PROTMODE:
-			ireq->i_val = ic->ic_protmode;
+		}
+		if (ireq->i_len > sizeof(tmpkey))
+			return EINVAL;
+		memset(tmpkey, 0, sizeof(tmpkey));
+		error = copyin(ireq->i_data, tmpkey, ireq->i_len);
+		if (error)
 			break;
-		case IEEE80211_IOC_TXPOWER:
-			if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+		ieee80211_key_update_begin(ic);
+		k->wk_keyix = kid;	/* NB: force fixed key id */
+		if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
+		    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
+			k->wk_keylen = ireq->i_len;
+			memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
+			if  (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
 				error = EINVAL;
-			else
-				ireq->i_val = ic->ic_txpower;
-			break;
-		default:
+		} else
 			error = EINVAL;
+		ieee80211_key_update_end(ic);
+		break;
+	case IEEE80211_IOC_WEPTXKEY:
+		kid = (u_int) ireq->i_val;
+		if (kid >= IEEE80211_WEP_NKID &&
+		    (u_int16_t) kid != IEEE80211_KEYIX_NONE)
+			return EINVAL;
+		ic->ic_def_txkey = kid;
+		error = ENETRESET;	/* push to hardware */
+		break;
+	case IEEE80211_IOC_AUTHMODE:
+		switch (ireq->i_val) {
+		case IEEE80211_AUTH_WPA:
+		case IEEE80211_AUTH_8021X:	/* 802.1x */
+		case IEEE80211_AUTH_OPEN:	/* open */
+		case IEEE80211_AUTH_SHARED:	/* shared-key */
+		case IEEE80211_AUTH_AUTO:	/* auto */
+			auth = ieee80211_authenticator_get(ireq->i_val);
+			if (auth == NULL)
+				return EINVAL;
 			break;
+		default:
+			return EINVAL;
 		}
+		switch (ireq->i_val) {
+		case IEEE80211_AUTH_WPA:	/* WPA w/ 802.1x */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			ireq->i_val = IEEE80211_AUTH_8021X;
+			break;
+		case IEEE80211_AUTH_OPEN:	/* open */
+			ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
+			break;
+		case IEEE80211_AUTH_SHARED:	/* shared-key */
+		case IEEE80211_AUTH_8021X:	/* 802.1x */
+			ic->ic_flags &= ~IEEE80211_F_WPA;
+			/* both require a key so mark the PRIVACY capability */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			break;
+		case IEEE80211_AUTH_AUTO:	/* auto */
+			ic->ic_flags &= ~IEEE80211_F_WPA;
+			/* XXX PRIVACY handling? */
+			/* XXX what's the right way to do this? */
+			break;
+		}
+		/* NB: authenticator attach/detach happens on state change */
+		ic->ic_bss->ni_authmode = ireq->i_val;
+		/* XXX mixed/mode/usage? */
+		ic->ic_auth = auth;
+		error = ENETRESET;
 		break;
-	case SIOCS80211:
-		error = suser_cred(cr, NULL_CRED_OKAY);
-		if (error)
-			break;
-		ireq = (struct ieee80211req *) data;
-		switch (ireq->i_type) {
-		case IEEE80211_IOC_SSID:
-			if (ireq->i_val != 0 ||
-			    ireq->i_len > IEEE80211_NWID_LEN) {
-				error = EINVAL;
-				break;
-			}
-			error = copyin(ireq->i_data, tmpssid, ireq->i_len);
-			if (error)
-				break;
-			memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
-			ic->ic_des_esslen = ireq->i_len;
-			memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
+	case IEEE80211_IOC_CHANNEL:
+		/* XXX 0xffff overflows 16-bit signed */
+		if (ireq->i_val == 0 ||
+		    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
+			ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+		else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
+		    isclr(ic->ic_chan_active, ireq->i_val)) {
+			return EINVAL;
+		} else
+			ic->ic_ibss_chan = ic->ic_des_chan =
+				&ic->ic_channels[ireq->i_val];
+		switch (ic->ic_state) {
+		case IEEE80211_S_INIT:
+		case IEEE80211_S_SCAN:
 			error = ENETRESET;
 			break;
-		case IEEE80211_IOC_WEP:
+		default:
 			/*
-			 * These cards only support one mode so
-			 * we just turn wep on if what ever is
-			 * passed in is not OFF.
+			 * If the desired channel has changed (to something
+			 * other than any) and we're not already scanning,
+			 * then kick the state machine.
 			 */
-			if (ireq->i_val == IEEE80211_WEP_OFF) {
-				ic->ic_flags &= ~IEEE80211_F_WEPON;
-			} else {
-				ic->ic_flags |= IEEE80211_F_WEPON;
-			}
-			error = ENETRESET;
-			break;
-		case IEEE80211_IOC_WEPKEY:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
-				error = EINVAL;
-				break;
-			}
-			kid = (u_int) ireq->i_val;
-			if (kid >= IEEE80211_WEP_NKID) {
-				error = EINVAL;
-				break;
-			} 
-			if (ireq->i_len > sizeof(tmpkey)) {
-				error = EINVAL;
-				break;
-			}
-			memset(tmpkey, 0, sizeof(tmpkey));
-			error = copyin(ireq->i_data, tmpkey, ireq->i_len);
-			if (error)
-				break;
-			memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
-				sizeof(tmpkey));
-			ic->ic_nw_keys[kid].wk_len = ireq->i_len;
-			error = ENETRESET;
+			if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+			    ic->ic_bss->ni_chan != ic->ic_des_chan &&
+			    (ic->ic_flags & IEEE80211_F_SCAN) == 0)
+				error = ENETRESET;
 			break;
-		case IEEE80211_IOC_WEPTXKEY:
-			kid = (u_int) ireq->i_val;
-			if (kid >= IEEE80211_WEP_NKID) {
-				error = EINVAL;
-				break;
+		}
+		if (error == ENETRESET && ic->ic_opmode == IEEE80211_M_MONITOR)
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_POWERSAVE:
+		switch (ireq->i_val) {
+		case IEEE80211_POWERSAVE_OFF:
+			if (ic->ic_flags & IEEE80211_F_PMGTON) {
+				ic->ic_flags &= ~IEEE80211_F_PMGTON;
+				error = ENETRESET;
 			}
-			ic->ic_wep_txkey = kid;
-			error = ENETRESET;
-			break;
-#if 0
-		case IEEE80211_IOC_AUTHMODE:
-			sc->wi_authmode = ireq->i_val;
 			break;
-#endif
-		case IEEE80211_IOC_CHANNEL:
-			/* XXX 0xffff overflows 16-bit signed */
-			if (ireq->i_val == 0 ||
-			    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
-				ic->ic_des_chan = IEEE80211_CHAN_ANYC;
-			else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
-			    isclr(ic->ic_chan_active, ireq->i_val)) {
+		case IEEE80211_POWERSAVE_ON:
+			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
 				error = EINVAL;
-				break;
-			} else
-				ic->ic_ibss_chan = ic->ic_des_chan =
-					&ic->ic_channels[ireq->i_val];
-			switch (ic->ic_state) {
-			case IEEE80211_S_INIT:
-			case IEEE80211_S_SCAN:
+			else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
+				ic->ic_flags |= IEEE80211_F_PMGTON;
 				error = ENETRESET;
-				break;
-			default:
-				if (ic->ic_opmode == IEEE80211_M_STA) {
-					if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
-					    ic->ic_bss->ni_chan != ic->ic_des_chan)
-						error = ENETRESET;
-				} else {
-					if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
-						error = ENETRESET;
-				}
-				break;
 			}
 			break;
-		case IEEE80211_IOC_POWERSAVE:
-			switch (ireq->i_val) {
-			case IEEE80211_POWERSAVE_OFF:
-				if (ic->ic_flags & IEEE80211_F_PMGTON) {
-					ic->ic_flags &= ~IEEE80211_F_PMGTON;
-					error = ENETRESET;
-				}
-				break;
-			case IEEE80211_POWERSAVE_ON:
-				if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
-					error = EINVAL;
-				else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
-					ic->ic_flags |= IEEE80211_F_PMGTON;
-					error = ENETRESET;
-				}
-				break;
-			default:
-				error = EINVAL;
-				break;
-			}
+		default:
+			error = EINVAL;
 			break;
-		case IEEE80211_IOC_POWERSAVESLEEP:
-			if (ireq->i_val < 0) {
-				error = EINVAL;
-				break;
-			}
-			ic->ic_lintval = ireq->i_val;
-			error = ENETRESET;
+		}
+		break;
+	case IEEE80211_IOC_POWERSAVESLEEP:
+		if (ireq->i_val < 0)
+			return EINVAL;
+		ic->ic_lintval = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_RTSTHRESHOLD:
+		if (!(IEEE80211_RTS_MIN < ireq->i_val &&
+		      ireq->i_val < IEEE80211_RTS_MAX))
+			return EINVAL;
+		ic->ic_rtsthreshold = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_PROTMODE:
+		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
+			return EINVAL;
+		ic->ic_protmode = ireq->i_val;
+		/* NB: if not operating in 11g this can wait */
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_TXPOWER:
+		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+			return EINVAL;
+		if (!(IEEE80211_TXPOWER_MIN < ireq->i_val &&
+		      ireq->i_val < IEEE80211_TXPOWER_MAX))
+			return EINVAL;
+		ic->ic_txpowlimit = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_ROAMING:
+		if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
+		    ireq->i_val <= IEEE80211_ROAMING_MANUAL))
+			return EINVAL;
+		ic->ic_roaming = ireq->i_val;
+		/* XXXX reset? */
+		break;
+	case IEEE80211_IOC_PRIVACY:
+		if (ireq->i_val) {
+			/* XXX check for key state? */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+		break;
+	case IEEE80211_IOC_DROPUNENCRYPTED:
+		if (ireq->i_val)
+			ic->ic_flags |= IEEE80211_F_DROPUNENC;
+		else
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+		break;
+	case IEEE80211_IOC_WPAKEY:
+		error = ieee80211_ioctl_setkey(ic, ireq);
+		break;
+	case IEEE80211_IOC_DELKEY:
+		error = ieee80211_ioctl_delkey(ic, ireq);
+		break;
+	case IEEE80211_IOC_MLME:
+		error = ieee80211_ioctl_setmlme(ic, ireq);
+		break;
+	case IEEE80211_IOC_OPTIE:
+		error = ieee80211_ioctl_setoptie(ic, ireq);
+		break;
+	case IEEE80211_IOC_COUNTERMEASURES:
+		if (ireq->i_val) {
+			if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
+				return EINVAL;
+			ic->ic_flags |= IEEE80211_F_COUNTERM;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+		break;
+	case IEEE80211_IOC_WPA:
+		if (ireq->i_val > 3)
+			return EINVAL;
+		/* XXX verify ciphers available */
+		ic->ic_flags &= ~IEEE80211_F_WPA;
+		switch (ireq->i_val) {
+		case 1:
+			ic->ic_flags |= IEEE80211_F_WPA1;
 			break;
-		case IEEE80211_IOC_RTSTHRESHOLD:
-			if (!(IEEE80211_RTS_MIN < ireq->i_val &&
-			      ireq->i_val < IEEE80211_RTS_MAX)) {
-				error = EINVAL;
-				break;
-			}
-			ic->ic_rtsthreshold = ireq->i_val;
-			error = ENETRESET;
+		case 2:
+			ic->ic_flags |= IEEE80211_F_WPA2;
 			break;
-		case IEEE80211_IOC_PROTMODE:
-			if (ireq->i_val > IEEE80211_PROT_RTSCTS) {
-				error = EINVAL;
-				break;
-			}
-			ic->ic_protmode = ireq->i_val;
-			/* NB: if not operating in 11g this can wait */
-			if (ic->ic_curmode == IEEE80211_MODE_11G)
-				error = ENETRESET;
+		case 3:
+			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
 			break;
-		case IEEE80211_IOC_TXPOWER:
-			if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) {
-				error = EINVAL;
-				break;
-			}
-			if (!(IEEE80211_TXPOWER_MIN < ireq->i_val &&
-			      ireq->i_val < IEEE80211_TXPOWER_MAX)) {
-				error = EINVAL;
-				break;
-			}
-			ic->ic_txpower = ireq->i_val;
-			error = ENETRESET;
+		}
+		error = ENETRESET;		/* XXX? */
+		break;
+	case IEEE80211_IOC_WME:
+		if (ireq->i_val) {
+			if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+				return EINVAL;
+			ic->ic_flags |= IEEE80211_F_WME;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_WME;
+		error = ENETRESET;		/* XXX maybe not for station? */
+		break;
+	case IEEE80211_IOC_HIDESSID:
+		if (ireq->i_val)
+			ic->ic_flags |= IEEE80211_F_HIDESSID;
+		else
+			ic->ic_flags &= ~IEEE80211_F_HIDESSID;
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_APBRIDGE:
+		if (ireq->i_val == 0)
+			ic->ic_flags |= IEEE80211_F_NOBRIDGE;
+		else
+			ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
+		break;
+	case IEEE80211_IOC_MCASTCIPHER:
+		if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
+		    !ieee80211_crypto_available(ireq->i_val))
+			return EINVAL;
+		rsn->rsn_mcastcipher = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_MCASTKEYLEN:
+		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
+			return EINVAL;
+		/* XXX no way to verify driver capability */
+		rsn->rsn_mcastkeylen = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_UCASTCIPHERS:
+		/*
+		 * Convert user-specified cipher set to the set
+		 * we can support (via hardware or software).
+		 * NB: this logic intentionally ignores unknown and
+		 * unsupported ciphers so folks can specify 0xff or
+		 * similar and get all available ciphers.
+		 */
+		caps = 0;
+		for (j = 1; j < 32; j++)	/* NB: skip WEP */
+			if ((ireq->i_val & (1<<j)) &&
+			    ((ic->ic_caps & cipher2cap(j)) ||
+			     ieee80211_crypto_available(j)))
+				caps |= 1<<j;
+		if (caps == 0)			/* nothing available */
+			return EINVAL;
+		/* XXX verify ciphers ok for unicast use? */
+		/* XXX disallow if running as it'll have no effect */
+		rsn->rsn_ucastcipherset = caps;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_UCASTCIPHER:
+		if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
+			return EINVAL;
+		rsn->rsn_ucastcipher = ireq->i_val;
+		break;
+	case IEEE80211_IOC_UCASTKEYLEN:
+		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
+			return EINVAL;
+		/* XXX no way to verify driver capability */
+		rsn->rsn_ucastkeylen = ireq->i_val;
+		break;
+	case IEEE80211_IOC_DRIVER_CAPS:
+		/* NB: for testing */
+		ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) |
+			       ((u_int16_t) ireq->i_len);
+		break;
+	case IEEE80211_IOC_KEYMGTALGS:
+		/* XXX check */
+		rsn->rsn_keymgmtset = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_RSNCAPS:
+		/* XXX check */
+		rsn->rsn_caps = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_BSSID:
+		/* NB: should only be set when in STA mode */
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			return EINVAL;
+		if (ireq->i_len != sizeof(tmpbssid))
+			return EINVAL;
+		error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
+		if (error)
 			break;
-		default:
-			error = EINVAL;
+		IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
+		if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
+			ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+		else
+			ic->ic_flags |= IEEE80211_F_DESBSSID;
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_CHANLIST:
+		error = ieee80211_ioctl_setchanlist(ic, ireq);
+		break;
+	case IEEE80211_IOC_SCAN_REQ:
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP)	/* XXX ignore */
 			break;
-		}
+		error = ieee80211_setupscan(ic, ic->ic_chan_avail);
+		if (error == 0)		/* XXX background scan */
+			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+		break;
+	case IEEE80211_IOC_ADDMAC:
+	case IEEE80211_IOC_DELMAC:
+		error = ieee80211_ioctl_macmac(ic, ireq);
+		break;
+	case IEEE80211_IOC_MACCMD:
+		error = ieee80211_ioctl_maccmd(ic, ireq);
+		break;
+	case IEEE80211_IOC_STA_TXPOW:
+		error = ieee80211_ioctl_setstatxpow(ic, ireq);
+		break;
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
+		error = ieee80211_ioctl_setwmeparam(ic, ireq);
+		break;
+	case IEEE80211_IOC_DTIM_PERIOD:
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+		    ic->ic_opmode != IEEE80211_M_IBSS)
+			return EINVAL;
+		if (IEEE80211_DTIM_MIN <= ireq->i_val &&
+		    ireq->i_val <= IEEE80211_DTIM_MAX) {
+			ic->ic_dtim_period = ireq->i_val;
+			error = ENETRESET;		/* requires restart */
+		} else
+			error = EINVAL;
+		break;
+	case IEEE80211_IOC_BEACON_INTERVAL:
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+		    ic->ic_opmode != IEEE80211_M_IBSS)
+			return EINVAL;
+		if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
+		    ireq->i_val <= IEEE80211_BINTVAL_MAX) {
+			ic->ic_lintval = ireq->i_val;
+			error = ENETRESET;		/* requires restart */
+		} else
+			error = EINVAL;
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+	if (error == ENETRESET && !IS_UP_AUTO(ic))
+		error = 0;
+	return error;
+}
+
+int
+ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data,  struct ucred *cr)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	int error = 0;
+	struct ifreq *ifr;
+	struct ifaddr *ifa;			/* XXX */
+
+	switch (cmd) {
+	case SIOCSIFMEDIA:
+	case SIOCGIFMEDIA:
+		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
+				&ic->ic_media, cmd);
+		break;
+	case SIOCG80211:
+		error = ieee80211_ioctl_get80211(ic, cmd,
+				(struct ieee80211req *) data, cr);
+		break;
+	case SIOCS80211:
+		error = suser_cred(cr, NULL_CRED_OKAY);
+ 
+		if (error == 0)
+			error = ieee80211_ioctl_set80211(ic, cmd,
+					(struct ieee80211req *) data);
 		break;
 	case SIOCGIFGENERIC:
-		error = ieee80211_cfgget(ifp, cmd, data, cr);
+		error = ieee80211_cfgget(ic, cmd, data, cr);
 		break;
 	case SIOCSIFGENERIC:
 		error = suser_cred(cr, NULL_CRED_OKAY);
 		if (error)
 			break;
-		error = ieee80211_cfgset(ifp, cmd, data);
+		error = ieee80211_cfgset(ic, cmd, data);
 		break;
 	case SIOCG80211STATS:
 		ifr = (struct ifreq *)data;
Index: src/sys/netproto/802_11/ieee80211_ioctl.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_ioctl.h,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_ioctl.h
--- src/sys/netproto/802_11/ieee80211_ioctl.h	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_ioctl.h	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,68 +29,326 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.5 2004/03/30 22:57:57 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_ioctl.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_IOCTL_H_
-#define	_NETPROTO_802_11_IEEE80211_IOCTL_H_
+#ifndef _NET80211_IEEE80211_IOCTL_H_
+#define _NET80211_IEEE80211_IOCTL_H_
 
 /*
  * IEEE 802.11 ioctls.
  */
+#include <netproto/802_11/_ieee80211.h>
+#include <netproto/802_11/ieee80211.h>
+#include <netproto/802_11/ieee80211_crypto.h>
+
+/*
+ * Per/node (station) statistics available when operating as an AP.
+ */
+struct ieee80211_nodestats {
+	u_int32_t	ns_rx_data;		/* rx data frames */
+	u_int32_t	ns_rx_mgmt;		/* rx management frames */
+	u_int32_t	ns_rx_ctrl;		/* rx control frames */
+	u_int32_t	ns_rx_ucast;		/* rx unicast frames */
+	u_int32_t	ns_rx_mcast;		/* rx multi/broadcast frames */
+	u_int64_t	ns_rx_bytes;		/* rx data count (bytes) */
+	u_int64_t	ns_rx_beacons;		/* rx beacon frames */
+	u_int32_t	ns_rx_proberesp;	/* rx probe response frames */
+
+	u_int32_t	ns_rx_dup;		/* rx discard 'cuz dup */
+	u_int32_t	ns_rx_noprivacy;	/* rx w/ wep but privacy off */
+	u_int32_t	ns_rx_wepfail;		/* rx wep processing failed */
+	u_int32_t	ns_rx_demicfail;	/* rx demic failed */
+	u_int32_t	ns_rx_decap;		/* rx decapsulation failed */
+	u_int32_t	ns_rx_defrag;		/* rx defragmentation failed */
+	u_int32_t	ns_rx_disassoc;		/* rx disassociation */
+	u_int32_t	ns_rx_deauth;		/* rx deauthentication */
+	u_int32_t	ns_rx_decryptcrc;	/* rx decrypt failed on crc */
+	u_int32_t	ns_rx_unauth;		/* rx on unauthorized port */
+	u_int32_t	ns_rx_unencrypted;	/* rx unecrypted w/ privacy */
+
+	u_int32_t	ns_tx_data;		/* tx data frames */
+	u_int32_t	ns_tx_mgmt;		/* tx management frames */
+	u_int32_t	ns_tx_ucast;		/* tx unicast frames */
+	u_int32_t	ns_tx_mcast;		/* tx multi/broadcast frames */
+	u_int64_t	ns_tx_bytes;		/* tx data count (bytes) */
+	u_int32_t	ns_tx_probereq;		/* tx probe request frames */
+
+	u_int32_t	ns_tx_novlantag;	/* tx discard 'cuz no tag */
+	u_int32_t	ns_tx_vlanmismatch;	/* tx discard 'cuz bad tag */
+
+	u_int32_t	ns_ps_discard;		/* ps discard 'cuz of age */
 
+	/* MIB-related state */
+	u_int32_t	ns_tx_assoc;		/* [re]associations */
+	u_int32_t	ns_tx_assoc_fail;	/* [re]association failures */
+	u_int32_t	ns_tx_auth;		/* [re]authentications */
+	u_int32_t	ns_tx_auth_fail;	/* [re]authentication failures*/
+	u_int32_t	ns_tx_deauth;		/* deauthentications */
+	u_int32_t	ns_tx_deauth_code;	/* last deauth reason */
+	u_int32_t	ns_tx_disassoc;		/* disassociations */
+	u_int32_t	ns_tx_disassoc_code;	/* last disassociation reason */
+};
+
+/*
+ * Summary statistics.
+ */
 struct ieee80211_stats {
-	uint32_t	is_rx_badversion;	/* rx frame with bad version */
-	uint32_t	is_rx_tooshort;		/* rx frame too short */
-	uint32_t	is_rx_wrongbss;		/* rx from wrong bssid */
-	uint32_t	is_rx_dup;		/* rx discard 'cuz dup */
-	uint32_t	is_rx_wrongdir;		/* rx w/ wrong direction */
-	uint32_t	is_rx_mcastecho;	/* rx discard 'cuz mcast echo */
-	uint32_t	is_rx_notassoc;		/* rx discard 'cuz sta !assoc */
-	uint32_t	is_rx_nowep;		/* rx w/ wep but wep !config */
-	uint32_t	is_rx_wepfail;		/* rx wep processing failed */
-	uint32_t	is_rx_decap;		/* rx decapsulation failed */
-	uint32_t	is_rx_mgtdiscard;	/* rx discard mgt frames */
-	uint32_t	is_rx_ctl;		/* rx discard ctrl frames */
-	uint32_t	is_rx_rstoobig;		/* rx rate set truncated */
-	uint32_t	is_rx_elem_missing;	/* rx required element missing*/
-	uint32_t	is_rx_elem_toobig;	/* rx element too big */
-	uint32_t	is_rx_elem_toosmall;	/* rx element too small */
-	uint32_t	is_rx_elem_unknown;	/* rx element unknown */
-	uint32_t	is_rx_badchan;		/* rx frame w/ invalid chan */
-	uint32_t	is_rx_chanmismatch;	/* rx frame chan mismatch */
-	uint32_t	is_rx_nodealloc;	/* rx frame dropped */
-	uint32_t	is_rx_ssidmismatch;	/* rx frame ssid mismatch  */
-	uint32_t	is_rx_auth_unsupported;	/* rx w/ unsupported auth alg */
-	uint32_t	is_rx_auth_fail;	/* rx sta auth failure */
-	uint32_t	is_rx_assoc_bss;	/* rx assoc from wrong bssid */
-	uint32_t	is_rx_assoc_notauth;	/* rx assoc w/o auth */
-	uint32_t	is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
-	uint32_t	is_rx_assoc_norate;	/* rx assoc w/ no rate match */
-	uint32_t	is_rx_deauth;		/* rx deauthentication */
-	uint32_t	is_rx_disassoc;		/* rx disassociation */
-	uint32_t	is_rx_badsubtype;	/* rx frame w/ unknown subtype*/
-	uint32_t	is_rx_nombuf;		/* rx failed for lack of mbuf */
-	uint32_t	is_rx_decryptcrc;	/* rx decrypt failed on crc */
-	uint32_t	is_rx_ahdemo_mgt;	/* rx discard ahdemo mgt frame*/
-	uint32_t	is_rx_bad_auth;		/* rx bad auth request */
-	uint32_t	is_tx_nombuf;		/* tx failed for lack of mbuf */
-	uint32_t	is_tx_nonode;		/* tx failed for no node */
-	uint32_t	is_tx_unknownmgt;	/* tx of unknown mgt frame */
-	uint32_t	is_scan_active;		/* active scans started */
-	uint32_t	is_scan_passive;	/* passive scans started */
-	uint32_t	is_node_timeout;	/* nodes timed out inactivity */
-	uint32_t	is_crypto_nomem;	/* no memory for crypto ctx */
+	u_int32_t	is_rx_badversion;	/* rx frame with bad version */
+	u_int32_t	is_rx_tooshort;		/* rx frame too short */
+	u_int32_t	is_rx_wrongbss;		/* rx from wrong bssid */
+	u_int32_t	is_rx_dup;		/* rx discard 'cuz dup */
+	u_int32_t	is_rx_wrongdir;		/* rx w/ wrong direction */
+	u_int32_t	is_rx_mcastecho;	/* rx discard 'cuz mcast echo */
+	u_int32_t	is_rx_notassoc;		/* rx discard 'cuz sta !assoc */
+	u_int32_t	is_rx_noprivacy;	/* rx w/ wep but privacy off */
+	u_int32_t	is_rx_unencrypted;	/* rx w/o wep and privacy on */
+	u_int32_t	is_rx_wepfail;		/* rx wep processing failed */
+	u_int32_t	is_rx_decap;		/* rx decapsulation failed */
+	u_int32_t	is_rx_mgtdiscard;	/* rx discard mgt frames */
+	u_int32_t	is_rx_ctl;		/* rx discard ctrl frames */
+	u_int32_t	is_rx_beacon;		/* rx beacon frames */
+	u_int32_t	is_rx_rstoobig;		/* rx rate set truncated */
+	u_int32_t	is_rx_elem_missing;	/* rx required element missing*/
+	u_int32_t	is_rx_elem_toobig;	/* rx element too big */
+	u_int32_t	is_rx_elem_toosmall;	/* rx element too small */
+	u_int32_t	is_rx_elem_unknown;	/* rx element unknown */
+	u_int32_t	is_rx_badchan;		/* rx frame w/ invalid chan */
+	u_int32_t	is_rx_chanmismatch;	/* rx frame chan mismatch */
+	u_int32_t	is_rx_nodealloc;	/* rx frame dropped */
+	u_int32_t	is_rx_ssidmismatch;	/* rx frame ssid mismatch  */
+	u_int32_t	is_rx_auth_unsupported;	/* rx w/ unsupported auth alg */
+	u_int32_t	is_rx_auth_fail;	/* rx sta auth failure */
+	u_int32_t	is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */
+	u_int32_t	is_rx_assoc_bss;	/* rx assoc from wrong bssid */
+	u_int32_t	is_rx_assoc_notauth;	/* rx assoc w/o auth */
+	u_int32_t	is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
+	u_int32_t	is_rx_assoc_norate;	/* rx assoc w/ no rate match */
+	u_int32_t	is_rx_assoc_badwpaie;	/* rx assoc w/ bad WPA IE */
+	u_int32_t	is_rx_deauth;		/* rx deauthentication */
+	u_int32_t	is_rx_disassoc;		/* rx disassociation */
+	u_int32_t	is_rx_badsubtype;	/* rx frame w/ unknown subtype*/
+	u_int32_t	is_rx_nobuf;		/* rx failed for lack of buf */
+	u_int32_t	is_rx_decryptcrc;	/* rx decrypt failed on crc */
+	u_int32_t	is_rx_ahdemo_mgt;	/* rx discard ahdemo mgt frame*/
+	u_int32_t	is_rx_bad_auth;		/* rx bad auth request */
+	u_int32_t	is_rx_unauth;		/* rx on unauthorized port */
+	u_int32_t	is_rx_badkeyid;		/* rx w/ incorrect keyid */
+	u_int32_t	is_rx_ccmpreplay;	/* rx seq# violation (CCMP) */
+	u_int32_t	is_rx_ccmpformat;	/* rx format bad (CCMP) */
+	u_int32_t	is_rx_ccmpmic;		/* rx MIC check failed (CCMP) */
+	u_int32_t	is_rx_tkipreplay;	/* rx seq# violation (TKIP) */
+	u_int32_t	is_rx_tkipformat;	/* rx format bad (TKIP) */
+	u_int32_t	is_rx_tkipmic;		/* rx MIC check failed (TKIP) */
+	u_int32_t	is_rx_tkipicv;		/* rx ICV check failed (TKIP) */
+	u_int32_t	is_rx_badcipher;	/* rx failed 'cuz key type */
+	u_int32_t	is_rx_nocipherctx;	/* rx failed 'cuz key !setup */
+	u_int32_t	is_rx_acl;		/* rx discard 'cuz acl policy */
+	u_int32_t	is_tx_nobuf;		/* tx failed for lack of buf */
+	u_int32_t	is_tx_nonode;		/* tx failed for no node */
+	u_int32_t	is_tx_unknownmgt;	/* tx of unknown mgt frame */
+	u_int32_t	is_tx_badcipher;	/* tx failed 'cuz key type */
+	u_int32_t	is_tx_nodefkey;		/* tx failed 'cuz no defkey */
+	u_int32_t	is_tx_noheadroom;	/* tx failed 'cuz no space */
+	u_int32_t	is_scan_active;		/* active scans started */
+	u_int32_t	is_scan_passive;	/* passive scans started */
+	u_int32_t	is_node_timeout;	/* nodes timed out inactivity */
+	u_int32_t	is_crypto_nomem;	/* no memory for crypto ctx */
+	u_int32_t	is_crypto_tkip;		/* tkip crypto done in s/w */
+	u_int32_t	is_crypto_tkipenmic;	/* tkip en-MIC done in s/w */
+	u_int32_t	is_crypto_tkipdemic;	/* tkip de-MIC done in s/w */
+	u_int32_t	is_crypto_tkipcm;	/* tkip counter measures */
+	u_int32_t	is_crypto_ccmp;		/* ccmp crypto done in s/w */
+	u_int32_t	is_crypto_wep;		/* wep crypto done in s/w */
+	u_int32_t	is_crypto_setkey_cipher;/* cipher rejected key */
+	u_int32_t	is_crypto_setkey_nokey;	/* no key index for setkey */
+	u_int32_t	is_crypto_delkey;	/* driver key delete failed */
+	u_int32_t	is_crypto_badcipher;	/* unknown cipher */
+	u_int32_t	is_crypto_nocipher;	/* cipher not available */
+	u_int32_t	is_crypto_attachfail;	/* cipher attach failed */
+	u_int32_t	is_crypto_swfallback;	/* cipher fallback to s/w */
+	u_int32_t	is_crypto_keyfail;	/* driver key alloc failed */
+	u_int32_t	is_crypto_enmicfail;	/* en-MIC failed */
+	u_int32_t	is_ibss_capmismatch;	/* merge failed-cap mismatch */
+	u_int32_t	is_ibss_norate;		/* merge failed-rate mismatch */
+	u_int32_t	is_ps_unassoc;		/* ps-poll for unassoc. sta */
+	u_int32_t	is_ps_badaid;		/* ps-poll w/ incorrect aid */
+	u_int32_t	is_ps_qempty;		/* ps-poll w/ nothing to send */
+};
+
+/*
+ * Max size of optional information elements.  We artificially
+ * constrain this; it's limited only by the max frame size (and
+ * the max parameter size of the wireless extensions).
+ */
+#define	IEEE80211_MAX_OPT_IE	256
+
+/*
+ * WPA/RSN get/set key request.  Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving.  The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap).  They key length
+ * must include any MIC key data; otherwise it should be no
+ more than IEEE80211_KEYBUF_SIZE.
+ */
+struct ieee80211req_key {
+	u_int8_t	ik_type;	/* key/cipher type */
+	u_int8_t	ik_pad;
+	u_int16_t	ik_keyix;	/* key index */
+	u_int8_t	ik_keylen;	/* key length in bytes */
+	u_int8_t	ik_flags;
+/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */
+#define	IEEE80211_KEY_DEFAULT	0x80	/* default xmit key */
+	u_int8_t	ik_macaddr[IEEE80211_ADDR_LEN];
+	u_int64_t	ik_keyrsc;	/* key receive sequence counter */
+	u_int64_t	ik_keytsc;	/* key transmit sequence counter */
+	u_int8_t	ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+};
+
+/*
+ * Delete a key either by index or address.  Set the index
+ * to IEEE80211_KEYIX_NONE when deleting a unicast key.
+ */
+struct ieee80211req_del_key {
+	u_int8_t	idk_keyix;	/* key index */
+	u_int8_t	idk_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/*
+ * MLME state manipulation request.  IEEE80211_MLME_ASSOC
+ * only makes sense when operating as a station.  The other
+ * requests can be used when operating as a station or an
+ * ap (to effect a station).
+ */
+struct ieee80211req_mlme {
+	u_int8_t	im_op;		/* operation to perform */
+#define	IEEE80211_MLME_ASSOC		1	/* associate station */
+#define	IEEE80211_MLME_DISASSOC		2	/* disassociate station */
+#define	IEEE80211_MLME_DEAUTH		3	/* deauthenticate station */
+#define	IEEE80211_MLME_AUTHORIZE	4	/* authorize station */
+#define	IEEE80211_MLME_UNAUTHORIZE	5	/* unauthorize station */
+	u_int16_t	im_reason;	/* 802.11 reason code */
+	u_int8_t	im_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/* 
+ * MAC ACL operations.
+ */
+enum {
+	IEEE80211_MACCMD_POLICY_OPEN	= 0,	/* set policy: no ACL's */
+	IEEE80211_MACCMD_POLICY_ALLOW	= 1,	/* set policy: allow traffic */
+	IEEE80211_MACCMD_POLICY_DENY	= 2,	/* set policy: deny traffic */
+	IEEE80211_MACCMD_FLUSH		= 3,	/* flush ACL database */
+	IEEE80211_MACCMD_DETACH		= 4,	/* detach ACL policy */
+};
+
+/*
+ * Set the active channel list.  Note this list is
+ * intersected with the available channel list in
+ * calculating the set of channels actually used in
+ * scanning.
+ */
+struct ieee80211req_chanlist {
+	u_int8_t	ic_channels[IEEE80211_CHAN_BYTES];
 };
 
 /*
+ * Get the active channel list info.
+ */
+struct ieee80211req_chaninfo {
+	u_int	ic_nchans;
+	struct ieee80211_channel ic_chans[IEEE80211_CHAN_MAX];
+};
+
+/*
+ * Retrieve the WPA/RSN information element for an associated station.
+ */
+struct ieee80211req_wpaie {
+	u_int8_t	wpa_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	wpa_ie[IEEE80211_MAX_OPT_IE];
+};
+
+/*
+ * Retrieve per-node statistics.
+ */
+struct ieee80211req_sta_stats {
+	union {
+		/* NB: explicitly force 64-bit alignment */
+		u_int8_t	macaddr[IEEE80211_ADDR_LEN];
+		u_int64_t	pad;
+	} is_u;
+	struct ieee80211_nodestats is_stats;
+};
+
+/*
+ * Station information block; the mac address is used
+ * to retrieve other data like stats, unicast key, etc.
+ */
+struct ieee80211req_sta_info {
+	u_int16_t	isi_len;		/* length (mult of 4) */
+	u_int16_t	isi_freq;		/* MHz */
+	u_int16_t	isi_flags;		/* channel flags */
+	u_int16_t	isi_state;		/* state flags */
+	u_int8_t	isi_authmode;		/* authentication algorithm */
+	u_int8_t	isi_rssi;
+	u_int8_t	isi_capinfo;		/* capabilities */
+	u_int8_t	isi_erp;		/* ERP element */
+	u_int8_t	isi_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	isi_nrates;
+						/* negotiated rates */
+	u_int8_t	isi_rates[IEEE80211_RATE_MAXSIZE];
+	u_int8_t	isi_txrate;		/* index to isi_rates[] */
+	u_int16_t	isi_ie_len;		/* IE length */
+	u_int16_t	isi_associd;		/* assoc response */
+	u_int16_t	isi_txpower;		/* current tx power */
+	u_int16_t	isi_vlan;		/* vlan tag */
+	u_int16_t	isi_txseqs[17];		/* seq to be transmitted */
+	u_int16_t	isi_rxseqs[17];		/* seq previous for qos frames*/
+	u_int16_t	isi_inact;		/* inactivity timer */
+	/* XXX frag state? */
+	/* variable length IE data */
+};
+
+/*
+ * Retrieve per-station information; to retrieve all
+ * specify a mac address of ff:ff:ff:ff:ff:ff.
+ */
+struct ieee80211req_sta_req {
+	union {
+		/* NB: explicitly force 64-bit alignment */
+		u_int8_t	macaddr[IEEE80211_ADDR_LEN];
+		u_int64_t	pad;
+	} is_u;
+	struct ieee80211req_sta_info info[1];	/* variable length */
+};
+
+/*
+ * Get/set per-station tx power cap.
+ */
+struct ieee80211req_sta_txpow {
+	u_int8_t	it_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	it_txpow;
+};
+
+/*
+ * WME parameters are set and return using i_val and i_len.
+ * i_val holds the value itself.  i_len specifies the AC
+ * and, as appropriate, then high bit specifies whether the
+ * operation is to be applied to the BSS or ourself.
+ */
+#define	IEEE80211_WMEPARAM_SELF	0x0000		/* parameter applies to self */
+#define	IEEE80211_WMEPARAM_BSS	0x8000		/* parameter applies to BSS */
+#define	IEEE80211_WMEPARAM_VAL	0x7fff		/* parameter value */
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+/*
  * FreeBSD-style ioctls.
  */
 /* the first member must be matched with struct ifreq */
 struct ieee80211req {
 	char		i_name[IFNAMSIZ];	/* if_name, e.g. "wi0" */
-	uint16_t	i_type;			/* req type */
+	u_int16_t	i_type;			/* req type */
 	int16_t		i_val;			/* Index or simple value */
 	int16_t		i_len;			/* Index or simple value */
 	void		*i_data;		/* Extra data */
@@ -98,38 +356,97 @@
 #define	SIOCS80211		 _IOW('i', 234, struct ieee80211req)
 #define	SIOCG80211		_IOWR('i', 235, struct ieee80211req)
 
-#define	IEEE80211_IOC_SSID		1
-#define	IEEE80211_IOC_NUMSSIDS		2
-#define	IEEE80211_IOC_WEP		3
-#define		IEEE80211_WEP_NOSUP	-1
-#define		IEEE80211_WEP_OFF	0
-#define		IEEE80211_WEP_ON	1
-#define		IEEE80211_WEP_MIXED	2
-#define	IEEE80211_IOC_WEPKEY		4
-#define	IEEE80211_IOC_NUMWEPKEYS	5
-#define	IEEE80211_IOC_WEPTXKEY		6
-#define	IEEE80211_IOC_AUTHMODE		7
-#define	IEEE80211_IOC_STATIONNAME	8
-#define	IEEE80211_IOC_CHANNEL		9
-#define	IEEE80211_IOC_POWERSAVE		10
-#define		IEEE80211_POWERSAVE_NOSUP	-1
-#define		IEEE80211_POWERSAVE_OFF		0
-#define		IEEE80211_POWERSAVE_CAM		1
-#define		IEEE80211_POWERSAVE_PSP		2
-#define		IEEE80211_POWERSAVE_PSP_CAM	3
-#define		IEEE80211_POWERSAVE_ON		IEEE80211_POWERSAVE_CAM
-#define	IEEE80211_IOC_POWERSAVESLEEP	11
+#define IEEE80211_IOC_SSID		1
+#define IEEE80211_IOC_NUMSSIDS		2
+#define IEEE80211_IOC_WEP		3
+#define 	IEEE80211_WEP_NOSUP	-1
+#define 	IEEE80211_WEP_OFF	0
+#define 	IEEE80211_WEP_ON	1
+#define 	IEEE80211_WEP_MIXED	2
+#define IEEE80211_IOC_WEPKEY		4
+#define IEEE80211_IOC_NUMWEPKEYS	5
+#define IEEE80211_IOC_WEPTXKEY		6
+#define IEEE80211_IOC_AUTHMODE		7
+#define IEEE80211_IOC_STATIONNAME	8
+#define IEEE80211_IOC_CHANNEL		9
+#define IEEE80211_IOC_POWERSAVE		10
+#define 	IEEE80211_POWERSAVE_NOSUP	-1
+#define 	IEEE80211_POWERSAVE_OFF		0
+#define 	IEEE80211_POWERSAVE_CAM		1
+#define 	IEEE80211_POWERSAVE_PSP		2
+#define 	IEEE80211_POWERSAVE_PSP_CAM	3
+#define 	IEEE80211_POWERSAVE_ON		IEEE80211_POWERSAVE_CAM
+#define IEEE80211_IOC_POWERSAVESLEEP	11
 #define	IEEE80211_IOC_RTSTHRESHOLD	12
-#define	IEEE80211_IOC_PROTMODE		13
-#define		IEEE80211_PROTMODE_OFF		0
-#define		IEEE80211_PROTMODE_CTS		1
-#define		IEEE80211_PROTMODE_RTSCTS	2
-#define	IEEE80211_IOC_TXPOWER		14
-
-#ifndef IEEE80211_CHAN_ANY
-#define	IEEE80211_CHAN_ANY	0xffff		/* token for ``any channel'' */
-#endif
+#define IEEE80211_IOC_PROTMODE		13
+#define 	IEEE80211_PROTMODE_OFF		0
+#define 	IEEE80211_PROTMODE_CTS		1
+#define 	IEEE80211_PROTMODE_RTSCTS	2
+#define	IEEE80211_IOC_TXPOWER		14	/* global tx power limit */
+#define	IEEE80211_IOC_BSSID		15
+#define	IEEE80211_IOC_ROAMING		16	/* roaming mode */
+#define	IEEE80211_IOC_PRIVACY		17	/* privacy invoked */
+#define	IEEE80211_IOC_DROPUNENCRYPTED	18	/* discard unencrypted frames */
+#define	IEEE80211_IOC_WPAKEY		19
+#define	IEEE80211_IOC_DELKEY		20
+#define	IEEE80211_IOC_MLME		21
+#define	IEEE80211_IOC_OPTIE		22	/* optional info. element */
+#define	IEEE80211_IOC_SCAN_REQ		23
+#define	IEEE80211_IOC_SCAN_RESULTS	24
+#define	IEEE80211_IOC_COUNTERMEASURES	25	/* WPA/TKIP countermeasures */
+#define	IEEE80211_IOC_WPA		26	/* WPA mode (0,1,2) */
+#define	IEEE80211_IOC_CHANLIST		27	/* channel list */
+#define	IEEE80211_IOC_WME		28	/* WME mode (on, off) */
+#define	IEEE80211_IOC_HIDESSID		29	/* hide SSID mode (on, off) */
+#define	IEEE80211_IOC_APBRIDGE		30	/* AP inter-sta bridging */
+#define	IEEE80211_IOC_MCASTCIPHER	31	/* multicast/default cipher */
+#define	IEEE80211_IOC_MCASTKEYLEN	32	/* multicast key length */
+#define	IEEE80211_IOC_UCASTCIPHERS	33	/* unicast cipher suites */
+#define	IEEE80211_IOC_UCASTCIPHER	34	/* unicast cipher */
+#define	IEEE80211_IOC_UCASTKEYLEN	35	/* unicast key length */
+#define	IEEE80211_IOC_DRIVER_CAPS	36	/* driver capabilities */
+#define	IEEE80211_IOC_KEYMGTALGS	37	/* key management algorithms */
+#define	IEEE80211_IOC_RSNCAPS		38	/* RSN capabilities */
+#define	IEEE80211_IOC_WPAIE		39	/* WPA information element */
+#define	IEEE80211_IOC_STA_STATS		40	/* per-station statistics */
+#define	IEEE80211_IOC_MACCMD		41	/* MAC ACL operation */
+#define	IEEE80211_IOC_CHANINFO		42	/* channel info list */
+#define	IEEE80211_IOC_TXPOWMAX		43	/* max tx power for channel */
+#define	IEEE80211_IOC_STA_TXPOW		44	/* per-station tx power limit */
+#define	IEEE80211_IOC_STA_INFO		45	/* station/neighbor info */
+#define	IEEE80211_IOC_WME_CWMIN		46	/* WME: ECWmin */
+#define	IEEE80211_IOC_WME_CWMAX		47	/* WME: ECWmax */
+#define	IEEE80211_IOC_WME_AIFS		48	/* WME: AIFSN */
+#define	IEEE80211_IOC_WME_TXOPLIMIT	49	/* WME: txops limit */
+#define	IEEE80211_IOC_WME_ACM		50	/* WME: ACM (bss only) */
+#define	IEEE80211_IOC_WME_ACKPOLICY	51	/* WME: ACK policy (!bss only)*/
+#define	IEEE80211_IOC_DTIM_PERIOD	52	/* DTIM period (beacons) */
+#define	IEEE80211_IOC_BEACON_INTERVAL	53	/* beacon interval (ms) */
+#define	IEEE80211_IOC_ADDMAC		54	/* add sta to MAC ACL table */
+#define	IEEE80211_IOC_DELMAC		55	/* del sta from MAC ACL table */
+
+/*
+ * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
+ */
+struct ieee80211req_scan_result {
+	u_int16_t	isr_len;		/* length (mult of 4) */
+	u_int16_t	isr_freq;		/* MHz */
+	u_int16_t	isr_flags;		/* channel flags */
+	u_int8_t	isr_noise;
+	u_int8_t	isr_rssi;
+	u_int8_t	isr_intval;		/* beacon interval */
+	u_int8_t	isr_capinfo;		/* capabilities */
+	u_int8_t	isr_erp;		/* ERP element */
+	u_int8_t	isr_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	isr_nrates;
+	u_int8_t	isr_rates[IEEE80211_RATE_MAXSIZE];
+	u_int8_t	isr_ssid_len;		/* SSID length */
+	u_int8_t	isr_ie_len;		/* IE length */
+	u_int8_t	isr_pad[5];
+	/* variable length SSID followed by IE data */
+};
 
 #define	SIOCG80211STATS		_IOWR('i', 236, struct ifreq)
+#endif /* __FreeBSD__ */
 
-#endif /* _NETPROTO_802_11_IEEE80211_IOCTL_H_ */
+#endif /* _NET80211_IEEE80211_IOCTL_H_ */
Index: src/sys/netproto/802_11/ieee80211_node.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_node.c,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_node.c
--- src/sys/netproto/802_11/ieee80211_node.c	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_node.c	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,126 +28,250 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.22 2004/04/05 04:15:55 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_node.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
-#include "opt_inet.h"
+#include <sys/cdefs.h>
 
 #include <sys/param.h>
 #include <sys/systm.h> 
 #include <sys/mbuf.h>   
 #include <sys/malloc.h>
 #include <sys/kernel.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-#include <sys/thread.h>
-#include <sys/thread2.h>
 
-#include <machine/atomic.h>
+#include <sys/socket.h>
  
+#include <sys/thread.h>
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
 #include <net/ethernet.h>
-#include <net/if_llc.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
 #include <net/bpf.h>
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
-
-static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
-static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *);
-static void ieee80211_node_copy(struct ieee80211com *,
-		struct ieee80211_node *, const struct ieee80211_node *);
-static uint8_t ieee80211_node_getrssi(struct ieee80211com *,
-		struct ieee80211_node *);
-
-static void ieee80211_setup_node(struct ieee80211com *ic,
-		struct ieee80211_node *ni, uint8_t *macaddr);
-static void _ieee80211_free_node(struct ieee80211com *,
-		struct ieee80211_node *);
+static struct ieee80211_node *node_alloc(struct ieee80211_node_table *);
+static void node_cleanup(struct ieee80211_node *);
+static void node_free(struct ieee80211_node *);
+static u_int8_t node_getrssi(const struct ieee80211_node *);
+
+static void ieee80211_setup_node(struct ieee80211_node_table *,
+		struct ieee80211_node *, const u_int8_t *);
+static void _ieee80211_free_node(struct ieee80211_node *);
+static void ieee80211_free_allnodes(struct ieee80211_node_table *);
+
+static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *);
+static void ieee80211_timeout_stations(struct ieee80211_node_table *);
+
+static void ieee80211_set_tim(struct ieee80211com *,
+		struct ieee80211_node *, int set);
+
+static void ieee80211_node_table_init(struct ieee80211com *ic,
+	struct ieee80211_node_table *nt, const char *name, int inact,
+	void (*timeout)(struct ieee80211_node_table *));
+static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
 
 MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
 
 void
-ieee80211_node_attach(struct ifnet *ifp)
+ieee80211_node_attach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 
-	lwkt_token_init(&ic->ic_nodetoken);
-	TAILQ_INIT(&ic->ic_node);
-	ic->ic_node_alloc = ieee80211_node_alloc;
-	ic->ic_node_free = ieee80211_node_free;
-	ic->ic_node_copy = ieee80211_node_copy;
-	ic->ic_node_getrssi = ieee80211_node_getrssi;
-	ic->ic_scangen = 1;
+	ieee80211_node_table_init(ic, &ic->ic_sta, "station",
+		IEEE80211_INACT_INIT, ieee80211_timeout_stations);
+	ieee80211_node_table_init(ic, &ic->ic_scan, "scan",
+		IEEE80211_INACT_SCAN, ieee80211_timeout_scan_candidates);
+
+	ic->ic_node_alloc = node_alloc;
+	ic->ic_node_free = node_free;
+	ic->ic_node_cleanup = node_cleanup;
+	ic->ic_node_getrssi = node_getrssi;
+
+	/* default station inactivity timer setings */
+	ic->ic_inact_init = IEEE80211_INACT_INIT;
+	ic->ic_inact_auth = IEEE80211_INACT_AUTH;
+	ic->ic_inact_run = IEEE80211_INACT_RUN;
+	ic->ic_inact_probe = IEEE80211_INACT_PROBE;
+
+	/* XXX defer */
+	if (ic->ic_max_aid == 0)
+		ic->ic_max_aid = IEEE80211_AID_DEF;
+	else if (ic->ic_max_aid > IEEE80211_AID_MAX)
+		ic->ic_max_aid = IEEE80211_AID_MAX;
+	MALLOC(ic->ic_aid_bitmap, u_int32_t *,
+		howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ic->ic_aid_bitmap == NULL) {
+		/* XXX no way to recover */
+		printf("%s: no memory for AID bitmap!\n", __func__);
+		ic->ic_max_aid = 0;
+	}
+
+	/* XXX defer until using hostap/ibss mode */
+	ic->ic_tim_len = howmany(ic->ic_max_aid, 8) * sizeof(u_int8_t);
+	MALLOC(ic->ic_tim_bitmap, u_int8_t *, ic->ic_tim_len,
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ic->ic_tim_bitmap == NULL) {
+		/* XXX no way to recover */
+		printf("%s: no memory for TIM bitmap!\n", __func__);
+	}
+	ic->ic_set_tim = ieee80211_set_tim;	/* NB: driver should override */
 }
 
 void
-ieee80211_node_lateattach(struct ifnet *ifp)
+ieee80211_node_lateattach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 	struct ieee80211_node *ni;
+	struct ieee80211_rsnparms *rsn;
 
-	ni = (*ic->ic_node_alloc)(ic);
+	ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
 	KASSERT(ni != NULL, ("unable to setup inital BSS node"));
-	ni->ni_chan = IEEE80211_CHAN_ANYC;
-	ic->ic_bss = ni;
-	ic->ic_txpower = IEEE80211_TXPOWER_MAX;
+	/*
+	 * Setup "global settings" in the bss node so that
+	 * each new station automatically inherits them.
+	 */
+	rsn = &ni->ni_rsn;
+	/* WEP, TKIP, and AES-CCM are always supported */
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_WEP;
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP;
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_CCM;
+	if (ic->ic_caps & IEEE80211_C_AES)
+		rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_OCB;
+	if (ic->ic_caps & IEEE80211_C_CKIP)
+		rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_CKIP;
+	/*
+	 * Default unicast cipher to WEP for 802.1x use.  If
+	 * WPA is enabled the management code will set these
+	 * values to reflect.
+	 */
+	rsn->rsn_ucastcipher = IEEE80211_CIPHER_WEP;
+	rsn->rsn_ucastkeylen = 104 / NBBY;
+	/*
+	 * WPA says the multicast cipher is the lowest unicast
+	 * cipher supported.  But we skip WEP which would
+	 * otherwise be used based on this criteria.
+	 */
+	rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP;
+	rsn->rsn_mcastkeylen = 128 / NBBY;
+
+	/*
+	 * We support both WPA-PSK and 802.1x; the one used
+	 * is determined by the authentication mode and the
+	 * setting of the PSK state.
+	 */
+	rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK;
+	rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+	ic->ic_bss = ieee80211_ref_node(ni);		/* hold reference */
+	ic->ic_auth = ieee80211_authenticator_get(ni->ni_authmode);
 }
 
 void
-ieee80211_node_detach(struct ifnet *ifp)
+ieee80211_node_detach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 
-	if (ic->ic_bss != NULL)
-		(*ic->ic_node_free)(ic, ic->ic_bss);
-	ieee80211_free_allnodes(ic);
-	lwkt_token_uninit(&ic->ic_nodetoken);
+	if (ic->ic_bss != NULL) {
+		ieee80211_free_node(ic->ic_bss);
+		ic->ic_bss = NULL;
+	}
+	ieee80211_node_table_cleanup(&ic->ic_scan);
+	ieee80211_node_table_cleanup(&ic->ic_sta);
+	if (ic->ic_aid_bitmap != NULL) {
+		FREE(ic->ic_aid_bitmap, M_DEVBUF);
+		ic->ic_aid_bitmap = NULL;
+	}
+	if (ic->ic_tim_bitmap != NULL) {
+		FREE(ic->ic_tim_bitmap, M_DEVBUF);
+		ic->ic_tim_bitmap = NULL;
+	}
+}
+
+/* 
+ * Port authorize/unauthorize interfaces for use by an authenticator.
+ */
+
+void
+ieee80211_node_authorize(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	ni->ni_flags |= IEEE80211_NODE_AUTH;
+	ni->ni_inact_reload = ic->ic_inact_run;
+}
+
+void
+ieee80211_node_unauthorize(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	ni->ni_flags &= ~IEEE80211_NODE_AUTH;
+}
+
+/*
+ * Set/change the channel.  The rate set is also updated as
+ * to insure a consistent view by drivers.
+ */
+static __inline void
+ieee80211_set_chan(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct ieee80211_channel *chan)
+{
+	ni->ni_chan = chan;
+	ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
 }
 
 /*
  * AP scanning support.
  */
 
+#ifdef IEEE80211_DEBUG
+static void
+dump_chanlist(const u_char chans[])
+{
+	const char *sep;
+	int i;
+
+	sep = " ";
+	for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+		if (isset(chans, i)) {
+			printf("%s%u", sep, i);
+			sep = ", ";
+		}
+}
+#endif /* IEEE80211_DEBUG */
+
 /*
- * Initialize the active channel set based on the set
+ * Initialize the channel set to scan based on the
  * of available channels and the current PHY mode.
  */
 static void
-ieee80211_reset_scan(struct ifnet *ifp)
+ieee80211_reset_scan(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 
-	memcpy(ic->ic_chan_scan, ic->ic_chan_active,
-		sizeof(ic->ic_chan_active));
+	/* XXX ic_des_chan should be handled with ic_chan_active */
+	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+		memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan));
+		setbit(ic->ic_chan_scan,
+			ieee80211_chan2ieee(ic, ic->ic_des_chan));
+	} else
+		memcpy(ic->ic_chan_scan, ic->ic_chan_active,
+			sizeof(ic->ic_chan_active));
 	/* NB: hack, setup so next_scan starts with the first channel */
 	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
-		ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX];
+		ieee80211_set_chan(ic, ic->ic_bss,
+			&ic->ic_channels[IEEE80211_CHAN_MAX]);
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic)) {
+		printf("%s: scan set:", __func__);
+		dump_chanlist(ic->ic_chan_scan);
+		printf(" start chan %u\n",
+			ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
+	}
+#endif /* IEEE80211_DEBUG */
 }
 
 /*
  * Begin an active scan.
  */
 void
-ieee80211_begin_scan(struct ifnet *ifp)
+ieee80211_begin_scan(struct ieee80211com *ic, int reset)
 {
-	struct ieee80211com *ic = (void *)ifp;
 
+	ic->ic_scan.nt_scangen++;
 	/*
 	 * In all but hostap mode scanning starts off in
 	 * an active mode before switching to passive.
@@ -157,96 +281,180 @@
 		ic->ic_stats.is_scan_active++;
 	} else
 		ic->ic_stats.is_scan_passive++;
-	if (ifp->if_flags & IFF_DEBUG)
-		if_printf(ifp, "begin %s scan\n",
-			(ic->ic_flags & IEEE80211_F_ASCAN) ?
-				"active" : "passive");
-	/*
-	 * Clear scan state and flush any previously seen
-	 * AP's.  Note that the latter assumes we don't act
-	 * as both an AP and a station, otherwise we'll
-	 * potentially flush state of stations associated
-	 * with us.
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		"begin %s scan in %s mode, scangen %u\n",
+		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive",
+		ieee80211_phymode_name[ic->ic_curmode], ic->ic_scan.nt_scangen);
+	/*
+	 * Clear scan state and flush any previously seen AP's.
 	 */
-	ieee80211_reset_scan(ifp);
-	ieee80211_free_allnodes(ic);
+	ieee80211_reset_scan(ic);
+	if (reset)
+		ieee80211_free_allnodes(&ic->ic_scan);
+
+	ic->ic_flags |= IEEE80211_F_SCAN;
 
 	/* Scan the next channel. */
-	ieee80211_next_scan(ifp);
+	ieee80211_next_scan(ic);
 }
 
 /*
  * Switch to the next channel marked for scanning.
  */
-void
-ieee80211_next_scan(struct ifnet *ifp)
+int
+ieee80211_next_scan(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 	struct ieee80211_channel *chan;
 
+	/*
+	 * Insure any previous mgt frame timeouts don't fire.
+	 * This assumes the driver does the right thing in
+	 * flushing anything queued in the driver and below.
+	 */
+	ic->ic_mgt_timer = 0;
+
 	chan = ic->ic_bss->ni_chan;
-	for (;;) {
+	do {
 		if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX])
 			chan = &ic->ic_channels[0];
 		if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) {
+			clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			    "%s: chan %d->%d\n", __func__,
+			    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
+			    ieee80211_chan2ieee(ic, chan));
+			ieee80211_set_chan(ic, ic->ic_bss, chan);
+#ifdef notyet
+			/* XXX driver state change */
 			/*
-			 * Honor channels marked passive-only
-			 * during an active scan.
+			 * Scan next channel. If doing an active scan
+			 * and the channel is not marked passive-only
+			 * then send a probe request.  Otherwise just
+			 * listen for beacons on the channel.
 			 */
-			if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 ||
-			    (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0)
-				break;
-		}
-		if (chan == ic->ic_bss->ni_chan) {
-			ieee80211_end_scan(ifp);
-			return;
+			if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
+			    (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+			}
+#else
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+#endif
+			return 1;
 		}
-	}
-	clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
-	IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
-	    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
-	    ieee80211_chan2ieee(ic, chan)));
-	ic->ic_bss->ni_chan = chan;
-	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+	} while (chan != ic->ic_bss->ni_chan);
+	ieee80211_end_scan(ic);
+	return 0;
+}
+
+static __inline void
+copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
+{
+	/* propagate useful state */
+	nbss->ni_authmode = obss->ni_authmode;
+	nbss->ni_txpower = obss->ni_txpower;
+	nbss->ni_vlan = obss->ni_vlan;
+	nbss->ni_rsn = obss->ni_rsn;
+	/* XXX statistics? */
 }
 
 void
 ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
 {
+	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
-	struct ifnet *ifp = &ic->ic_if;
+	IEEE80211_LOCK_INFO;
 
-	ni = ic->ic_bss;
-	if (ifp->if_flags & IFF_DEBUG)
-		if_printf(ifp, "creating ibss\n");
-	ic->ic_flags |= IEEE80211_F_SIBSS;
-	ni->ni_chan = chan;
-	ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
-	IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		"%s: creating ibss\n", __func__);
+
+	/*
+	 * Create the station/neighbor table.  Note that for adhoc
+	 * mode we make the initial inactivity timer longer since
+	 * we create nodes only through discovery and they typically
+	 * are long-lived associations.
+	 */
+	nt = &ic->ic_sta;
+	IEEE80211_NODE_LOCK(nt);
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		nt->nt_name = "station";
+		nt->nt_inact_init = ic->ic_inact_init;
+	} else {
+		nt->nt_name = "neighbor";
+		nt->nt_inact_init = ic->ic_inact_run;
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	ni = ieee80211_alloc_node(nt, ic->ic_myaddr);
+	if (ni == NULL) {
+		/* XXX recovery? */
+		return;
+	}
 	IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
-	if (ic->ic_opmode == IEEE80211_M_IBSS)
-		ni->ni_bssid[0] |= 0x02;	/* local bit for IBSS */
 	ni->ni_esslen = ic->ic_des_esslen;
 	memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
-	ni->ni_rssi = 0;
-	ni->ni_rstamp = 0;
-	memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
+	copy_bss(ni, ic->ic_bss);
 	ni->ni_intval = ic->ic_lintval;
-	ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
-	if (ic->ic_flags & IEEE80211_F_WEPON)
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
 		ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
 	if (ic->ic_phytype == IEEE80211_T_FH) {
 		ni->ni_fhdwell = 200;	/* XXX */
 		ni->ni_fhindex = 1;
 	}
-	ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		ic->ic_flags |= IEEE80211_F_SIBSS;
+		ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS;	/* XXX */
+		if (ic->ic_flags & IEEE80211_F_DESBSSID)
+			IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+		else
+			ni->ni_bssid[0] |= 0x02;	/* local bit for IBSS */
+	}
+	/* 
+	 * Fix the channel and related attributes.
+	 */
+	ieee80211_set_chan(ic, ni, chan);
+	ic->ic_curmode = ieee80211_chan2mode(ic, chan);
+	/*
+	 * Do mode-specific rate setup.
+	 */
+	if (ic->ic_curmode == IEEE80211_MODE_11G) {
+		/*
+		 * Use a mixed 11b/11g rate set.
+		 */
+		ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11G);
+	} else if (ic->ic_curmode == IEEE80211_MODE_11B) {
+		/*
+		 * Force pure 11b rate set.
+		 */
+		ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11B);
+	}
+
+	(void) ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+}
+
+void
+ieee80211_reset_bss(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni, *obss;
+
+	ieee80211_node_table_reset(&ic->ic_scan);
+	ieee80211_node_table_reset(&ic->ic_sta);
+
+	ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
+	KASSERT(ni != NULL, ("unable to setup inital BSS node"));
+	obss = ic->ic_bss;
+	ic->ic_bss = ieee80211_ref_node(ni);
+	if (obss != NULL) {
+		copy_bss(ni, obss);
+		ni->ni_intval = ic->ic_lintval;
+		ieee80211_free_node(obss);
+	}
 }
 
 static int
-ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni)
+ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = (void *)ifp;
-        uint8_t rate;
+        u_int8_t rate;
         int fail;
 
 	fail = 0;
@@ -262,7 +470,7 @@
 		if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
 			fail |= 0x02;
 	}
-	if (ic->ic_flags & IEEE80211_F_WEPON) {
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
 		if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
 			fail |= 0x04;
 	} else {
@@ -270,7 +478,8 @@
 		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
 			fail |= 0x04;
 	}
-	rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
+	rate = ieee80211_fix_rate(ic, ni,
+			IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
 	if (rate & IEEE80211_RATE_BASIC)
 		fail |= 0x08;
 	if (ic->ic_des_esslen != 0 &&
@@ -281,9 +490,11 @@
 	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
 		fail |= 0x20;
 #ifdef IEEE80211_DEBUG
-	if (ifp->if_flags & IFF_DEBUG) {
-		printf(" %c %6D", fail ? '-' : '+', ni->ni_macaddr, ":");
-		printf(" %6D%c", ni->ni_bssid, ":", fail & 0x20 ? '!' : ' ');
+	if (ieee80211_msg_scan(ic)) {
+		printf(" %c %6D", fail ? '-' : '+',
+		    (ni->ni_macaddr), ":");
+		printf(" %6D%c", (ni->ni_bssid), ":",
+		    fail & 0x20 ? '!' : ' ');
 		printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
 			fail & 0x01 ? '!' : ' ');
 		printf(" %+4d", ni->ni_rssi);
@@ -305,49 +516,149 @@
 	return fail;
 }
 
+static __inline u_int8_t
+maxrate(const struct ieee80211_node *ni)
+{
+	const struct ieee80211_rateset *rs = &ni->ni_rates;
+	/* NB: assumes rate set is sorted (happens on frame receive) */
+	return rs->rs_rates[rs->rs_nrates-1] & IEEE80211_RATE_VAL;
+}
+
+/*
+ * Compare the capabilities of two nodes and decide which is
+ * more desirable (return >0 if a is considered better).  Note
+ * that we assume compatibility/usability has already been checked
+ * so we don't need to (e.g. validate whether privacy is supported).
+ * Used to select the best scan candidate for association in a BSS.
+ */
+static int
+ieee80211_node_compare(struct ieee80211com *ic,
+		       const struct ieee80211_node *a,
+		       const struct ieee80211_node *b)
+{
+#define ABS(x) (x < 0 ? -x : x)
+	u_int8_t maxa, maxb;
+	u_int8_t rssia, rssib;
+
+	/* privacy support preferred */
+	if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) &&
+	    (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+		return 1;
+	if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0 &&
+	    (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY))
+		return -1;
+
+	rssia = ic->ic_node_getrssi(a);
+	rssib = ic->ic_node_getrssi(b);
+	if (ABS(rssib - rssia) < 5) {
+		/* best/max rate preferred if signal level close enough XXX */
+		maxa = maxrate(a);
+		maxb = maxrate(b);
+		if (maxa != maxb)
+			return maxa - maxb;
+		/* XXX use freq for channel preference */
+		/* for now just prefer 5Ghz band to all other bands */
+		if (IEEE80211_IS_CHAN_5GHZ(a->ni_chan) &&
+		   !IEEE80211_IS_CHAN_5GHZ(b->ni_chan))
+			return 1;
+		if (!IEEE80211_IS_CHAN_5GHZ(a->ni_chan) &&
+		     IEEE80211_IS_CHAN_5GHZ(b->ni_chan))
+			return -1;
+	}
+	/* all things being equal, use signal level */
+	return rssia - rssib;
+#undef ABS
+}
+
+/*
+ * Mark an ongoing scan stopped.
+ */
+void
+ieee80211_cancel_scan(struct ieee80211com *ic)
+{
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s: end %s scan\n",
+		__func__,
+		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive");
+
+	ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN);
+}
+
 /*
  * Complete a scan of potential channels.
  */
 void
-ieee80211_end_scan(struct ifnet *ifp)
+ieee80211_end_scan(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
-	struct ieee80211_node *ni, *nextbs, *selbs;
-	int i, fail;
+	struct ieee80211_node_table *nt = &ic->ic_scan;
+	struct ieee80211_node *ni, *selbs;
+	IEEE80211_LOCK_INFO;
 
-	ic->ic_flags &= ~IEEE80211_F_ASCAN;
-	ni = TAILQ_FIRST(&ic->ic_node);
+	ieee80211_cancel_scan(ic);
+	ieee80211_notify_scan_done(ic);
 
 	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-		/* XXX off stack? */
-		u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)];
+		u_int8_t maxrssi[IEEE80211_CHAN_MAX];	/* XXX off stack? */
+		int i, bestchan;
+		u_int8_t rssi;
+
 		/*
 		 * The passive scan to look for existing AP's completed,
 		 * select a channel to camp on.  Identify the channels
 		 * that already have one or more AP's and try to locate
-		 * an unnoccupied one.  If that fails, pick a random
-		 * channel from the active set.
+		 * an unoccupied one.  If that fails, pick a channel that
+		 * looks to be quietest.
 		 */
-		for (; ni != NULL; ni = nextbs) {
-			ieee80211_ref_node(ni);
-			nextbs = TAILQ_NEXT(ni, ni_list);
-			setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan));
-			ieee80211_free_node(ic, ni);
+		memset(maxrssi, 0, sizeof(maxrssi));
+		IEEE80211_NODE_LOCK(nt);
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+			rssi = ic->ic_node_getrssi(ni);
+			i = ieee80211_chan2ieee(ic, ni->ni_chan);
+			if (rssi > maxrssi[i])
+				maxrssi[i] = rssi;
 		}
+		IEEE80211_NODE_UNLOCK(nt);
+		/* XXX select channel more intelligently */
+		bestchan = -1;
 		for (i = 0; i < IEEE80211_CHAN_MAX; i++)
-			if (isset(ic->ic_chan_active, i) && isclr(occupied, i))
-				break;
-		if (i == IEEE80211_CHAN_MAX) {
-			fail = arc4random() & 3;	/* random 0-3 */
-			for (i = 0; i < IEEE80211_CHAN_MAX; i++)
-				if (isset(ic->ic_chan_active, i) && fail-- == 0)
+			if (isset(ic->ic_chan_active, i)) {
+				/*
+				 * If the channel is unoccupied the max rssi
+				 * should be zero; just take it.  Otherwise
+				 * track the channel with the lowest rssi and
+				 * use that when all channels appear occupied.
+				 */
+				if (maxrssi[i] == 0) {
+					bestchan = i;
 					break;
+				}
+				if (bestchan == -1 ||
+				    maxrssi[i] < maxrssi[bestchan])
+					bestchan = i;
+			}
+		if (bestchan != -1) {
+			ieee80211_create_ibss(ic, &ic->ic_channels[bestchan]);
+			return;
 		}
-		ieee80211_create_ibss(ic, &ic->ic_channels[i]);
-		return;
+		/* no suitable channel, should not happen */
 	}
-	if (ni == NULL) {
-		IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__));
+
+	/*
+	 * When manually sequencing the state machine; scan just once
+	 * regardless of whether we have a candidate or not.  The
+	 * controlling application is expected to setup state and
+	 * initiate an association.
+	 */
+	if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL)
+		return;
+	/*
+	 * Automatic sequencing; look for a candidate and
+	 * if found join the network.
+	 */
+	/* NB: unlocked read should be ok */
+	if (TAILQ_FIRST(&nt->nt_node) == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			"%s: no scan candidate\n", __func__);
   notfound:
 		if (ic->ic_opmode == IEEE80211_M_IBSS &&
 		    (ic->ic_flags & IEEE80211_F_IBSSON) &&
@@ -358,184 +669,442 @@
 		/*
 		 * Reset the list of channels to scan and start again.
 		 */
-		ieee80211_reset_scan(ifp);
-		ieee80211_next_scan(ifp);
+		ieee80211_reset_scan(ic);
+		ic->ic_flags |= IEEE80211_F_SCAN;
+		ieee80211_next_scan(ic);
 		return;
 	}
 	selbs = NULL;
-	if (ifp->if_flags & IFF_DEBUG)
-		if_printf(ifp, "\tmacaddr          bssid         chan  rssi rate flag  wep  essid\n");
-	for (; ni != NULL; ni = nextbs) {
-		ieee80211_ref_node(ni);
-		nextbs = TAILQ_NEXT(ni, ni_list);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "\t%s\n",
+	    "macaddr          bssid         chan  rssi rate flag  wep  essid");
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
 		if (ni->ni_fails) {
 			/*
 			 * The configuration of the access points may change
 			 * during my scan.  So delete the entry for the AP
 			 * and retry to associate if there is another beacon.
 			 */
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+				"%s: skip scan candidate %6D, fails %u\n",
+				__func__, (ni->ni_macaddr), ":",
+				ni->ni_fails);
+			ni->ni_fails++;
+#if 0
 			if (ni->ni_fails++ > 2)
-				ieee80211_free_node(ic, ni);
+				ieee80211_free_node(ni);
+#endif
 			continue;
 		}
-		if (ieee80211_match_bss(ifp, ni) == 0) {
+		if (ieee80211_match_bss(ic, ni) == 0) {
 			if (selbs == NULL)
 				selbs = ni;
-			else if (ni->ni_rssi > selbs->ni_rssi) {
-				ieee80211_unref_node(&selbs);
+			else if (ieee80211_node_compare(ic, ni, selbs) > 0)
 				selbs = ni;
-			} else
-				ieee80211_unref_node(&ni);
-		} else {
-			ieee80211_unref_node(&ni);
 		}
 	}
+	if (selbs != NULL)		/* NB: grab ref while dropping lock */
+		(void) ieee80211_ref_node(selbs);
+	IEEE80211_NODE_UNLOCK(nt);
 	if (selbs == NULL)
 		goto notfound;
-	(*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
+	if (!ieee80211_sta_join(ic, selbs)) {
+		ieee80211_free_node(selbs);
+		goto notfound;
+	}
+}
+ 
+/*
+ * Handle 802.11 ad hoc network merge.  The
+ * convention, set by the Wireless Ethernet Compatibility Alliance
+ * (WECA), is that an 802.11 station will change its BSSID to match
+ * the "oldest" 802.11 ad hoc network, on the same channel, that
+ * has the station's desired SSID.  The "oldest" 802.11 network
+ * sends beacons with the greatest TSF timestamp.
+ *
+ * The caller is assumed to validate TSF's before attempting a merge.
+ *
+ * Return !0 if the BSSID changed, 0 otherwise.
+ */
+int
+ieee80211_ibss_merge(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	if (ni == ic->ic_bss ||
+	    IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) {
+		/* unchanged, nothing to do */
+		return 0;
+	}
+	if (ieee80211_match_bss(ic, ni) != 0) {	/* capabilities mismatch */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "%s: merge failed, capabilities mismatch\n", __func__);
+		ic->ic_stats.is_ibss_capmismatch++;
+		return 0;
+	}
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		"%s: new bssid %6D: %s preamble, %s slot time%s\n", __func__,
+		(ni->ni_bssid), ":",
+		ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+		ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+		ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : ""
+	);
+	return ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+}
+
+/*
+ * Join the specified IBSS/BSS network.  The node is assumed to
+ * be passed in with a held reference.
+ */
+int
+ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs)
+{
+	struct ieee80211_node *obss;
+	IEEE80211_LOCK_INFO;
+
 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
-		ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE |
-		    IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
-		if (ic->ic_bss->ni_rates.rs_nrates == 0) {
-			selbs->ni_fails++;
-			ieee80211_unref_node(&selbs);
-			goto notfound;
-		}
-		ieee80211_unref_node(&selbs);
-		/*
-		 * Discard scan set; the nodes have a refcnt of zero
-		 * and have not asked the driver to setup private
-		 * node state.  Let them be repopulated on demand either
-		 * through transmission (ieee80211_find_txnode) or receipt
-		 * of a probe response (to be added).
+		struct ieee80211_node_table *nt;
+		/*
+		 * Delete unusable rates; we've already checked
+		 * that the negotiated rate set is acceptable.
 		 */
-		ieee80211_free_allnodes(ic);
-		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-	} else {
-		ieee80211_unref_node(&selbs);
-		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
+		ieee80211_fix_rate(ic, selbs, IEEE80211_F_DODEL);
+		/*
+		 * Fillin the neighbor table; it will already
+		 * exist if we are simply switching mastership.
+		 * XXX ic_sta always setup so this is unnecessary?
+		 */
+		nt = &ic->ic_sta;
+		IEEE80211_NODE_LOCK(nt);
+		nt->nt_name = "neighbor";
+		nt->nt_inact_init = ic->ic_inact_run;
+		IEEE80211_NODE_UNLOCK(nt);
 	}
+
+	/*
+	 * Committed to selbs, setup state.
+	 */
+	obss = ic->ic_bss;
+	ic->ic_bss = selbs;		/* NB: caller assumed to bump refcnt */
+	if (obss != NULL)
+		ieee80211_free_node(obss);
+	/*
+	 * Set the erp state (mostly the slot time) to deal with
+	 * the auto-select case; this should be redundant if the
+	 * mode is locked.
+	 */ 
+	ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan);
+	ieee80211_reset_erp(ic);
+	ieee80211_wme_initparams(ic);
+
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
+	else
+		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	return 1;
+}
+
+/*
+ * Leave the specified IBSS/BSS network.  The node is assumed to
+ * be passed in with a held reference.
+ */
+void
+ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	ic->ic_node_cleanup(ni);
+	ieee80211_notify_node_leave(ic, ni);
 }
 
 static struct ieee80211_node *
-ieee80211_node_alloc(struct ieee80211com *ic)
+node_alloc(struct ieee80211_node_table *nt)
 {
 	struct ieee80211_node *ni;
+
 	MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node),
 		M_80211_NODE, M_NOWAIT | M_ZERO);
 	return ni;
 }
 
+/*
+ * Reclaim any resources in a node and reset any critical
+ * state.  Typically nodes are free'd immediately after,
+ * but in some cases the storage may be reused so we need
+ * to insure consistent state (should probably fix that).
+ */
 static void
-ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
+node_cleanup(struct ieee80211_node *ni)
 {
-	FREE(ni, M_80211_NODE);
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ieee80211com *ic = ni->ni_ic;
+	int i, qlen;
+
+	/* NB: preserve ni_table */
+	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
+		ic->ic_ps_sta--;
+		ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] power save mode off, %u sta's in ps mode\n",
+		    (ni->ni_macaddr), ":", ic->ic_ps_sta);
+	}
+
+	/*
+	 * Drain power save queue and, if needed, clear TIM.
+	 */
+	IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen);
+	if (qlen != 0 && ic->ic_set_tim != NULL)
+		ic->ic_set_tim(ic, ni, 0);
+
+	ni->ni_associd = 0;
+	if (ni->ni_challenge != NULL) {
+		FREE(ni->ni_challenge, M_DEVBUF);
+		ni->ni_challenge = NULL;
+	}
+	/*
+	 * Preserve SSID, WPA, and WME ie's so the bss node is
+	 * reusable during a re-auth/re-assoc state transition.
+	 * If we remove these data they will not be recreated
+	 * because they come from a probe-response or beacon frame
+	 * which cannot be expected prior to the association-response.
+	 * This should not be an issue when operating in other modes
+	 * as stations leaving always go through a full state transition
+	 * which will rebuild this state.
+	 *
+	 * XXX does this leave us open to inheriting old state?
+	 */
+	for (i = 0; i < N(ni->ni_rxfrag); i++)
+		if (ni->ni_rxfrag[i] != NULL) {
+			m_freem(ni->ni_rxfrag[i]);
+			ni->ni_rxfrag[i] = NULL;
+		}
+	ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+#undef N
 }
 
 static void
-ieee80211_node_copy(struct ieee80211com *ic,
-	struct ieee80211_node *dst, const struct ieee80211_node *src)
+node_free(struct ieee80211_node *ni)
 {
-	*dst = *src;
+	struct ieee80211com *ic = ni->ni_ic;
+
+	ic->ic_node_cleanup(ni);
+	if (ni->ni_wpa_ie != NULL)
+		FREE(ni->ni_wpa_ie, M_DEVBUF);
+	if (ni->ni_wme_ie != NULL)
+		FREE(ni->ni_wme_ie, M_DEVBUF);
+	IEEE80211_NODE_SAVEQ_DESTROY(ni);
+	FREE(ni, M_80211_NODE);
 }
 
-static uint8_t
-ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
+static u_int8_t
+node_getrssi(const struct ieee80211_node *ni)
 {
 	return ni->ni_rssi;
 }
 
 static void
-ieee80211_setup_node(struct ieee80211com *ic,
-	struct ieee80211_node *ni, uint8_t *macaddr)
+ieee80211_setup_node(struct ieee80211_node_table *nt,
+	struct ieee80211_node *ni, const u_int8_t *macaddr)
 {
-	lwkt_tokref ilock;
+	struct ieee80211com *ic = nt->nt_ic;
+	IEEE80211_LOCK_INFO;
 	int hash;
 
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s %p<%6D> in %s table\n", __func__, ni,
+		(macaddr), ":", nt->nt_name);
+
 	IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
 	hash = IEEE80211_NODE_HASH(macaddr);
-	ni->ni_refcnt = 1;		/* mark referenced */
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
-	LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
-	/* 
-	 * Note we don't enable the inactive timer when acting
-	 * as a station.  Nodes created in this mode represent
-	 * AP's identified while scanning.  If we time them out
-	 * then several things happen: we can't return the data
-	 * to users to show the list of AP's we encountered, and
-	 * more importantly, we'll incorrectly deauthenticate
-	 * ourself because the inactivity timer will kick us off. 
-	 */
-	if (ic->ic_opmode != IEEE80211_M_STA)
-		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
-	lwkt_reltoken(&ilock);
+	ieee80211_node_initref(ni);		/* mark referenced */
+	ni->ni_chan = IEEE80211_CHAN_ANYC;
+	ni->ni_authmode = IEEE80211_AUTH_OPEN;
+	ni->ni_txpower = ic->ic_txpowlimit;	/* max power */
+	ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+	ni->ni_inact_reload = nt->nt_inact_init;
+	ni->ni_inact = ni->ni_inact_reload;
+	IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
+
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
+	LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash);
+	ni->ni_table = nt;
+	ni->ni_ic = ic;
+	IEEE80211_NODE_UNLOCK(nt);
 }
 
 struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211com *ic, uint8_t *macaddr)
+ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
 {
-	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni;
+
+	ni = ic->ic_node_alloc(nt);
 	if (ni != NULL)
-		ieee80211_setup_node(ic, ni, macaddr);
+		ieee80211_setup_node(nt, ni, macaddr);
 	else
 		ic->ic_stats.is_rx_nodealloc++;
 	return ni;
 }
 
 struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211com *ic, uint8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
 {
-	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni;
+
+	ni = ic->ic_node_alloc(nt);
 	if (ni != NULL) {
-		ieee80211_setup_node(ic, ni, macaddr);
+		ieee80211_setup_node(nt, ni, macaddr);
 		/*
 		 * Inherit from ic_bss.
 		 */
+		ni->ni_authmode = ic->ic_bss->ni_authmode;
+		ni->ni_txpower = ic->ic_bss->ni_txpower;
+		ni->ni_vlan = ic->ic_bss->ni_vlan;	/* XXX?? */
 		IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
-		ni->ni_chan = ic->ic_bss->ni_chan;
+		ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+		ni->ni_rsn = ic->ic_bss->ni_rsn;
 	} else
 		ic->ic_stats.is_rx_nodealloc++;
 	return ni;
 }
 
 static struct ieee80211_node *
-_ieee80211_find_node(struct ieee80211com *ic, uint8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+_ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, const char *func, int line)
+#else
+_ieee80211_find_node(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr)
+#endif
 {
 	struct ieee80211_node *ni;
 	int hash;
 
+	IEEE80211_NODE_LOCK_ASSERT(nt);
+
 	hash = IEEE80211_NODE_HASH(macaddr);
-	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+	LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
 		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
-			atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
+			ieee80211_ref_node(ni);	/* mark referenced */
+#ifdef IEEE80211_DEBUG_REFCNT
+			IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+			    "%s (%s:%u) %p<%6D> refcnt %d\n", __func__,
+			    func, line,
+			    ni, (ni->ni_macaddr), ":",
+			    ieee80211_node_refcnt(ni));
+#endif
 			return ni;
 		}
 	}
 	return NULL;
 }
+#ifdef IEEE80211_DEBUG_REFCNT
+#define	_ieee80211_find_node(nt, mac) \
+	_ieee80211_find_node_debug(nt, mac, func, line)
+#endif
 
 struct ieee80211_node *
-ieee80211_find_node(struct ieee80211com *ic, uint8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, const char *func, int line)
+#else
+ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+#endif
 {
 	struct ieee80211_node *ni;
-	struct lwkt_tokref ilock;
+	IEEE80211_LOCK_INFO;
 
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	ni = _ieee80211_find_node(ic, macaddr);
-	lwkt_reltoken(&ilock);
+	IEEE80211_NODE_LOCK(nt);
+	ni = _ieee80211_find_node(nt, macaddr);
+	IEEE80211_NODE_UNLOCK(nt);
 	return ni;
 }
 
 /*
+ * Fake up a node; this handles node discovery in adhoc mode.
+ * Note that for the driver's benefit we we treat this like
+ * an association so the driver has an opportunity to setup
+ * it's private state.
+ */
+struct ieee80211_node *
+ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
+	const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+{
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni;
+
+	ni = ieee80211_dup_bss(nt, macaddr);
+	if (ni != NULL) {
+		/* XXX no rate negotiation; just dup */
+		ni->ni_rates = ic->ic_bss->ni_rates;
+		if (ic->ic_newassoc != NULL)
+			ic->ic_newassoc(ic, ni, 1);
+		/* XXX not right for 802.1x/WPA */
+		ieee80211_node_authorize(ic, ni);
+	}
+	return ni;
+}
+
+/*
+ * Locate the node for sender, track state, and then pass the
+ * (referenced) node up to the 802.11 layer for its use.  We
+ * are required to pass some node so we fall back to ic_bss
+ * when this frame is from an unknown sender.  The 802.11 layer
+ * knows this means the sender wasn't in the node table and
+ * acts accordingly. 
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_rxnode_debug(struct ieee80211com *ic,
+	const struct ieee80211_frame_min *wh, const char *func, int line)
+#else
+ieee80211_find_rxnode(struct ieee80211com *ic,
+	const struct ieee80211_frame_min *wh)
+#endif
+{
+#define	IS_CTL(wh) \
+	((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+#define	IS_PSPOLL(wh) \
+	((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+	struct ieee80211_node_table *nt;
+	struct ieee80211_node *ni;
+	IEEE80211_LOCK_INFO;
+
+	/* XXX may want scanned nodes in the neighbor table for adhoc */
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_MONITOR ||
+	    (ic->ic_flags & IEEE80211_F_SCAN))
+		nt = &ic->ic_scan;
+	else
+		nt = &ic->ic_sta;
+	/* XXX check ic_bss first in station mode */
+	/* XXX 4-address frames? */
+	IEEE80211_NODE_LOCK(nt);
+	if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+		ni = _ieee80211_find_node(nt, wh->i_addr1);
+	else
+		ni = _ieee80211_find_node(nt, wh->i_addr2);
+	IEEE80211_NODE_UNLOCK(nt);
+
+	return (ni != NULL ? ni : ieee80211_ref_node(ic->ic_bss));
+#undef IS_PSPOLL
+#undef IS_CTL
+}
+
+/*
  * Return a reference to the appropriate node for sending
  * a data frame.  This handles node discovery in adhoc networks.
  */
 struct ieee80211_node *
-ieee80211_find_txnode(struct ieee80211com *ic, uint8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_txnode_debug(struct ieee80211com *ic, const u_int8_t *macaddr,
+	const char *func, int line)
+#else
+ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr)
+#endif
 {
+	struct ieee80211_node_table *nt = &ic->ic_sta;
 	struct ieee80211_node *ni;
-	struct lwkt_tokref ilock;
+	IEEE80211_LOCK_INFO;
 
 	/*
 	 * The destination address should be in the node table
@@ -543,31 +1112,29 @@
 	 * multicast/broadcast frame.
 	 */
 	if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
-		return ic->ic_bss;
+		return ieee80211_ref_node(ic->ic_bss);
 
 	/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	ni = _ieee80211_find_node(ic, macaddr);
-	lwkt_reltoken(&ilock);
-	ni = _ieee80211_find_node(ic, macaddr);
-	if (ni == NULL &&
-	    (ic->ic_opmode == IEEE80211_M_IBSS ||
-	     ic->ic_opmode == IEEE80211_M_AHDEMO)) {
-		/*
-		 * Fake up a node; this handles node discovery in
-		 * adhoc mode.  Note that for the driver's benefit
-		 * we we treat this like an association so the driver
-		 * has an opportunity to setup it's private state.
-		 *
-		 * XXX need better way to handle this; issue probe
-		 *     request so we can deduce rate set, etc.
-		 */
-		ni = ieee80211_dup_bss(ic, macaddr);
-		if (ni != NULL) {
-			/* XXX no rate negotiation; just dup */
-			ni->ni_rates = ic->ic_bss->ni_rates;
-			if (ic->ic_newassoc)
-				(*ic->ic_newassoc)(ic, ni, 1);
+	IEEE80211_NODE_LOCK(nt);
+	ni = _ieee80211_find_node(nt, macaddr);
+	IEEE80211_NODE_UNLOCK(nt);
+
+	if (ni == NULL) {
+		if (ic->ic_opmode == IEEE80211_M_IBSS ||
+		    ic->ic_opmode == IEEE80211_M_AHDEMO) {
+			/*
+			 * In adhoc mode cons up a node for the destination.
+			 * Note that we need an additional reference for the
+			 * caller to be consistent with _ieee80211_find_node.
+			 */
+			ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+			if (ni != NULL)
+				(void) ieee80211_ref_node(ni);
+		} else {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+				"[%6D] no node, discard frame (%s)\n",
+				(macaddr), ":", __func__);
+			ic->ic_stats.is_tx_nonode++;
 		}
 	}
 	return ni;
@@ -577,126 +1144,781 @@
  * Like find but search based on the channel too.
  */
 struct ieee80211_node *
-ieee80211_lookup_node(struct ieee80211com *ic,
-	uint8_t *macaddr, struct ieee80211_channel *chan)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_with_channel_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, struct ieee80211_channel *chan,
+	const char *func, int line)
+#else
+ieee80211_find_node_with_channel(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, struct ieee80211_channel *chan)
+#endif
 {
 	struct ieee80211_node *ni;
 	int hash;
-	lwkt_tokref ilock;
+	IEEE80211_LOCK_INFO;
 
 	hash = IEEE80211_NODE_HASH(macaddr);
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+	IEEE80211_NODE_LOCK(nt);
+	LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
 		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
 		    ni->ni_chan == chan) {
-			atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
+			ieee80211_ref_node(ni);		/* mark referenced */
+			IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+#ifdef IEEE80211_DEBUG_REFCNT
+			    "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
+			    func, line,
+#else
+			    "%s %p<%6D> refcnt %d\n", __func__,
+#endif
+			    ni, (ni->ni_macaddr), ":",
+			    ieee80211_node_refcnt(ni));
 			break;
 		}
 	}
-	lwkt_reltoken(&ilock);
+	IEEE80211_NODE_UNLOCK(nt);
+	return ni;
+}
+
+/*
+ * Like find but search based on the ssid too.
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_with_ssid_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid,
+	const char *func, int line)
+#else
+ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid)
+#endif
+{
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni;
+	int hash;
+	IEEE80211_LOCK_INFO;
+
+	hash = IEEE80211_NODE_HASH(macaddr);
+	IEEE80211_NODE_LOCK(nt);
+	LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
+		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
+		    ni->ni_esslen == ic->ic_des_esslen &&
+		    memcmp(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen) == 0) {
+			ieee80211_ref_node(ni);		/* mark referenced */
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+#ifdef IEEE80211_DEBUG_REFCNT
+			    "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
+			    func, line,
+#else
+			    "%s %p<%6D> refcnt %d\n", __func__,
+#endif
+			     ni, (ni->ni_macaddr), ":",
+			     ieee80211_node_refcnt(ni));
+			break;
+		}
+	}
+	IEEE80211_NODE_UNLOCK(nt);
 	return ni;
 }
 
 static void
-_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+_ieee80211_free_node(struct ieee80211_node *ni)
 {
-	KASSERT(ni != ic->ic_bss, ("freeing bss node"));
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_node_table *nt = ni->ni_table;
 
-	TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
-	LIST_REMOVE(ni, ni_hash);
-	if (TAILQ_EMPTY(&ic->ic_node))
-		ic->ic_inact_timer = 0;
-	(*ic->ic_node_free)(ic, ni);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s %p<%6D> in %s table\n", __func__, ni,
+		(ni->ni_macaddr), ":",
+		nt != NULL ? nt->nt_name : "<gone>");
+
+	IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+	if (nt != NULL) {
+		TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
+		LIST_REMOVE(ni, ni_hash);
+	}
+	ic->ic_node_free(ni);
 }
 
 void
-ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line)
+#else
+ieee80211_free_node(struct ieee80211_node *ni)
+#endif
 {
-	lwkt_tokref ilock;
-
-	KASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
+	struct ieee80211_node_table *nt = ni->ni_table;
+	IEEE80211_LOCK_INFO;
 
-	/* XXX DF atomic op */
-	crit_enter();
-	--ni->ni_refcnt;
-	if (ni->ni_refcnt == 0) {
-		crit_exit();
-		lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-		_ieee80211_free_node(ic, ni);
-		lwkt_reltoken(&ilock);
-	} else {
-		crit_exit();
+#ifdef IEEE80211_DEBUG_REFCNT
+	IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+		"%s (%s:%u) %p<%6D> refcnt %d\n", __func__, func, line, ni,
+		 (ni->ni_macaddr), ":", ieee80211_node_refcnt(ni)-1);
+#endif
+	if (ieee80211_node_dectestref(ni)) {
+		/*
+		 * Beware; if the node is marked gone then it's already
+		 * been removed from the table and we cannot assume the
+		 * table still exists.  Regardless, there's no need to lock
+		 * the table.
+		 */
+		if (ni->ni_table != NULL) {
+			IEEE80211_NODE_LOCK(nt);
+			_ieee80211_free_node(ni);
+			IEEE80211_NODE_UNLOCK(nt);
+		} else
+			_ieee80211_free_node(ni);
 	}
 }
 
-void
-ieee80211_free_allnodes(struct ieee80211com *ic)
+/*
+ * Reclaim a node.  If this is the last reference count then
+ * do the normal free work.  Otherwise remove it from the node
+ * table and mark it gone by clearing the back-reference.
+ */
+static void
+node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+{
+
+	IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+		"%s: remove %p<%6D> from %s table, refcnt %d\n",
+		__func__, ni, (ni->ni_macaddr), ":",
+		nt->nt_name, ieee80211_node_refcnt(ni)-1);
+	if (!ieee80211_node_dectestref(ni)) {
+		/*
+		 * Other references are present, just remove the
+		 * node from the table so it cannot be found.  When
+		 * the references are dropped storage will be
+		 * reclaimed.
+		 */
+		TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
+		LIST_REMOVE(ni, ni_hash);
+		ni->ni_table = NULL;		/* clear reference */
+	} else
+		_ieee80211_free_node(ni);
+}
+
+static void
+ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
 {
+	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
-	lwkt_tokref ilock;
 
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
-		_ieee80211_free_node(ic, ni);  
-	lwkt_reltoken(&ilock);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s: free all nodes in %s table\n", __func__, nt->nt_name);
+
+	while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) {
+		if (ni->ni_associd != 0) {
+			if (ic->ic_auth->ia_node_leave != NULL)
+				ic->ic_auth->ia_node_leave(ic, ni);
+			IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+		}
+		node_reclaim(nt, ni);
+	}
+	ieee80211_reset_erp(ic);
+}
+
+static void
+ieee80211_free_allnodes(struct ieee80211_node_table *nt)
+{
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_NODE_LOCK(nt);
+	ieee80211_free_allnodes_locked(nt);
+	IEEE80211_NODE_UNLOCK(nt);
+}
+
+/*
+ * Timeout entries in the scan cache.
+ */
+static void
+ieee80211_timeout_scan_candidates(struct ieee80211_node_table *nt)
+{
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni, *tni;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_NODE_LOCK(nt);
+	ni = ic->ic_bss;
+	/* XXX belongs elsewhere */
+	if (ni->ni_rxfrag[0] != NULL && ticks > ni->ni_rxfragstamp + hz) {
+		m_freem(ni->ni_rxfrag[0]);
+		ni->ni_rxfrag[0] = NULL;
+	}
+#ifndef TAILQ_FOREACH_SAFE
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
+		for ((var) = TAILQ_FIRST((head));			\
+		(var) && ((tvar) = TAILQ_NEXT((var), field), 1);	\
+		(var) = (tvar))
+#endif
+
+	TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, tni) {
+		if (ni->ni_inact && --ni->ni_inact == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+			    "[%6D] scan candidate purged from cache "
+			    "(refcnt %u)\n", (ni->ni_macaddr), ":",
+			    ieee80211_node_refcnt(ni));
+			node_reclaim(nt, ni);
+		}
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
 }
 
 /*
- * Timeout inactive nodes.  Note that we cannot hold the node
- * lock while sending a frame as this would lead to a LOR.
- * Instead we use a generation number to mark nodes that we've
- * scanned and drop the lock and restart a scan if we have to
- * time out a node.  Since we are single-threaded by virtue of
+ * Timeout inactive stations and do related housekeeping.
+ * Note that we cannot hold the node lock while sending a
+ * frame as this would lead to a LOR.  Instead we use a
+ * generation number to mark nodes that we've scanned and
+ * drop the lock and restart a scan if we have to time out
+ * a node.  Since we are single-threaded by virtue of
  * controlling the inactivity timer we can be sure this will
  * process each node only once.
  */
-void
-ieee80211_timeout_nodes(struct ieee80211com *ic)
+static void
+ieee80211_timeout_stations(struct ieee80211_node_table *nt)
 {
+	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
-	lwkt_tokref ilock;
-	u_int gen = ic->ic_scangen++;		/* NB: ok 'cuz single-threaded*/
-
+	u_int gen;
+	IEEE80211_LOCK_INFO;
+	IEEE80211_SCANLOCK_INFO;
+
+	IEEE80211_SCAN_LOCK(nt);
+	gen = nt->nt_scangen++;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s: %s scangen %u\n", __func__, nt->nt_name, gen);
 restart:
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
 		if (ni->ni_scangen == gen)	/* previously handled */
 			continue;
 		ni->ni_scangen = gen;
-		if (++ni->ni_inact > IEEE80211_INACT_MAX) {
-			IEEE80211_DPRINTF(("station %6D timed out "
-			    "due to inactivity (%u secs)\n",
-			    ni->ni_macaddr, ":", ni->ni_inact));
+		/*
+		 * Free fragment if not needed anymore
+		 * (last fragment older than 1s).
+		 * XXX doesn't belong here
+		 */
+		if (ni->ni_rxfrag[0] != NULL &&
+		    ticks > ni->ni_rxfragstamp + hz) {
+			m_freem(ni->ni_rxfrag[0]);
+			ni->ni_rxfrag[0] = NULL;
+		}
+		/*
+		 * Special case ourself; we may be idle for extended periods
+		 * of time and regardless reclaiming our state is wrong.
+		 */
+		if (ni == ic->ic_bss)
+			continue;
+		ni->ni_inact--;
+		if (ni->ni_associd != 0) {
+			/*
+			 * Age frames on the power save queue. The
+			 * aging interval is 4 times the listen
+			 * interval specified by the station.  This
+			 * number is factored into the age calculations
+			 * when the frame is placed on the queue.  We
+			 * store ages as time differences we can check
+			 * and/or adjust only the head of the list.
+			 */
+			if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+				struct mbuf *m;
+				int discard = 0;
+
+				IEEE80211_NODE_SAVEQ_LOCK(ni);
+				while (IF_POLL(&ni->ni_savedq, m) != NULL &&
+				     M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%6D] discard frame, age %u\n", (ni->ni_macaddr), ":", M_AGE_GET(m));/*XXX*/
+					_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
+					m_freem(m);
+					discard++;
+				}
+				if (m != NULL)
+					M_AGE_SUB(m, IEEE80211_INACT_WAIT);
+				IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+				if (discard != 0) {
+					IEEE80211_DPRINTF(ic,
+					    IEEE80211_MSG_POWER,
+					    "[%6D] discard %u frames for age\n",
+					    (ni->ni_macaddr), ":",
+					    discard);
+					IEEE80211_NODE_STAT_ADD(ni,
+						ps_discard, discard);
+					if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0)
+						ic->ic_set_tim(ic, ni, 0);
+				}
+			}
 			/*
-			 * Send a deauthenticate frame.
+			 * Probe the station before time it out.  We
+			 * send a null data frame which may not be
+			 * universally supported by drivers (need it
+			 * for ps-poll support so it should be...).
+			 */
+			if (0 < ni->ni_inact &&
+			    ni->ni_inact <= ic->ic_inact_probe) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+				    "[%6D] probe station due to inactivity\n",
+				    (ni->ni_macaddr), ":");
+				IEEE80211_NODE_UNLOCK(nt);
+				ieee80211_send_nulldata(ic, ni);
+				/* XXX stat? */
+				goto restart;
+			}
+		}
+		if (ni->ni_inact <= 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+			    "[%6D] station timed out due to inactivity "
+			    "(refcnt %u)\n", (ni->ni_macaddr), ":",
+			    ieee80211_node_refcnt(ni));
+			/*
+			 * Send a deauthenticate frame and drop the station.
+			 * This is somewhat complicated due to reference counts
+			 * and locking.  At this point a station will typically
+			 * have a reference count of 1.  ieee80211_node_leave
+			 * will do a "free" of the node which will drop the
+			 * reference count.  But in the meantime a reference
+			 * wil be held by the deauth frame.  The actual reclaim
+			 * of the node will happen either after the tx is
+			 * completed or by ieee80211_node_leave.
 			 *
-			 * Drop the node lock before sending the
-			 * deauthentication frame in case the driver takes     
-			 * a lock, as this will result in a LOR between the     
-			 * node lock and the driver lock.
+			 * Separately we must drop the node lock before sending
+			 * in case the driver takes a lock, as this will result
+			 * in  LOR between the node lock and the driver lock.
 			 */
-			lwkt_reltoken(&ilock);
-			IEEE80211_SEND_MGMT(ic, ni,
-			    IEEE80211_FC0_SUBTYPE_DEAUTH,
-			    IEEE80211_REASON_AUTH_EXPIRE);
-			ieee80211_free_node(ic, ni);
+			IEEE80211_NODE_UNLOCK(nt);
+			if (ni->ni_associd != 0) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH,
+				    IEEE80211_REASON_AUTH_EXPIRE);
+			}
+			ieee80211_node_leave(ic, ni);
 			ic->ic_stats.is_node_timeout++;
 			goto restart;
 		}
 	}
-	if (!TAILQ_EMPTY(&ic->ic_node))
-		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
-	lwkt_reltoken(&ilock);
+	IEEE80211_NODE_UNLOCK(nt);
+
+	IEEE80211_SCAN_UNLOCK(nt);
+
+	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
+}
+
+void
+ieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg)
+{
+	struct ieee80211_node *ni;
+	u_int gen;
+	IEEE80211_LOCK_INFO;
+	IEEE80211_SCANLOCK_INFO;
+
+	IEEE80211_SCAN_LOCK(nt);
+	gen = nt->nt_scangen++;
+restart:
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+		if (ni->ni_scangen != gen) {
+			ni->ni_scangen = gen;
+			(void) ieee80211_ref_node(ni);
+			IEEE80211_NODE_UNLOCK(nt);
+			(*f)(arg, ni);
+			ieee80211_free_node(ni);
+			goto restart;
+		}
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	IEEE80211_SCAN_UNLOCK(nt);
+}
+
+void
+ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+{
+	printf("0x%p: mac %6D refcnt %d\n", ni,
+		(ni->ni_macaddr), ":", ieee80211_node_refcnt(ni));
+	printf("\tscangen %u authmode %u flags 0x%x\n",
+		ni->ni_scangen, ni->ni_authmode, ni->ni_flags);
+	printf("\tassocid 0x%x txpower %u vlan %u\n",
+		ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
+	printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
+		ni->ni_txseqs[0],
+		ni->ni_rxseqs[0] >> IEEE80211_SEQ_SEQ_SHIFT,
+		ni->ni_rxseqs[0] & IEEE80211_SEQ_FRAG_MASK,
+		ni->ni_rxfragstamp);
+	printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n",
+		ni->ni_rstamp, ni->ni_rssi, ni->ni_intval, ni->ni_capinfo);
+	printf("\tbssid %6D essid \"%.*s\" channel %u:0x%x\n",
+		(ni->ni_bssid), ":",
+		ni->ni_esslen, ni->ni_essid,
+		ni->ni_chan->ic_freq, ni->ni_chan->ic_flags);
+	printf("\tfails %u inact %u txrate %u\n",
+		ni->ni_fails, ni->ni_inact, ni->ni_txrate);
+}
+
+void
+ieee80211_dump_nodes(struct ieee80211_node_table *nt)
+{
+	ieee80211_iterate_nodes(nt,
+		(ieee80211_iter_func *) ieee80211_dump_node, nt);
+}
+
+/*
+ * Handle a station joining an 11g network.
+ */
+static void
+ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	/*
+	 * Station isn't capable of short slot time.  Bump
+	 * the count of long slot time stations and disable
+	 * use of short slot time.  Note that the actual switch
+	 * over to long slot time use may not occur until the
+	 * next beacon transmission (per sec. 7.3.1.4 of 11g).
+	 */
+	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+		ic->ic_longslotsta++;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] station needs long slot time, count %d\n",
+		    (ni->ni_macaddr), ":", ic->ic_longslotsta);
+		/* XXX vap's w/ conflicting needs won't work */
+		ieee80211_set_shortslottime(ic, 0);
+	}
+	/*
+	 * If the new station is not an ERP station
+	 * then bump the counter and enable protection
+	 * if configured.
+	 */
+	if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
+		ic->ic_nonerpsta++;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] station is !ERP, %d non-ERP stations associated\n",
+		    (ni->ni_macaddr), ":", ic->ic_nonerpsta);
+		/*
+		 * If protection is configured, enable it.
+		 */
+		if (ic->ic_protmode != IEEE80211_PROT_NONE) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "%s: enable use of protection\n", __func__);
+			ic->ic_flags |= IEEE80211_F_USEPROT;
+		}
+		/*
+		 * If station does not support short preamble
+		 * then we must enable use of Barker preamble.
+		 */
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "[%6D] station needs long preamble\n",
+			    (ni->ni_macaddr), ":");
+			ic->ic_flags |= IEEE80211_F_USEBARKER;
+			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+		}
+	} else
+		ni->ni_flags |= IEEE80211_NODE_ERP;
+}
+
+void
+ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp)
+{
+	int newassoc;
+
+	if (ni->ni_associd == 0) {
+		u_int16_t aid;
+
+		/*
+		 * It would be good to search the bitmap
+		 * more efficiently, but this will do for now.
+		 */
+		for (aid = 1; aid < ic->ic_max_aid; aid++) {
+			if (!IEEE80211_AID_ISSET(aid,
+			    ic->ic_aid_bitmap))
+				break;
+		}
+		if (aid >= ic->ic_max_aid) {
+			IEEE80211_SEND_MGMT(ic, ni, resp,
+			    IEEE80211_REASON_ASSOC_TOOMANY);
+			ieee80211_node_leave(ic, ni);
+			return;
+		}
+		ni->ni_associd = aid | 0xc000;
+		IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
+		ic->ic_sta_assoc++;
+		newassoc = 1;
+		if (ic->ic_curmode == IEEE80211_MODE_11G ||
+		    ic->ic_curmode == IEEE80211_MODE_TURBO_G)
+			ieee80211_node_join_11g(ic, ni);
+	} else
+		newassoc = 0;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
+	    "[%6D] station %sassociated at aid %d: %s preamble, %s slot time%s%s\n",
+	    (ni->ni_macaddr), ":", newassoc ? "" : "re",
+	    IEEE80211_NODE_AID(ni),
+	    ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
+	    ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long",
+	    ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "",
+	    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : ""
+	);
+
+	/* give driver a chance to setup state like ni_txrate */
+	if (ic->ic_newassoc != NULL)
+		ic->ic_newassoc(ic, ni, newassoc);
+	ni->ni_inact_reload = ic->ic_inact_auth;
+	ni->ni_inact = ni->ni_inact_reload;
+	IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+	/* tell the authenticator about new station */
+	if (ic->ic_auth->ia_node_join != NULL)
+		ic->ic_auth->ia_node_join(ic, ni);
+	ieee80211_notify_node_join(ic, ni, newassoc);
+}
+
+/*
+ * Handle a station leaving an 11g network.
+ */
+static void
+ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	KASSERT(ic->ic_curmode == IEEE80211_MODE_11G ||
+		ic->ic_curmode == IEEE80211_MODE_TURBO_G,
+		("not in 11g, curmode %x", ic->ic_curmode));
+
+	/*
+	 * If a long slot station do the slot time bookkeeping.
+	 */
+	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+		KASSERT(ic->ic_longslotsta > 0,
+		    ("bogus long slot station count %d", ic->ic_longslotsta));
+		ic->ic_longslotsta--;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] long slot time station leaves, count now %d\n",
+		    (ni->ni_macaddr), ":", ic->ic_longslotsta);
+		if (ic->ic_longslotsta == 0) {
+			/*
+			 * Re-enable use of short slot time if supported
+			 * and not operating in IBSS mode (per spec).
+			 */
+			if ((ic->ic_caps & IEEE80211_C_SHSLOT) &&
+			    ic->ic_opmode != IEEE80211_M_IBSS) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "%s: re-enable use of short slot time\n",
+				    __func__);
+				ieee80211_set_shortslottime(ic, 1);
+			}
+		}
+	}
+	/*
+	 * If a non-ERP station do the protection-related bookkeeping.
+	 */
+	if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) {
+		KASSERT(ic->ic_nonerpsta > 0,
+		    ("bogus non-ERP station count %d", ic->ic_nonerpsta));
+		ic->ic_nonerpsta--;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] non-ERP station leaves, count now %d\n",
+		    (ni->ni_macaddr), ":", ic->ic_nonerpsta);
+		if (ic->ic_nonerpsta == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				"%s: disable use of protection\n", __func__);
+			ic->ic_flags &= ~IEEE80211_F_USEPROT;
+			/* XXX verify mode? */
+			if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "%s: re-enable use of short preamble\n",
+				    __func__);
+				ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+				ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+			}
+		}
+	}
 }
 
+/*
+ * Handle bookkeeping for station deauthentication/disassociation
+ * when operating as an ap.
+ */
 void
-ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg)
+ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct ieee80211_node_table *nt = ni->ni_table;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
+	    "[%6D] station with aid %d leaves\n",
+	    (ni->ni_macaddr), ":", IEEE80211_NODE_AID(ni));
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		ic->ic_opmode == IEEE80211_M_IBSS ||
+		ic->ic_opmode == IEEE80211_M_AHDEMO,
+		("unexpected operating mode %u", ic->ic_opmode));
+	/*
+	 * If node wasn't previously associated all
+	 * we need to do is reclaim the reference.
+	 */
+	/* XXX ibss mode bypasses 11g and notification */
+	if (ni->ni_associd == 0)
+		goto done;
+	/*
+	 * Tell the authenticator the station is leaving.
+	 * Note that we must do this before yanking the
+	 * association id as the authenticator uses the
+	 * associd to locate it's state block.
+	 */
+	if (ic->ic_auth->ia_node_leave != NULL)
+		ic->ic_auth->ia_node_leave(ic, ni);
+	IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+	ni->ni_associd = 0;
+	ic->ic_sta_assoc--;
+
+	if (ic->ic_curmode == IEEE80211_MODE_11G ||
+	    ic->ic_curmode == IEEE80211_MODE_TURBO_G)
+		ieee80211_node_leave_11g(ic, ni);
+	/*
+	 * Cleanup station state.  In particular clear various
+	 * state that might otherwise be reused if the node
+	 * is reused before the reference count goes to zero
+	 * (and memory is reclaimed).
+	 */
+	ieee80211_sta_leave(ic, ni);
+done:
+	/*
+	 * Remove the node from any table it's recorded in and
+	 * drop the caller's reference.  Removal from the table
+	 * is important to insure the node is not reprocessed
+	 * for inactivity.
+	 */
+	if (nt != NULL) {
+		IEEE80211_NODE_LOCK(nt);
+		node_reclaim(nt, ni);
+		IEEE80211_NODE_UNLOCK(nt);
+	} else
+		ieee80211_free_node(ni);
+}
+
+u_int8_t
+ieee80211_getrssi(struct ieee80211com *ic)
 {
+#define	NZ(x)	((x) == 0 ? 1 : (x))
+	struct ieee80211_node_table *nt = &ic->ic_sta;
+	u_int32_t rssi_samples, rssi_total;
 	struct ieee80211_node *ni;
-	struct lwkt_tokref ilock;
 
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
-		(*f)(arg, ni);
-	lwkt_reltoken(&ilock);
+	rssi_total = 0;
+	rssi_samples = 0;
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_IBSS:		/* average of all ibss neighbors */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
+			if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) {
+				rssi_samples++;
+				rssi_total += ic->ic_node_getrssi(ni);
+			}
+		break;
+	case IEEE80211_M_AHDEMO:	/* average of all neighbors */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+			rssi_samples++;
+			rssi_total += ic->ic_node_getrssi(ni);
+		}
+		break;
+	case IEEE80211_M_HOSTAP:	/* average of all associated stations */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
+			if (IEEE80211_AID(ni->ni_associd) != 0) {
+				rssi_samples++;
+				rssi_total += ic->ic_node_getrssi(ni);
+			}
+		break;
+	case IEEE80211_M_MONITOR:	/* XXX */
+	case IEEE80211_M_STA:		/* use stats from associated ap */
+	default:
+		if (ic->ic_bss != NULL)
+			rssi_total = ic->ic_node_getrssi(ic->ic_bss);
+		rssi_samples = 1;
+		break;
+	}
+	return rssi_total / NZ(rssi_samples);
+#undef NZ
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static void
+ieee80211_set_tim(struct ieee80211com *ic, struct ieee80211_node *ni, int set)
+{
+	u_int16_t aid;
+	IEEE80211_LOCK_INFO;
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		ic->ic_opmode == IEEE80211_M_IBSS,
+		("operating mode %u", ic->ic_opmode));
+
+	aid = IEEE80211_AID(ni->ni_associd);
+	KASSERT(aid < ic->ic_max_aid,
+		("bogus aid %u, max %u", aid, ic->ic_max_aid));
+
+	IEEE80211_BEACON_LOCK(ic);
+	if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) {
+		if (set) {
+			setbit(ic->ic_tim_bitmap, aid);
+			ic->ic_ps_pending++;
+		} else {
+			clrbit(ic->ic_tim_bitmap, aid);
+			ic->ic_ps_pending--;
+		}
+		ic->ic_flags |= IEEE80211_F_TIMUPDATE;
+	}
+	IEEE80211_BEACON_UNLOCK(ic);
+}
+
+/*
+ * Node table support.
+ */
+
+static void
+ieee80211_node_table_init(struct ieee80211com *ic,
+	struct ieee80211_node_table *nt,
+	const char *name, int inact,
+	void (*timeout)(struct ieee80211_node_table *))
+{
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s %s table, inact %u\n", __func__, name, inact);
+
+	nt->nt_ic = ic;
+	/* XXX need unit */
+	IEEE80211_NODE_LOCK_INIT(nt, ic->ic_ifp->if_xname);
+	IEEE80211_SCAN_LOCK_INIT(nt, ic->ic_ifp->if_xname);
+	TAILQ_INIT(&nt->nt_node);
+	nt->nt_name = name;
+	nt->nt_scangen = 1;
+	nt->nt_inact_init = inact;
+	nt->nt_timeout = timeout;
+}
+
+void
+ieee80211_node_table_reset(struct ieee80211_node_table *nt)
+{
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+		"%s %s table\n", __func__, nt->nt_name);
+
+	IEEE80211_NODE_LOCK(nt);
+	nt->nt_inact_timer = 0;
+	ieee80211_free_allnodes_locked(nt);
+	IEEE80211_NODE_UNLOCK(nt);
+}
+
+static void
+ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
+{
+
+	IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+		"%s %s table\n", __func__, nt->nt_name);
+
+	ieee80211_free_allnodes_locked(nt);
+	IEEE80211_SCAN_LOCK_DESTROY(nt);
+	IEEE80211_NODE_LOCK_DESTROY(nt);
 }
Index: src/sys/netproto/802_11/ieee80211_node.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_node.h,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_node.h
--- src/sys/netproto/802_11/ieee80211_node.h	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_node.h	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,31 +29,57 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.10 2004/04/05 22:10:26 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_node.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
+#ifndef _NET80211_IEEE80211_NODE_H_
+#define _NET80211_IEEE80211_NODE_H_
+
+#include <netproto/802_11/ieee80211_ioctl.h>		/* for ieee80211_nodestats */
 
-#ifndef _NETPROTO_802_11_IEEE80211_NODE_H_
-#define	_NETPROTO_802_11_IEEE80211_NODE_H_
+/*
+ * Each ieee80211com instance has a single timer that fires once a
+ * second.  This is used to initiate various work depending on the
+ * state of the instance: scanning (passive or active), ``transition''
+ * (waiting for a response to a management frame when operating
+ * as a station), and node inactivity processing (when operating
+ * as an AP).  For inactivity processing each node has a timeout
+ * set in it's ni_inact field that is decremented on each timeout
+ * and the node is reclaimed when the counter goes to zero.  We
+ * use different inactivity timeout values depending on whether
+ * the node is associated and authorized (either by 802.1x or
+ * open/shared key authentication) or associated but yet to be
+ * authorized.  The latter timeout is shorter to more aggressively
+ * reclaim nodes that leave part way through the 802.1x exchange.
+ */
+#define	IEEE80211_INACT_WAIT	15		/* inactivity interval (secs) */
+#define	IEEE80211_INACT_INIT	(30/IEEE80211_INACT_WAIT)	/* initial */
+#define	IEEE80211_INACT_AUTH	(180/IEEE80211_INACT_WAIT)	/* associated but not authorized */
+#define	IEEE80211_INACT_RUN	(300/IEEE80211_INACT_WAIT)	/* authorized */
+#define	IEEE80211_INACT_PROBE	(30/IEEE80211_INACT_WAIT)	/* probe */
+#define	IEEE80211_INACT_SCAN	(300/IEEE80211_INACT_WAIT)	/* scanned */
 
-#define	IEEE80211_PSCAN_WAIT 	5		/* passive scan wait */
-#define	IEEE80211_TRANS_WAIT 	5		/* transition wait */
-#define	IEEE80211_INACT_WAIT	5		/* inactivity timer interval */
-#define	IEEE80211_INACT_MAX	(300/IEEE80211_INACT_WAIT)
+#define	IEEE80211_TRANS_WAIT 	5		/* mgt frame tx timer (secs) */
 
 #define	IEEE80211_NODE_HASHSIZE	32
 /* simple hash is enough for variation of macaddr */
 #define	IEEE80211_NODE_HASH(addr)	\
-	(((uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE)
+	(((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
+		IEEE80211_NODE_HASHSIZE)
 
-#define	IEEE80211_RATE_SIZE	8		/* 802.11 standard */
-#define	IEEE80211_RATE_MAXSIZE	15		/* max rates we'll handle */
-
-struct ieee80211_rateset {
-	uint8_t			rs_nrates;
-	uint8_t			rs_rates[IEEE80211_RATE_MAXSIZE];
+struct ieee80211_rsnparms {
+	u_int8_t	rsn_mcastcipher;	/* mcast/group cipher */
+	u_int8_t	rsn_mcastkeylen;	/* mcast key length */
+	u_int8_t	rsn_ucastcipherset;	/* unicast cipher set */
+	u_int8_t	rsn_ucastcipher;	/* selected unicast cipher */
+	u_int8_t	rsn_ucastkeylen;	/* unicast key length */
+	u_int8_t	rsn_keymgmtset;		/* key mangement algorithms */
+	u_int8_t	rsn_keymgmt;		/* selected key mgmt algo */
+	u_int16_t	rsn_caps;		/* capabilities */
 };
 
+struct ieee80211_node_table;
+struct ieee80211com;
+
 /*
  * Node specific information.  Note that drivers are expected
  * to derive from this structure to add device-specific per-node
@@ -61,96 +87,196 @@
  * the ieee80211com structure.
  */
 struct ieee80211_node {
+	struct ieee80211com	*ni_ic;
+	struct ieee80211_node_table *ni_table;
 	TAILQ_ENTRY(ieee80211_node)	ni_list;
 	LIST_ENTRY(ieee80211_node)	ni_hash;
 	u_int			ni_refcnt;
 	u_int			ni_scangen;	/* gen# for timeout scan */
+	u_int8_t		ni_authmode;	/* authentication algorithm */
+	u_int16_t		ni_flags;	/* special-purpose state */
+#define	IEEE80211_NODE_AUTH	0x0001		/* authorized for data */
+#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
+#define	IEEE80211_NODE_ERP	0x0004		/* ERP enabled */
+/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */
+#define	IEEE80211_NODE_PWR_MGT	0x0010		/* power save mode enabled */
+	u_int16_t		ni_associd;	/* assoc response */
+	u_int16_t		ni_txpower;	/* current transmit power */
+	u_int16_t		ni_vlan;	/* vlan tag */
+	u_int32_t		*ni_challenge;	/* shared-key challenge */
+	u_int8_t		*ni_wpa_ie;	/* captured WPA/RSN ie */
+	u_int8_t		*ni_wme_ie;	/* captured WME ie */
+	u_int16_t		ni_txseqs[17];	/* tx seq per-tid */
+	u_int16_t		ni_rxseqs[17];	/* rx seq previous per-tid*/
+	u_int32_t		ni_rxfragstamp;	/* time stamp of last rx frag */
+	struct mbuf		*ni_rxfrag[3];	/* rx frag reassembly */
+	struct ieee80211_rsnparms ni_rsn;	/* RSN/WPA parameters */
+	struct ieee80211_key	ni_ucastkey;	/* unicast key */
 
 	/* hardware */
-	uint32_t		ni_rstamp;	/* recv timestamp */
-	uint8_t			ni_rssi;	/* recv ssi */
+	u_int32_t		ni_rstamp;	/* recv timestamp */
+	u_int8_t		ni_rssi;	/* recv ssi */
 
 	/* header */
-	uint8_t			ni_macaddr[IEEE80211_ADDR_LEN];
-	uint8_t			ni_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t		ni_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t		ni_bssid[IEEE80211_ADDR_LEN];
 
 	/* beacon, probe response */
-	uint8_t			ni_tstamp[8];	/* from last rcv'd beacon */
-	uint16_t		ni_intval;	/* beacon interval */
-	uint16_t		ni_capinfo;	/* capabilities */
-	uint8_t			ni_esslen;
-	uint8_t			ni_essid[IEEE80211_NWID_LEN];
+	union {
+		u_int8_t	data[8];
+		u_int64_t	tsf;
+	} ni_tstamp;				/* from last rcv'd beacon */
+	u_int16_t		ni_intval;	/* beacon interval */
+	u_int16_t		ni_capinfo;	/* capabilities */
+	u_int8_t		ni_esslen;
+	u_int8_t		ni_essid[IEEE80211_NWID_LEN];
 	struct ieee80211_rateset ni_rates;	/* negotiated rate set */
-	uint8_t			*ni_country;	/* country information XXX */
 	struct ieee80211_channel *ni_chan;
-	uint16_t		ni_fhdwell;	/* FH only */
-	uint8_t			ni_fhindex;	/* FH only */
-	uint8_t			ni_erp;		/* 11g only */
-
-#ifdef notyet
-	/* DTIM and contention free period (CFP) */
-	uint8_t			ni_dtimperiod;
-	uint8_t			ni_cfpperiod;	/* # of DTIMs between CFPs */
-	uint16_t		ni_cfpduremain;	/* remaining cfp duration */
-	uint16_t		ni_cfpmaxduration;/* max CFP duration in TU */
-	uint16_t		ni_nextdtim;	/* time to next DTIM */
-	uint16_t		ni_timoffset;
-#endif
+	u_int16_t		ni_fhdwell;	/* FH only */
+	u_int8_t		ni_fhindex;	/* FH only */
+	u_int8_t		ni_erp;		/* ERP from beacon/probe resp */
+	u_int16_t		ni_timoff;	/* byte offset to TIM ie */
 
 	/* others */
-	uint16_t		ni_associd;	/* assoc response */
-	uint16_t		ni_txseq;	/* seq to be transmitted */
-	uint16_t		ni_rxseq;	/* seq previous received */
 	int			ni_fails;	/* failure count to associate */
-	int			ni_inact;	/* inactivity mark count */
+	short			ni_inact;	/* inactivity mark count */
+	short			ni_inact_reload;/* inactivity reload value */
 	int			ni_txrate;	/* index to ni_rates[] */
+	struct	ifqueue		ni_savedq;	/* ps-poll queue */
+	struct ieee80211_nodestats ni_stats;	/* per-node statistics */
 };
+MALLOC_DECLARE(M_80211_NODE);
+
+#define	IEEE80211_NODE_AID(ni)	IEEE80211_AID(ni->ni_associd)
+
+#define	IEEE80211_NODE_STAT(ni,stat)	(ni->ni_stats.ns_##stat++)
+#define	IEEE80211_NODE_STAT_ADD(ni,stat,v)	(ni->ni_stats.ns_##stat += v)
+#define	IEEE80211_NODE_STAT_SET(ni,stat,v)	(ni->ni_stats.ns_##stat = v)
 
 static __inline struct ieee80211_node *
 ieee80211_ref_node(struct ieee80211_node *ni)
 {
-	atomic_add_int(&ni->ni_refcnt, 1);
+	ieee80211_node_incref(ni);
 	return ni;
 }
 
 static __inline void
 ieee80211_unref_node(struct ieee80211_node **ni)
 {
-	atomic_subtract_int(&(*ni)->ni_refcnt, 1);
+	ieee80211_node_decref(*ni);
 	*ni = NULL;			/* guard against use */
 }
 
 struct ieee80211com;
 
-#ifdef MALLOC_DECLARE
-MALLOC_DECLARE(M_80211_NODE);
-#endif
+void	ieee80211_node_attach(struct ieee80211com *);
+void	ieee80211_node_lateattach(struct ieee80211com *);
+void	ieee80211_node_detach(struct ieee80211com *);
+
+static __inline int
+ieee80211_node_is_authorized(const struct ieee80211_node *ni)
+{
+	return (ni->ni_flags & IEEE80211_NODE_AUTH);
+}
 
-extern	void ieee80211_node_attach(struct ifnet *);
-extern	void ieee80211_node_lateattach(struct ifnet *);
-extern	void ieee80211_node_detach(struct ifnet *);
-
-extern	void ieee80211_begin_scan(struct ifnet *);
-extern	void ieee80211_next_scan(struct ifnet *);
-extern	void ieee80211_end_scan(struct ifnet *);
-extern	struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node *ieee80211_find_node(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *,
-		uint8_t *macaddr, struct ieee80211_channel *);
-extern	void ieee80211_free_node(struct ieee80211com *,
+void	ieee80211_node_authorize(struct ieee80211com *,
+		struct ieee80211_node *);
+void	ieee80211_node_unauthorize(struct ieee80211com *,
 		struct ieee80211_node *);
-extern	void ieee80211_free_allnodes(struct ieee80211com *);
+
+void	ieee80211_begin_scan(struct ieee80211com *, int);
+int	ieee80211_next_scan(struct ieee80211com *);
+void	ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *);
+void	ieee80211_reset_bss(struct ieee80211com *);
+void	ieee80211_cancel_scan(struct ieee80211com *);
+void	ieee80211_end_scan(struct ieee80211com *);
+int	ieee80211_ibss_merge(struct ieee80211com *, struct ieee80211_node *);
+int	ieee80211_sta_join(struct ieee80211com *, struct ieee80211_node *);
+void	ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *);
+
+/*
+ * Table of ieee80211_node instances.  Each ieee80211com
+ * has at least one for holding the scan candidates.
+ * When operating as an access point or in ibss mode there
+ * is a second table for associated stations or neighbors.
+ */
+struct ieee80211_node_table {
+	struct ieee80211com	*nt_ic;		/* back reference */
+	ieee80211_node_lock_t	nt_nodelock;	/* on node table */
+	TAILQ_HEAD(, ieee80211_node) nt_node;	/* information of all nodes */
+	LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
+	const char		*nt_name;	/* for debugging */
+	ieee80211_scan_lock_t	nt_scanlock;	/* on nt_scangen */
+	u_int			nt_scangen;	/* gen# for timeout scan */
+	int			nt_inact_timer;	/* inactivity timer */
+	int			nt_inact_init;	/* initial node inact setting */
+
+	void			(*nt_timeout)(struct ieee80211_node_table *);
+};
+void	ieee80211_node_table_reset(struct ieee80211_node_table *);
+
+struct ieee80211_node *ieee80211_alloc_node(
+		struct ieee80211_node_table *, const u_int8_t *);
+struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
+		const u_int8_t *);
+#ifdef IEEE80211_DEBUG_REFCNT
+void	ieee80211_free_node_debug(struct ieee80211_node *,
+		const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_debug(
+		struct ieee80211_node_table *, const u_int8_t *,
+		const char *func, int line);
+struct ieee80211_node * ieee80211_find_rxnode_debug(
+		struct ieee80211com *, const struct ieee80211_frame_min *,
+		const char *func, int line);
+struct ieee80211_node *ieee80211_find_txnode_debug(
+		struct ieee80211com *, const u_int8_t *,
+		const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_with_channel_debug(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		struct ieee80211_channel *, const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_with_ssid_debug(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		u_int ssidlen, const u_int8_t *ssid,
+		const char *func, int line);
+#define	ieee80211_free_node(ni) \
+	ieee80211_free_node_debug(ni, __func__, __LINE__)
+#define	ieee80211_find_node(nt, mac) \
+	ieee80211_find_node_debug(nt, mac, __func__, __LINE__)
+#define	ieee80211_find_rxnode(nt, wh) \
+	ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__)
+#define	ieee80211_find_txnode(nt, mac) \
+	ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__)
+#define	ieee80211_find_node_with_channel(nt, mac, c) \
+	ieee80211_find_node_with_channel_debug(nt, mac, c, __func__, __LINE__)
+#define	ieee80211_find_node_with_ssid(nt, mac, sl, ss) \
+	ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__)
+#else
+void	ieee80211_free_node(struct ieee80211_node *);
+struct ieee80211_node *ieee80211_find_node(
+		struct ieee80211_node_table *, const u_int8_t *);
+struct ieee80211_node * ieee80211_find_rxnode(
+		struct ieee80211com *, const struct ieee80211_frame_min *);
+struct ieee80211_node *ieee80211_find_txnode(
+		struct ieee80211com *, const u_int8_t *);
+struct ieee80211_node *ieee80211_find_node_with_channel(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		struct ieee80211_channel *);
+struct ieee80211_node *ieee80211_find_node_with_ssid(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		u_int ssidlen, const u_int8_t *ssid);
+#endif
+
 typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
-extern	void ieee80211_iterate_nodes(struct ieee80211com *ic,
+void	ieee80211_iterate_nodes(struct ieee80211_node_table *,
 		ieee80211_iter_func *, void *);
-extern	void ieee80211_timeout_nodes(struct ieee80211com *);
 
-extern	void ieee80211_create_ibss(struct ieee80211com* ,
-		struct ieee80211_channel *);
-#endif /* _NETPROTO_802_11_IEEE80211_NODE_H_ */
+void	ieee80211_dump_node(struct ieee80211_node_table *,
+		struct ieee80211_node *);
+void	ieee80211_dump_nodes(struct ieee80211_node_table *);
+
+struct ieee80211_node *ieee80211_fakeup_adhoc_node(
+		struct ieee80211_node_table *, const u_int8_t macaddr[]);
+void	ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
+void	ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
+u_int8_t ieee80211_getrssi(struct ieee80211com *ic);
+#endif /* _NET80211_IEEE80211_NODE_H_ */
Index: src/sys/netproto/802_11/ieee80211_output.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_output.c,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_output.c
--- src/sys/netproto/802_11/ieee80211_output.c	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_output.c	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,42 +28,55 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.10 2004/04/02 23:25:39 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_output.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 #include "opt_inet.h"
 
 #include <sys/param.h>
 #include <sys/systm.h> 
 #include <sys/mbuf.h>   
-#include <sys/malloc.h>
 #include <sys/kernel.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
 #include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
 
-#include <machine/atomic.h>
+#include <sys/socket.h>
+#include <sys/thread.h>
  
+#include <net/bpf.h>
+#include <net/ethernet.h>
 #include <net/if.h>
-#include <net/if_dl.h>
+#include <net/if_llc.h>
 #include <net/if_media.h>
+#include <net/if_var.h>
 #include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
+#include <net/vlan/if_vlan_var.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
-#include <net/bpf.h>
-
 #ifdef INET
 #include <netinet/in.h> 
 #include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Decide if an outbound management frame should be
+ * printed when debugging is enabled.  This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211com *ic, int subtype)
+{
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		return (ic->ic_opmode == IEEE80211_M_IBSS);
+	}
+	return 1;
+}
 #endif
 
 /*
@@ -74,14 +87,13 @@
  * reference (and potentially free'ing up any associated storage).
  */
 static int
-ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
+ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
     struct mbuf *m, int type)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_frame *wh;
 
 	KASSERT(ni != NULL, ("null node"));
-	ni->ni_inact = 0;
 
 	/*
 	 * Yech, hack alert!  We want to pass the node down to the
@@ -104,30 +116,48 @@
 	wh = mtod(m, struct ieee80211_frame *);
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
-	*(uint16_t *)wh->i_dur = 0;
-	*(uint16_t *)wh->i_seq =
-	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
-	ni->ni_txseq++;
-	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
-	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
-	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+	*(u_int16_t *)wh->i_dur = 0;
+	*(u_int16_t *)wh->i_seq =
+	    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+	ni->ni_txseqs[0]++;
+	/*
+	 * Hack.  When sending PROBE_REQ frames while scanning we
+	 * explicitly force a broadcast rather than (as before) clobber
+	 * ni_macaddr and ni_bssid.  This is stopgap, we need a way
+	 * to communicate this directly rather than do something
+	 * implicit based on surrounding state.
+	 */
+	if (type == IEEE80211_FC0_SUBTYPE_PROBE_REQ &&
+	    (ic->ic_flags & IEEE80211_F_SCAN)) {
+		IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
+		IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+		IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
+	} else {
+		IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+		IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+		IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+	}
 
-	if (ifp->if_flags & IFF_DEBUG) {
-		/* avoid to print too many frames */
-		if (ic->ic_opmode == IEEE80211_M_IBSS ||
+	if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
+		m->m_flags &= ~M_LINK0;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			"[%6D] encrypting frame (%s)\n",
+			(wh->i_addr1), ":", __func__);
+		wh->i_fc[1] |= IEEE80211_FC1_WEP;
+	}
 #ifdef IEEE80211_DEBUG
-		    ieee80211_debug > 1 ||
-#endif
-		    (type & IEEE80211_FC0_SUBTYPE_MASK) !=
-		    IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-			if_printf(ifp, "sending %s to %6D on channel %u\n",
-			    ieee80211_mgt_subtype_name[
-			    (type & IEEE80211_FC0_SUBTYPE_MASK)
-			    >> IEEE80211_FC0_SUBTYPE_SHIFT],
-			    ni->ni_macaddr, ":",
-			    ieee80211_chan2ieee(ic, ni->ni_chan));
+	/* avoid printing too many frames */
+	if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
+	    ieee80211_msg_dumppkts(ic)) {
+		printf("[%6D] send %s on channel %u\n",
+		    (wh->i_addr1), ":",
+		    ieee80211_mgt_subtype_name[
+			(type & IEEE80211_FC0_SUBTYPE_MASK) >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+		    ieee80211_chan2ieee(ic, ni->ni_chan));
 	}
-
+#endif
+	IEEE80211_NODE_STAT(ni, tx_mgmt);
 	IF_ENQUEUE(&ic->ic_mgtq, m);
 	ifp->if_timer = 1;
 	(*ifp->if_start)(ifp);
@@ -135,41 +165,337 @@
 }
 
 /*
- * Encapsulate an outbound data frame.  The mbuf chain is updated and
- * a reference to the destination node is returned.  If an error is
- * encountered NULL is returned and the node reference will also be NULL.
- * 
- * NB: The caller is responsible for free'ing a returned node reference.
- *     The convention is ic_bss is not reference counted; the caller must
- *     maintain that.
+ * Send a null data frame to the specified node.
+ */
+int
+ieee80211_send_nulldata(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct mbuf *m;
+	struct ieee80211_frame *wh;
+
+	MGETHDR(m, M_NOWAIT, MT_HEADER);
+	if (m == NULL) {
+		/* XXX debug msg */
+		ic->ic_stats.is_tx_nobuf++;
+		return ENOMEM;
+	}
+	m->m_pkthdr.rcvif = (void *) ieee80211_ref_node(ni);
+
+	wh = mtod(m, struct ieee80211_frame *);
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA |
+		IEEE80211_FC0_SUBTYPE_NODATA;
+	*(u_int16_t *)wh->i_dur = 0;
+	*(u_int16_t *)wh->i_seq =
+	    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+	ni->ni_txseqs[0]++;
+
+	/* XXX WDS */
+	wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
+	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_myaddr);
+	m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+
+	IEEE80211_NODE_STAT(ni, tx_data);
+
+	IF_ENQUEUE(&ic->ic_mgtq, m);		/* cheat */
+	(*ifp->if_start)(ifp);
+
+	return 0;
+}
+
+/* 
+ * Assign priority to a frame based on any vlan tag assigned
+ * to the station and/or any Diffserv setting in an IP header.
+ * Finally, if an ACM policy is setup (in station mode) it's
+ * applied.
+ */
+int
+ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni)
+{
+	int v_wme_ac, d_wme_ac, ac;
+#ifdef INET
+	struct ether_header *eh;
+#endif
+
+	if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) {
+		ac = WME_AC_BE;
+		goto done;
+	}
+
+	/* 
+	 * If node has a vlan tag then all traffic
+	 * to it must have a matching tag.
+	 */
+	v_wme_ac = 0;
+	if (ni->ni_vlan != 0) {
+#if 1
+		IEEE80211_NODE_STAT(ni, tx_novlantag);
+		return 1;
+#else
+		 struct m_tag *mtag = VLAN_OUTPUT_TAG(ic->ic_ifp, m);
+		 if (mtag == NULL) {
+			IEEE80211_NODE_STAT(ni, tx_novlantag);
+			return 1;
+		}
+		if (EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)) !=
+		    EVL_VLANOFTAG(ni->ni_vlan)) {
+			IEEE80211_NODE_STAT(ni, tx_vlanmismatch);
+			return 1;
+		}
+		/* map vlan priority to AC */
+		switch (EVL_PRIOFTAG(ni->ni_vlan)) {
+		case 1:
+		case 2:
+			v_wme_ac = WME_AC_BK;
+			break;
+		case 0:
+		case 3:
+			v_wme_ac = WME_AC_BE;
+			break;
+		case 4:
+		case 5:
+			v_wme_ac = WME_AC_VI;
+			break;
+		case 6:
+		case 7:
+			v_wme_ac = WME_AC_VO;
+			break;
+		}
+#endif
+	}
+
+#ifdef INET
+	eh = mtod(m, struct ether_header *);
+	if (eh->ether_type == htons(ETHERTYPE_IP)) {
+		const struct ip *ip = (struct ip *)
+			(mtod(m, u_int8_t *) + sizeof (*eh));
+		/*
+		 * IP frame, map the TOS field.
+		 */
+		switch (ip->ip_tos) {
+		case 0x08:
+		case 0x20:
+			d_wme_ac = WME_AC_BK;	/* background */
+			break;
+		case 0x28:
+		case 0xa0:
+			d_wme_ac = WME_AC_VI;	/* video */
+			break;
+		case 0x30:			/* voice */
+		case 0xe0:
+		case 0x88:			/* XXX UPSD */
+		case 0xb8:
+			d_wme_ac = WME_AC_VO;
+			break;
+		default:
+			d_wme_ac = WME_AC_BE;
+			break;
+		}
+	} else {
+#endif /* INET */
+		d_wme_ac = WME_AC_BE;
+#ifdef INET
+	}
+#endif
+	/*
+	 * Use highest priority AC.
+	 */
+	if (v_wme_ac > d_wme_ac)
+		ac = v_wme_ac;
+	else
+		ac = d_wme_ac;
+
+	/*
+	 * Apply ACM policy.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		static const int acmap[4] = {
+			WME_AC_BK,	/* WME_AC_BE */
+			WME_AC_BK,	/* WME_AC_BK */
+			WME_AC_BE,	/* WME_AC_VI */
+			WME_AC_VI,	/* WME_AC_VO */
+		};
+		while (ac != WME_AC_BK &&
+		    ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm)
+			ac = acmap[ac];
+	}
+done:
+	M_WME_SETAC(m, ac);
+	return 0;
+}
+
+/*
+ * Insure there is sufficient contiguous space to encapsulate the
+ * 802.11 data frame.  If room isn't already there, arrange for it.
+ * Drivers and cipher modules assume we have done the necessary work
+ * and fail rudely if they don't find the space they need.
+ */
+static struct mbuf *
+ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
+	struct ieee80211_key *key, struct mbuf *m)
+{
+#define	TO_BE_RECLAIMED	(sizeof(struct ether_header) - sizeof(struct llc))
+	int needed_space = hdrsize;
+
+	if (key != NULL) {
+		/* XXX belongs in crypto code? */
+		needed_space += key->wk_cipher->ic_header;
+		/* XXX frags */
+	}
+	/*
+	 * We know we are called just before stripping an Ethernet
+	 * header and prepending an LLC header.  This means we know
+	 * there will be
+	 *	sizeof(struct ether_header) - sizeof(struct llc)
+	 * bytes recovered to which we need additional space for the
+	 * 802.11 header and any crypto header.
+	 */
+	/* XXX check trailing space and copy instead? */
+	if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) {
+		struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type);
+		if (n == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+			    "%s: cannot expand storage\n", __func__);
+			ic->ic_stats.is_tx_nobuf++;
+			m_freem(m);
+			return NULL;
+		}
+		KASSERT(needed_space <= MHLEN,
+		    ("not enough room, need %u got %zu\n", needed_space, MHLEN));
+		/*
+		 * Setup new mbuf to have leading space to prepend the
+		 * 802.11 header and any crypto header bits that are
+		 * required (the latter are added when the driver calls
+		 * back to ieee80211_crypto_encap to do crypto encapsulation).
+		 */
+		/* NB: must be first 'cuz it clobbers m_data */
+		m_move_pkthdr(n, m);
+		n->m_len = 0;			/* NB: m_gethdr does not set */
+		n->m_data += needed_space;
+		/*
+		 * Pull up Ethernet header to create the expected layout.
+		 * We could use m_pullup but that's overkill (i.e. we don't
+		 * need the actual data) and it cannot fail so do it inline
+		 * for speed.
+		 */
+		/* NB: struct ether_header is known to be contiguous */
+		n->m_len += sizeof(struct ether_header);
+		m->m_len -= sizeof(struct ether_header);
+		m->m_data += sizeof(struct ether_header);
+		/*
+		 * Replace the head of the chain.
+		 */
+		n->m_next = m;
+		m = n;
+	}
+	return m;
+#undef TO_BE_RECLAIMED
+}
+
+#define	KEY_UNDEFINED(k)	((k).wk_cipher == &ieee80211_cipher_none)
+/*
+ * Return the transmit key to use in sending a unicast frame.
+ * If a unicast key is set we use that.  When no unicast key is set
+ * we fall back to the default transmit key.
+ */ 
+static __inline struct ieee80211_key *
+ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	if (KEY_UNDEFINED(ni->ni_ucastkey)) {
+		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
+		    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+			return NULL;
+		return &ic->ic_nw_keys[ic->ic_def_txkey];
+	} else {
+		return &ni->ni_ucastkey;
+	}
+}
+
+/*
+ * Return the transmit key to use in sending a multicast frame.
+ * Multicast traffic always uses the group key which is installed as
+ * the default tx key.
+ */ 
+static __inline struct ieee80211_key *
+ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
+	    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+		return NULL;
+	return &ic->ic_nw_keys[ic->ic_def_txkey];
+}
+
+/*
+ * Encapsulate an outbound data frame.  The mbuf chain is updated.
+ * If an error is encountered NULL is returned.  The caller is required
+ * to provide a node reference and pullup the ethernet header in the
+ * first mbuf.
  */
 struct mbuf *
-ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
+ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
+	struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = (void *)ifp;
 	struct ether_header eh;
 	struct ieee80211_frame *wh;
-	struct ieee80211_node *ni = NULL;
+	struct ieee80211_key *key;
 	struct llc *llc;
+	int hdrsize, datalen, addqos;
 
-	if (m->m_len < sizeof(struct ether_header)) {
-		m = m_pullup(m, sizeof(struct ether_header));
-		if (m == NULL) {
-			ic->ic_stats.is_tx_nombuf++;
-			goto bad;
-		}
-	}
+	KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
 	memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
 
-	ni = ieee80211_find_txnode(ic, eh.ether_dhost);
-	if (ni == NULL) {
-		IEEE80211_DPRINTF(("%s: no node for dst %6D, discard frame\n",
-			__func__, eh.ether_dhost, ":"));
-		ic->ic_stats.is_tx_nonode++; 
+	/*
+	 * Insure space for additional headers.  First identify
+	 * transmit key to use in calculating any buffer adjustments
+	 * required.  This is also used below to do privacy
+	 * encapsulation work.  Then calculate the 802.11 header
+	 * size and any padding required by the driver.
+	 *
+	 * Note key may be NULL if we fall back to the default
+	 * transmit key and that is not set.  In that case the
+	 * buffer may not be expanded as needed by the cipher
+	 * routines, but they will/should discard it.
+	 */
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		if (ic->ic_opmode == IEEE80211_M_STA ||
+		    !IEEE80211_IS_MULTICAST(eh.ether_dhost))
+			key = ieee80211_crypto_getucastkey(ic, ni);
+		else
+			key = ieee80211_crypto_getmcastkey(ic, ni);
+		if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "[%6D] no default transmit key (%s) deftxkey %u\n",
+			    (eh.ether_dhost), ":",  __func__,
+			    ic->ic_def_txkey);
+			ic->ic_stats.is_tx_nodefkey++;
+		}
+	} else
+		key = NULL;
+	/* XXX 4-address format */
+	/*
+	 * XXX Some ap's don't handle QoS-encapsulated EAPOL
+	 * frames so suppress use.  This may be an issue if other
+	 * ap's require all data frames to be QoS-encapsulated
+	 * once negotiated in which case we'll need to make this
+	 * configurable.
+	 */
+	addqos = (ni->ni_flags & IEEE80211_NODE_QOS) &&
+		 eh.ether_type != htons(ETHERTYPE_PAE);
+	if (addqos)
+		hdrsize = sizeof(struct ieee80211_qosframe);
+	else
+		hdrsize = sizeof(struct ieee80211_frame);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		hdrsize = roundup(hdrsize, sizeof(u_int32_t));
+	m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+	if (m == NULL) {
+		/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
 		goto bad;
 	}
-	ni->ni_inact = 0;
 
+	/* NB: this could be optimized because of ieee80211_mbuf_adjust */
 	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
 	llc = mtod(m, struct llc *);
 	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
@@ -178,17 +504,16 @@
 	llc->llc_snap.org_code[1] = 0;
 	llc->llc_snap.org_code[2] = 0;
 	llc->llc_snap.ether_type = eh.ether_type;
-	M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT);
+	datalen = m->m_pkthdr.len;		/* NB: w/o 802.11 header */
+
+	M_PREPEND(m, hdrsize, MB_DONTWAIT);
 	if (m == NULL) {
-		ic->ic_stats.is_tx_nombuf++;
+		ic->ic_stats.is_tx_nobuf++;
 		goto bad;
 	}
 	wh = mtod(m, struct ieee80211_frame *);
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
-	*(uint16_t *)wh->i_dur = 0;
-	*(uint16_t *)wh->i_seq =
-	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
-	ni->ni_txseq++;
+	*(u_int16_t *)wh->i_dur = 0;
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
 		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
@@ -201,7 +526,11 @@
 		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
 		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
 		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
-		IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+		/*
+		 * NB: always use the bssid from ic_bss as the
+		 *     neighbor's may be stale after an ibss merge
+		 */
+		IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
 		break;
 	case IEEE80211_M_HOSTAP:
 		wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
@@ -212,22 +541,64 @@
 	case IEEE80211_M_MONITOR:
 		goto bad;
 	}
-	*pni = ni;
+	if (addqos) {
+		struct ieee80211_qosframe *qwh =
+			(struct ieee80211_qosframe *) wh;
+		int ac, tid;
+
+		ac = M_WME_GETAC(m);
+		/* map from access class/queue to 11e header priorty value */
+		tid = WME_AC_TO_TID(ac);
+		qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
+			qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
+		qwh->i_qos[1] = 0;
+		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+
+		*(u_int16_t *)wh->i_seq =
+		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+		ni->ni_txseqs[tid]++;
+	} else {
+		*(u_int16_t *)wh->i_seq =
+		    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+		ni->ni_txseqs[0]++;
+	}
+	if (key != NULL) {
+		/*
+		 * IEEE 802.1X: send EAPOL frames always in the clear.
+		 * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
+		 */
+		if (eh.ether_type != htons(ETHERTYPE_PAE) ||
+		    ((ic->ic_flags & IEEE80211_F_WPA) &&
+		     (ic->ic_opmode == IEEE80211_M_STA ?
+		      !KEY_UNDEFINED(*key) : !KEY_UNDEFINED(ni->ni_ucastkey)))) {
+			wh->i_fc[1] |= IEEE80211_FC1_WEP;
+			/* XXX do fragmentation */
+			if (!ieee80211_crypto_enmic(ic, key, m)) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+				    "[%6D] enmic failed, discard frame\n",
+				    (eh.ether_dhost), ":");
+				ic->ic_stats.is_crypto_enmicfail++;
+				goto bad;
+			}
+		}
+	}
+
+	IEEE80211_NODE_STAT(ni, tx_data);
+	IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
+
 	return m;
 bad:
 	if (m != NULL)
 		m_freem(m);
-	if (ni && ni != ic->ic_bss)
-		ieee80211_free_node(ic, ni);
-	*pni = NULL;
 	return NULL;
 }
 
 /*
  * Add a supported rates element id to a frame.
  */
-uint8_t *
-ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
+static u_int8_t *
+ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
 {
 	int nrates;
 
@@ -243,8 +614,8 @@
 /*
  * Add an extended supported rates element id to a frame.
  */
-uint8_t *
-ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
+static u_int8_t *
+ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
 {
 	/*
 	 * Add an extended supported rates element if operating in 11g mode.
@@ -262,8 +633,8 @@
 /* 
  * Add an ssid elemet to a frame.
  */
-static uint8_t *
-ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
+static u_int8_t *
+ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
 {
 	*frm++ = IEEE80211_ELEMID_SSID;
 	*frm++ = len;
@@ -271,18 +642,276 @@
 	return frm + len;
 }
 
-static struct mbuf *
-ieee80211_getmbuf(int flags, int type, u_int pktlen)
+/*
+ * Add an erp element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
 {
-	struct mbuf *m;
+	u_int8_t erp;
 
-	KASSERT(pktlen <= MCLBYTES, ("802.11 packet too large: %u", pktlen));
-	if (pktlen <= MHLEN)
-		MGETHDR(m, flags, type);
+	*frm++ = IEEE80211_ELEMID_ERP;
+	*frm++ = 1;
+	erp = 0;
+	if (ic->ic_nonerpsta != 0)
+		erp |= IEEE80211_ERP_NON_ERP_PRESENT;
+	if (ic->ic_flags & IEEE80211_F_USEPROT)
+		erp |= IEEE80211_ERP_USE_PROTECTION;
+	if (ic->ic_flags & IEEE80211_F_USEBARKER)
+		erp |= IEEE80211_ERP_LONG_PREAMBLE;
+	*frm++ = erp;
+	return frm;
+}
+
+static u_int8_t *
+ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie)
+{
+#define	WPA_OUI_BYTES		0x00, 0x50, 0xf2
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+#define	ADDSELECTOR(frm, sel) do {		\
+	memcpy(frm, sel, 4);			\
+	frm += 4;				\
+} while (0)
+	static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
+	static const u_int8_t cipher_suite[][4] = {
+		{ WPA_OUI_BYTES, WPA_CSE_WEP40 },	/* NB: 40-bit */
+		{ WPA_OUI_BYTES, WPA_CSE_TKIP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX WRAP */
+		{ WPA_OUI_BYTES, WPA_CSE_CCMP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
+		{ WPA_OUI_BYTES, WPA_CSE_NULL },
+	};
+	static const u_int8_t wep104_suite[4] =
+		{ WPA_OUI_BYTES, WPA_CSE_WEP104 };
+	static const u_int8_t key_mgt_unspec[4] =
+		{ WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
+	static const u_int8_t key_mgt_psk[4] =
+		{ WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	u_int8_t *frm = ie;
+	u_int8_t *selcnt;
+
+	*frm++ = IEEE80211_ELEMID_VENDOR;
+	*frm++ = 0;				/* length filled in below */
+	memcpy(frm, oui, sizeof(oui));		/* WPA OUI */
+	frm += sizeof(oui);
+	ADDSHORT(frm, WPA_VERSION);
+
+	/* XXX filter out CKIP */
+
+	/* multicast cipher */
+	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
+	    rsn->rsn_mcastkeylen >= 13)
+		ADDSELECTOR(frm, wep104_suite);
 	else
-		m = m_getcl(flags, type, M_PKTHDR);
-	return m;
+		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
+
+	/* unicast cipher list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
+	}
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
+	}
+
+	/* authenticator selector list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_unspec);
+	}
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_psk);
+	}
+
+	/* optional capabilities */
+	if (rsn->rsn_caps != 0)
+		ADDSHORT(frm, rsn->rsn_caps);
+
+	/* calculate element length */
+	ie[1] = frm - ie - 2;
+	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
+		("WPA IE too big, %u > %zu",
+		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+	return frm;
+#undef ADDSHORT
+#undef ADDSELECTOR
+#undef WPA_OUI_BYTES
+}
+
+static u_int8_t *
+ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie)
+{
+#define	RSN_OUI_BYTES		0x00, 0x0f, 0xac
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+#define	ADDSELECTOR(frm, sel) do {		\
+	memcpy(frm, sel, 4);			\
+	frm += 4;				\
+} while (0)
+	static const u_int8_t cipher_suite[][4] = {
+		{ RSN_OUI_BYTES, RSN_CSE_WEP40 },	/* NB: 40-bit */
+		{ RSN_OUI_BYTES, RSN_CSE_TKIP },
+		{ RSN_OUI_BYTES, RSN_CSE_WRAP },
+		{ RSN_OUI_BYTES, RSN_CSE_CCMP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
+		{ RSN_OUI_BYTES, RSN_CSE_NULL },
+	};
+	static const u_int8_t wep104_suite[4] =
+		{ RSN_OUI_BYTES, RSN_CSE_WEP104 };
+	static const u_int8_t key_mgt_unspec[4] =
+		{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
+	static const u_int8_t key_mgt_psk[4] =
+		{ RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	u_int8_t *frm = ie;
+	u_int8_t *selcnt;
+
+	*frm++ = IEEE80211_ELEMID_RSN;
+	*frm++ = 0;				/* length filled in below */
+	ADDSHORT(frm, RSN_VERSION);
+
+	/* XXX filter out CKIP */
+
+	/* multicast cipher */
+	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
+	    rsn->rsn_mcastkeylen >= 13)
+		ADDSELECTOR(frm, wep104_suite);
+	else
+		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
+
+	/* unicast cipher list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
+	}
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
+	}
+
+	/* authenticator selector list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_unspec);
+	}
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_psk);
+	}
+
+	/* optional capabilities */
+	if (rsn->rsn_caps != 0)
+		ADDSHORT(frm, rsn->rsn_caps);
+	/* XXX PMKID */
+
+	/* calculate element length */
+	ie[1] = frm - ie - 2;
+	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
+		("RSN IE too big, %u > %zu",
+		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+	return frm;
+#undef ADDSELECTOR
+#undef ADDSHORT
+#undef RSN_OUI_BYTES
+}
+
+/*
+ * Add a WPA/RSN element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic)
+{
+
+	KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
+	if (ic->ic_flags & IEEE80211_F_WPA2)
+		frm = ieee80211_setup_rsn_ie(ic, frm);
+	if (ic->ic_flags & IEEE80211_F_WPA1)
+		frm = ieee80211_setup_wpa_ie(ic, frm);
+	return frm;
+}
+
+#define	WME_OUI_BYTES		0x00, 0x50, 0xf2
+/*
+ * Add a WME information element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme)
+{
+	static const struct ieee80211_wme_info info = {
+		.wme_id		= IEEE80211_ELEMID_VENDOR,
+		.wme_len	= sizeof(struct ieee80211_wme_info) - 2,
+		.wme_oui	= { WME_OUI_BYTES },
+		.wme_type	= WME_OUI_TYPE,
+		.wme_subtype	= WME_INFO_OUI_SUBTYPE,
+		.wme_version	= WME_VERSION,
+		.wme_info	= 0,
+	};
+	memcpy(frm, &info, sizeof(info));
+	return frm + sizeof(info); 
+}
+
+/*
+ * Add a WME parameters element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme)
+{
+#define	SM(_v, _f)	(((_v) << _f##_S) & _f)
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+	/* NB: this works 'cuz a param has an info at the front */
+	static const struct ieee80211_wme_info param = {
+		.wme_id		= IEEE80211_ELEMID_VENDOR,
+		.wme_len	= sizeof(struct ieee80211_wme_param) - 2,
+		.wme_oui	= { WME_OUI_BYTES },
+		.wme_type	= WME_OUI_TYPE,
+		.wme_subtype	= WME_PARAM_OUI_SUBTYPE,
+		.wme_version	= WME_VERSION,
+	};
+	int i;
+
+	memcpy(frm, &param, sizeof(param));
+	frm += __offsetof(struct ieee80211_wme_info, wme_info);
+	*frm++ = wme->wme_bssChanParams.cap_info;	/* AC info */
+	*frm++ = 0;					/* reserved field */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		const struct wmeParams *ac =
+		       &wme->wme_bssChanParams.cap_wmeParams[i];
+		*frm++ = SM(i, WME_PARAM_ACI)
+		       | SM(ac->wmep_acm, WME_PARAM_ACM)
+		       | SM(ac->wmep_aifsn, WME_PARAM_AIFSN)
+		       ;
+		*frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
+		       | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN)
+		       ;
+		ADDSHORT(frm, ac->wmep_txopLimit);
+	}
+	return frm;
+#undef SM
+#undef ADDSHORT
 }
+#undef WME_OUI_BYTES
 
 /*
  * Send a management frame.  The node is for the destination (or ic_bss
@@ -294,12 +923,11 @@
 	int type, int arg)
 {
 #define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
-	struct ifnet *ifp = &ic->ic_if;
 	struct mbuf *m;
-	uint8_t *frm;
+	u_int8_t *frm;
 	enum ieee80211_phymode mode;
-	uint16_t capinfo;
-	int ret, timer;
+	u_int16_t capinfo;
+	int has_challenge, is_shared_key, ret, timer, status;
 
 	KASSERT(ni != NULL, ("null node"));
 
@@ -308,8 +936,13 @@
 	 * the xmit is complete all the way in the driver.  On error we
 	 * will remove our reference.
 	 */
-	if (ni != ic->ic_bss)
-		ieee80211_ref_node(ni);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n",
+		__func__, __LINE__,
+		ni, (ni->ni_macaddr), ":",
+		ieee80211_node_refcnt(ni)+1);
+	ieee80211_ref_node(ni);
+
 	timer = 0;
 	switch (type) {
 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
@@ -318,22 +951,30 @@
 		 *	[tlv] ssid
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] user-specified ie's
 		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 2 + ic->ic_des_esslen
+		m = ieee80211_getmgtframe(&frm,
+			 2 + IEEE80211_NWID_LEN
 		       + 2 + IEEE80211_RATE_SIZE
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
+			senderr(ENOMEM, is_tx_nobuf);
+
 		frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
 		mode = ieee80211_chan2mode(ic, ni->ni_chan);
 		frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
 		frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		if (ic->ic_opt_ie != NULL) {
+			memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
+			frm += ic->ic_opt_ie_len;
+		}
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 
-		timer = IEEE80211_TRANS_WAIT;
+		IEEE80211_NODE_STAT(ni, tx_probereq);
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			timer = IEEE80211_TRANS_WAIT;
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
@@ -346,39 +987,50 @@
 		 *	[tlv] supported rates
 		 *	[tlv] parameter set (FH/DS)
 		 *	[tlv] parameter set (IBSS)
+		 *	[tlv] extended rate phy (ERP)
 		 *	[tlv] extended supported rates
+		 *	[tlv] WPA
+		 *	[tlv] WME (optional)
 		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 8 + 2 + 2 + 2
-		       + 2 + ni->ni_esslen
+		m = ieee80211_getmgtframe(&frm,
+			 8
+		       + sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
+		       + 2 + IEEE80211_NWID_LEN
 		       + 2 + IEEE80211_RATE_SIZE
-		       + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3)
+		       + 7	/* max(7,3) */
 		       + 6
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		       + 3
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       /* XXX !WPA1+WPA2 fits w/o a cluster */
+		       + (ic->ic_flags & IEEE80211_F_WPA ?
+				2*sizeof(struct ieee80211_ie_wpa) : 0)
+		       + sizeof(struct ieee80211_wme_param)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
+			senderr(ENOMEM, is_tx_nobuf);
 
 		memset(frm, 0, 8);	/* timestamp should be filled later */
 		frm += 8;
-		*(uint16_t *)frm = htole16(ic->ic_bss->ni_intval);
+		*(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
 		frm += 2;
 		if (ic->ic_opmode == IEEE80211_M_IBSS)
 			capinfo = IEEE80211_CAPINFO_IBSS;
 		else
 			capinfo = IEEE80211_CAPINFO_ESS;
-		if (ic->ic_flags & IEEE80211_F_WEPON)
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
 			capinfo |= IEEE80211_CAPINFO_PRIVACY;
 		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
 		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-		*(uint16_t *)frm = htole16(capinfo);
+		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+		*(u_int16_t *)frm = htole16(capinfo);
 		frm += 2;
 
 		frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
 				ic->ic_bss->ni_esslen);
-		frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
+		frm = ieee80211_add_rates(frm, &ni->ni_rates);
 
 		if (ic->ic_phytype == IEEE80211_T_FH) {
                         *frm++ = IEEE80211_ELEMID_FHPARMS;
@@ -400,44 +1052,99 @@
 			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
 			*frm++ = 2;
 			*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
-		} else {	/* IEEE80211_M_HOSTAP */
-			/* TODO: TIM */
-			*frm++ = IEEE80211_ELEMID_TIM;
-			*frm++ = 4;	/* length */
-			*frm++ = 0;	/* DTIM count */
-			*frm++ = 1;	/* DTIM period */
-			*frm++ = 0;	/* bitmap control */
-			*frm++ = 0;	/* Partial Virtual Bitmap (variable length) */
 		}
-		frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			frm = ieee80211_add_wpa(frm, ic);
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			frm = ieee80211_add_erp(frm, ic);
+		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+		if (ic->ic_flags & IEEE80211_F_WME)
+			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_AUTH:
-		MGETHDR(m, MB_DONTWAIT, MT_DATA);
+		status = arg >> 16;
+		arg &= 0xffff;
+		has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
+		    arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
+		    ni->ni_challenge != NULL);
+
+		/*
+		 * Deduce whether we're doing open authentication or
+		 * shared key authentication.  We do the latter if
+		 * we're in the middle of a shared key authentication
+		 * handshake or if we're initiating an authentication
+		 * request and configured to use shared key.
+		 */
+		is_shared_key = has_challenge ||
+		     arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
+		     (arg == IEEE80211_AUTH_SHARED_REQUEST &&
+		      ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
+
+		m = ieee80211_getmgtframe(&frm,
+			  3 * sizeof(u_int16_t)
+			+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
+				sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		MH_ALIGN(m, 2 * 3);
-		m->m_pkthdr.len = m->m_len = 6;
-		frm = mtod(m, uint8_t *);
-		/* TODO: shared key auth */
-		((uint16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN);
-		((uint16_t *)frm)[1] = htole16(arg);	/* sequence number */
-		((uint16_t *)frm)[2] = 0;		/* status */
+			senderr(ENOMEM, is_tx_nobuf);
+
+		((u_int16_t *)frm)[0] =
+		    (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED)
+		                    : htole16(IEEE80211_AUTH_ALG_OPEN);
+		((u_int16_t *)frm)[1] = htole16(arg);	/* sequence number */
+		((u_int16_t *)frm)[2] = htole16(status);/* status */
+
+		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
+			((u_int16_t *)frm)[3] =
+			    htole16((IEEE80211_CHALLENGE_LEN << 8) |
+			    IEEE80211_ELEMID_CHALLENGE);
+			memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
+			    IEEE80211_CHALLENGE_LEN);
+			m->m_pkthdr.len = m->m_len =
+				4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN;
+			if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				    "[%6D] request encrypt frame (%s)\n",
+				    (ni->ni_macaddr), ":", __func__);
+				m->m_flags |= M_LINK0; /* WEP-encrypt, please */
+			}
+		} else
+			m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t);
+
+		/* XXX not right for shared key */
+		if (status == IEEE80211_STATUS_SUCCESS)
+			IEEE80211_NODE_STAT(ni, tx_auth);
+		else
+			IEEE80211_NODE_STAT(ni, tx_auth_fail);
+
+		/*
+		 * When 802.1x is not in use mark the port
+		 * authorized at this point so traffic can flow.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		    status == IEEE80211_STATUS_SUCCESS &&
+		    ni->ni_authmode != IEEE80211_AUTH_8021X)
+			ieee80211_node_authorize(ic, ni);
 		if (ic->ic_opmode == IEEE80211_M_STA)
 			timer = IEEE80211_TRANS_WAIT;
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "station %6D deauthenticate (reason %d)\n",
-			    ni->ni_macaddr, ":", arg);
-		MGETHDR(m, MB_DONTWAIT, MT_DATA);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			"[%6D] send station deauthenticate (reason %d)\n",
+			(ni->ni_macaddr), ":", arg);
+		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		MH_ALIGN(m, 2);
-		m->m_pkthdr.len = m->m_len = 2;
-		*mtod(m, uint16_t *) = htole16(arg);	/* reason */
+			senderr(ENOMEM, is_tx_nobuf);
+		*(u_int16_t *)frm = htole16(arg);	/* reason */
+		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+
+		IEEE80211_NODE_STAT(ni, tx_deauth);
+		IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
+
+		ieee80211_node_unauthorize(ic, ni);	/* port closed */
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
@@ -450,25 +1157,28 @@
 		 *	[tlv] ssid
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WME
+		 *	[tlv] user-specified ie's
 		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 sizeof(capinfo)
-		       + sizeof(uint16_t)
+		m = ieee80211_getmgtframe(&frm,
+			 sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
 		       + IEEE80211_ADDR_LEN
-		       + 2 + ni->ni_esslen
+		       + 2 + IEEE80211_NWID_LEN
 		       + 2 + IEEE80211_RATE_SIZE
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + sizeof(struct ieee80211_wme_info)
+		       + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
+			senderr(ENOMEM, is_tx_nobuf);
 
 		capinfo = 0;
 		if (ic->ic_opmode == IEEE80211_M_IBSS)
 			capinfo |= IEEE80211_CAPINFO_IBSS;
 		else		/* IEEE80211_M_STA */
 			capinfo |= IEEE80211_CAPINFO_ESS;
-		if (ic->ic_flags & IEEE80211_F_WEPON)
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
 			capinfo |= IEEE80211_CAPINFO_PRIVACY;
 		/*
 		 * NB: Some 11a AP's reject the request when
@@ -477,12 +1187,13 @@
 		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
 		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
+		    (ic->ic_caps & IEEE80211_C_SHSLOT))
 			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
-		*(uint16_t *)frm = htole16(capinfo);
+		*(u_int16_t *)frm = htole16(capinfo);
 		frm += 2;
 
-		*(uint16_t *)frm = htole16(ic->ic_lintval);
+		*(u_int16_t *)frm = htole16(ic->ic_lintval);
 		frm += 2;
 
 		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
@@ -493,7 +1204,13 @@
 		frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
 		frm = ieee80211_add_rates(frm, &ni->ni_rates);
 		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+			frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+		if (ic->ic_opt_ie != NULL) {
+			memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
+			frm += ic->ic_opt_ie_len;
+		}
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 
 		timer = IEEE80211_TRANS_WAIT;
 		break;
@@ -507,67 +1224,397 @@
 		 *	[2] association ID
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WME (if enabled and STA enabled)
 		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 sizeof(capinfo)
-		       + sizeof(uint16_t)
-		       + sizeof(uint16_t)
+		m = ieee80211_getmgtframe(&frm,
+			 sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
 		       + 2 + IEEE80211_RATE_SIZE
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + sizeof(struct ieee80211_wme_param)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
+			senderr(ENOMEM, is_tx_nobuf);
 
 		capinfo = IEEE80211_CAPINFO_ESS;
-		if (ic->ic_flags & IEEE80211_F_WEPON)
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
 			capinfo |= IEEE80211_CAPINFO_PRIVACY;
 		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
 		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-		*(uint16_t *)frm = htole16(capinfo);
+		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+		*(u_int16_t *)frm = htole16(capinfo);
 		frm += 2;
 
-		*(uint16_t *)frm = htole16(arg);	/* status */
+		*(u_int16_t *)frm = htole16(arg);	/* status */
 		frm += 2;
 
-		if (arg == IEEE80211_STATUS_SUCCESS)
-			*(uint16_t *)frm = htole16(ni->ni_associd);
+		if (arg == IEEE80211_STATUS_SUCCESS) {
+			*(u_int16_t *)frm = htole16(ni->ni_associd);
+			IEEE80211_NODE_STAT(ni, tx_assoc);
+		} else
+			IEEE80211_NODE_STAT(ni, tx_assoc_fail);
 		frm += 2;
 
 		frm = ieee80211_add_rates(frm, &ni->ni_rates);
 		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "station %6D disassociate (reason %d)\n",
-			    ni->ni_macaddr, ":", arg);
-		MGETHDR(m, MB_DONTWAIT, MT_DATA);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			"[%6D] send station disassociate (reason %d)\n",
+			(ni->ni_macaddr), ":", arg);
+		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		MH_ALIGN(m, 2);
-		m->m_pkthdr.len = m->m_len = 2;
-		*mtod(m, uint16_t *) = htole16(arg);	/* reason */
+			senderr(ENOMEM, is_tx_nobuf);
+		*(u_int16_t *)frm = htole16(arg);	/* reason */
+		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+
+		IEEE80211_NODE_STAT(ni, tx_disassoc);
+		IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg);
 		break;
 
 	default:
-		IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n",
-			__func__, type));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"[%6D] invalid mgmt frame type %u\n",
+			(ni->ni_macaddr), ":", type);
 		senderr(EINVAL, is_tx_unknownmgt);
 		/* NOTREACHED */
 	}
 
-	ret = ieee80211_mgmt_output(ifp, ni, m, type);
+	ret = ieee80211_mgmt_output(ic, ni, m, type);
 	if (ret == 0) {
 		if (timer)
 			ic->ic_mgt_timer = timer;
 	} else {
 bad:
-		if (ni != ic->ic_bss)		/* remove ref we added */
-			ieee80211_free_node(ic, ni);
+		ieee80211_free_node(ni);
 	}
 	return ret;
 #undef senderr
 }
+
+/*
+ * Allocate a beacon frame and fillin the appropriate bits.
+ */
+struct mbuf *
+ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct ieee80211_beacon_offsets *bo)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ieee80211_frame *wh;
+	struct mbuf *m;
+	int pktlen;
+	u_int8_t *frm, *efrm;
+	u_int16_t capinfo;
+	struct ieee80211_rateset *rs;
+
+	/*
+	 * beacon frame format
+	 *	[8] time stamp
+	 *	[2] beacon interval
+	 *	[2] cabability information
+	 *	[tlv] ssid
+	 *	[tlv] supported rates
+	 *	[3] parameter set (DS)
+	 *	[tlv] parameter set (IBSS/TIM)
+	 *	[tlv] extended rate phy (ERP)
+	 *	[tlv] extended supported rates
+	 *	[tlv] WME parameters
+	 *	[tlv] WPA/RSN parameters
+	 * XXX Vendor-specific OIDs (e.g. Atheros)
+	 * NB: we allocate the max space required for the TIM bitmap.
+	 */
+	rs = &ni->ni_rates;
+	pktlen =   8					/* time stamp */
+		 + sizeof(u_int16_t)			/* beacon interval */
+		 + sizeof(u_int16_t)			/* capabilities */
+		 + 2 + ni->ni_esslen			/* ssid */
+	         + 2 + IEEE80211_RATE_SIZE		/* supported rates */
+	         + 2 + 1				/* DS parameters */
+		 + 2 + 4 + ic->ic_tim_len		/* DTIM/IBSSPARMS */
+		 + 2 + 1				/* ERP */
+	         + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		 + (ic->ic_caps & IEEE80211_C_WME ?	/* WME */
+			sizeof(struct ieee80211_wme_param) : 0)
+		 + (ic->ic_caps & IEEE80211_C_WPA ?	/* WPA 1+2 */
+			2*sizeof(struct ieee80211_ie_wpa) : 0)
+		 ;
+	m = ieee80211_getmgtframe(&frm, pktlen);
+	if (m == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: cannot get buf; size %u\n", __func__, pktlen);
+		ic->ic_stats.is_tx_nobuf++;
+		return NULL;
+	}
+
+	memset(frm, 0, 8);	/* XXX timestamp is set by hardware/driver */
+	frm += 8;
+	*(u_int16_t *)frm = htole16(ni->ni_intval);
+	frm += 2;
+	if (ic->ic_opmode == IEEE80211_M_IBSS)
+		capinfo = IEEE80211_CAPINFO_IBSS;
+	else
+		capinfo = IEEE80211_CAPINFO_ESS;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+	bo->bo_caps = (u_int16_t *)frm;
+	*(u_int16_t *)frm = htole16(capinfo);
+	frm += 2;
+	*frm++ = IEEE80211_ELEMID_SSID;
+	if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
+		*frm++ = ni->ni_esslen;
+		memcpy(frm, ni->ni_essid, ni->ni_esslen);
+		frm += ni->ni_esslen;
+	} else
+		*frm++ = 0;
+	frm = ieee80211_add_rates(frm, rs);
+	if (ic->ic_curmode != IEEE80211_MODE_FH) {
+		*frm++ = IEEE80211_ELEMID_DSPARMS;
+		*frm++ = 1;
+		*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+	}
+	bo->bo_tim = frm;
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
+		*frm++ = 2;
+		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
+		bo->bo_tim_len = 0;
+	} else {
+		struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm;
+
+		tie->tim_ie = IEEE80211_ELEMID_TIM;
+		tie->tim_len = 4;	/* length */
+		tie->tim_count = 0;	/* DTIM count */ 
+		tie->tim_period = ic->ic_dtim_period;	/* DTIM period */
+		tie->tim_bitctl = 0;	/* bitmap control */
+		tie->tim_bitmap[0] = 0;	/* Partial Virtual Bitmap */
+		frm += sizeof(struct ieee80211_tim_ie);
+		bo->bo_tim_len = 1;
+	}
+	bo->bo_trailer = frm;
+	if (ic->ic_flags & IEEE80211_F_WME) {
+		bo->bo_wme = frm;
+		frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+		ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
+	}
+	if (ic->ic_flags & IEEE80211_F_WPA)
+		frm = ieee80211_add_wpa(frm, ic);
+	if (ic->ic_curmode == IEEE80211_MODE_11G)
+		frm = ieee80211_add_erp(frm, ic);
+	efrm = ieee80211_add_xrates(frm, rs);
+	bo->bo_trailer_len = efrm - bo->bo_trailer;
+	m->m_pkthdr.len = m->m_len = efrm - mtod(m, u_int8_t *);
+
+	M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT);
+	KASSERT(m != NULL, ("no space for 802.11 header?"));
+	wh = mtod(m, struct ieee80211_frame *);
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
+	    IEEE80211_FC0_SUBTYPE_BEACON;
+	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+	*(u_int16_t *)wh->i_dur = 0;
+	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+	*(u_int16_t *)wh->i_seq = 0;
+
+	return m;
+}
+
+/*
+ * Update the dynamic parts of a beacon frame based on the current state.
+ */
+int
+ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
+{
+	int len_changed = 0;
+	u_int16_t capinfo;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_BEACON_LOCK(ic);
+	/* XXX faster to recalculate entirely or just changes? */
+	if (ic->ic_opmode == IEEE80211_M_IBSS)
+		capinfo = IEEE80211_CAPINFO_IBSS;
+	else
+		capinfo = IEEE80211_CAPINFO_ESS;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+	*bo->bo_caps = htole16(capinfo);
+
+	if (ic->ic_flags & IEEE80211_F_WME) {
+		struct ieee80211_wme_state *wme = &ic->ic_wme;
+
+		/*
+		 * Check for agressive mode change.  When there is
+		 * significant high priority traffic in the BSS
+		 * throttle back BE traffic by using conservative
+		 * parameters.  Otherwise BE uses agressive params
+		 * to optimize performance of legacy/non-QoS traffic.
+		 */
+		if (wme->wme_flags & WME_F_AGGRMODE) {
+			if (wme->wme_hipri_traffic >
+			    wme->wme_hipri_switch_thresh) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+				    "%s: traffic %u, disable aggressive mode\n",
+				    __func__, wme->wme_hipri_traffic);
+				wme->wme_flags &= ~WME_F_AGGRMODE;
+				ieee80211_wme_updateparams_locked(ic);
+				wme->wme_hipri_traffic =
+					wme->wme_hipri_switch_hysteresis;
+			} else
+				wme->wme_hipri_traffic = 0;
+		} else {
+			if (wme->wme_hipri_traffic <=
+			    wme->wme_hipri_switch_thresh) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+				    "%s: traffic %u, enable aggressive mode\n",
+				    __func__, wme->wme_hipri_traffic);
+				wme->wme_flags |= WME_F_AGGRMODE;
+				ieee80211_wme_updateparams_locked(ic);
+				wme->wme_hipri_traffic = 0;
+			} else
+				wme->wme_hipri_traffic =
+					wme->wme_hipri_switch_hysteresis;
+		}
+		if (ic->ic_flags & IEEE80211_F_WMEUPDATE) {
+			(void) ieee80211_add_wme_param(bo->bo_wme, wme);
+			ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
+		}
+	}
+
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {	/* NB: no IBSS support*/
+		struct ieee80211_tim_ie *tie =
+			(struct ieee80211_tim_ie *) bo->bo_tim;
+		if (ic->ic_flags & IEEE80211_F_TIMUPDATE) {
+			u_int timlen, timoff, i;
+			/* 
+			 * ATIM/DTIM needs updating.  If it fits in the
+			 * current space allocated then just copy in the
+			 * new bits.  Otherwise we need to move any trailing
+			 * data to make room.  Note that we know there is
+			 * contiguous space because ieee80211_beacon_allocate
+			 * insures there is space in the mbuf to write a
+			 * maximal-size virtual bitmap (based on ic_max_aid).
+			 */
+			/*
+			 * Calculate the bitmap size and offset, copy any
+			 * trailer out of the way, and then copy in the
+			 * new bitmap and update the information element.
+			 * Note that the tim bitmap must contain at least
+			 * one byte and any offset must be even.
+			 */
+			if (ic->ic_ps_pending != 0) {
+				timoff = 128;		/* impossibly large */
+				for (i = 0; i < ic->ic_tim_len; i++)
+					if (ic->ic_tim_bitmap[i]) {
+						timoff = i &~ 1;
+						break;
+					}
+				KASSERT(timoff != 128, ("tim bitmap empty!"));
+				for (i = ic->ic_tim_len-1; i >= timoff; i--)
+					if (ic->ic_tim_bitmap[i])
+						break;
+				timlen = 1 + (i - timoff);
+			} else {
+				timoff = 0;
+				timlen = 1;
+			}
+			if (timlen != bo->bo_tim_len) {
+				/* copy up/down trailer */
+				ovbcopy(bo->bo_trailer, tie->tim_bitmap+timlen,
+					bo->bo_trailer_len);
+				bo->bo_trailer = tie->tim_bitmap+timlen;
+				bo->bo_wme = bo->bo_trailer;
+				bo->bo_tim_len = timlen;
+
+				/* update information element */
+				tie->tim_len = 3 + timlen;
+				tie->tim_bitctl = timoff;
+				len_changed = 1;
+			}
+			memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
+				bo->bo_tim_len);
+
+			ic->ic_flags &= ~IEEE80211_F_TIMUPDATE;
+
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+				"%s: TIM updated, pending %u, off %u, len %u\n",
+				__func__, ic->ic_ps_pending, timoff, timlen);
+		}
+		/* count down DTIM period */
+		if (tie->tim_count == 0)
+			tie->tim_count = tie->tim_period - 1;
+		else
+			tie->tim_count--;
+		/* update state for buffered multicast frames on DTIM */
+		if (mcast && (tie->tim_count == 1 || tie->tim_period == 1))
+			tie->tim_bitctl |= 1;
+		else
+			tie->tim_bitctl &= ~1;
+	}
+	IEEE80211_BEACON_UNLOCK(ic);
+
+	return len_changed;
+}
+
+/*
+ * Save an outbound packet for a node in power-save sleep state.
+ * The new packet is placed on the node's saved queue, and the TIM
+ * is changed, if necessary.
+ */
+void
+ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, 
+		  struct mbuf *m)
+{
+	int qlen, age;
+
+	IEEE80211_NODE_SAVEQ_LOCK(ni);
+	if (IF_QFULL(&ni->ni_savedq)) {
+		IF_DROP(&ni->ni_savedq);
+		IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"[%6D] pwr save q overflow, drops %d (size %d)\n",
+			(ni->ni_macaddr),  ":",
+			ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
+#ifdef IEEE80211_DEBUG
+		if (ieee80211_msg_dumppkts(ic))
+			ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1);
+#endif
+		m_freem(m);
+		return;
+	}
+	/*
+	 * Tag the frame with it's expiry time and insert
+	 * it in the queue.  The aging interval is 4 times
+	 * the listen interval specified by the station. 
+	 * Frames that sit around too long are reclaimed
+	 * using this information.
+	 */
+	/* XXX handle overflow? */
+	age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024; /* TU -> secs */
+	_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
+	IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		"[%6D] save frame, %u now queued\n",
+		(ni->ni_macaddr), ":", qlen);
+
+	if (qlen == 1)
+		ic->ic_set_tim(ic, ni, 1);
+}
Index: src/sys/netproto/802_11/ieee80211_proto.c
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_proto.c,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_proto.c
--- src/sys/netproto/802_11/ieee80211_proto.c	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_proto.c	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,11 +28,10 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.8 2004/04/02 20:22:25 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 /*
  * IEEE 802.11 protocol support.
  */
@@ -40,35 +39,20 @@
 #include "opt_inet.h"
 
 #include <sys/param.h>
-#include <sys/systm.h> 
-#include <sys/mbuf.h>   
-#include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/systm.h> 
+ 
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
+#include <sys/thread.h>
 
-#include <machine/atomic.h>
- 
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
-#include <net/bpf.h>
-
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
+/* XXX tunables */
+#define	AGGRESSIVE_MODE_SWITCH_HYSTERESIS	3	/* pkts / 100ms */
+#define	HIGH_PRI_SWITCH_THRESH			10	/* pkts / 100ms */
 
 #define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
 
@@ -78,6 +62,12 @@
 	"beacon",	"atim",		"disassoc",	"auth",
 	"deauth",	"reserved#13",	"reserved#14",	"reserved#15"
 };
+const char *ieee80211_ctl_subtype_name[] = {
+	"reserved#0",	"reserved#1",	"reserved#2",	"reserved#3",
+	"reserved#3",	"reserved#5",	"reserved#6",	"reserved#7",
+	"reserved#8",	"reserved#9",	"ps_poll",	"rts",
+	"cts",		"ack",		"cf_end",	"cf_end_ack"
+};
 const char *ieee80211_state_name[IEEE80211_S_MAX] = {
 	"INIT",		/* IEEE80211_S_INIT */
 	"SCAN",		/* IEEE80211_S_SCAN */
@@ -85,15 +75,23 @@
 	"ASSOC",	/* IEEE80211_S_ASSOC */
 	"RUN"		/* IEEE80211_S_RUN */
 };
+const char *ieee80211_wme_acnames[] = {
+	"WME_AC_BE",
+	"WME_AC_BK",
+	"WME_AC_VI",
+	"WME_AC_VO",
+	"WME_UPSD",
+};
 
 static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
 
 void
-ieee80211_proto_attach(struct ifnet *ifp)
+ieee80211_proto_attach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 
-	ifp->if_hdrlen = sizeof(struct ieee80211_frame);
+	/* XXX room for crypto  */
+	ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4);
 
 #ifdef notdef
 	ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
@@ -103,6 +101,12 @@
 	ic->ic_fragthreshold = 2346;		/* XXX not used yet */
 	ic->ic_fixed_rate = -1;			/* no fixed rate */
 	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
+	ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+	ic->ic_wme.wme_hipri_switch_hysteresis =
+		AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
+
+	lwkt_token_init(&ic->ic_mgtq.ifq_mtx);
 
 	/* protocol state change handler */
 	ic->ic_newstate = ieee80211_newstate;
@@ -113,18 +117,125 @@
 }
 
 void
-ieee80211_proto_detach(struct ifnet *ifp)
+ieee80211_proto_detach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+
+	/*
+	 * This should not be needed as we detach when reseting
+	 * the state but be conservative here since the
+	 * authenticator may do things like spawn kernel threads.
+	 */
+	if (ic->ic_auth->ia_detach)
+		ic->ic_auth->ia_detach(ic);
 
 	IF_DRAIN(&ic->ic_mgtq);
+	lwkt_token_uninit(&ic->ic_mgtq.ifq_mtx);
+
+	/*
+	 * Detach any ACL'ator.
+	 */
+	if (ic->ic_acl != NULL)
+		ic->ic_acl->iac_detach(ic);
+}
+
+/*
+ * Simple-minded authenticator module support.
+ */
+
+#define	IEEE80211_AUTH_MAX	(IEEE80211_AUTH_WPA+1)
+/* XXX well-known names */
+static const char *auth_modnames[IEEE80211_AUTH_MAX] = {
+	"wlan_internal",	/* IEEE80211_AUTH_NONE */
+	"wlan_internal",	/* IEEE80211_AUTH_OPEN */
+	"wlan_internal",	/* IEEE80211_AUTH_SHARED */
+	"wlan_xauth",		/* IEEE80211_AUTH_8021X	 */
+	"wlan_internal",	/* IEEE80211_AUTH_AUTO */
+	"wlan_xauth",		/* IEEE80211_AUTH_WPA */
+};
+static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX];
+
+static const struct ieee80211_authenticator auth_internal = {
+	.ia_name		= "wlan_internal",
+	.ia_attach		= NULL,
+	.ia_detach		= NULL,
+	.ia_node_join		= NULL,
+	.ia_node_leave		= NULL,
+};
+
+/*
+ * Setup internal authenticators once; they are never unregistered.
+ */
+static void
+ieee80211_auth_setup(void)
+{
+	ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal);
+	ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal);
+	ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal);
+}
+SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL);
+
+const struct ieee80211_authenticator *
+ieee80211_authenticator_get(int auth)
+{
+	if (auth >= IEEE80211_AUTH_MAX)
+		return NULL;
+	if (authenticators[auth] == NULL)
+		ieee80211_load_module(auth_modnames[auth]);
+	return authenticators[auth];
 }
 
 void
-ieee80211_print_essid(uint8_t *essid, int len)
+ieee80211_authenticator_register(int type,
+	const struct ieee80211_authenticator *auth)
 {
+	if (type >= IEEE80211_AUTH_MAX)
+		return;
+	authenticators[type] = auth;
+}
+
+void
+ieee80211_authenticator_unregister(int type)
+{
+
+	if (type >= IEEE80211_AUTH_MAX)
+		return;
+	authenticators[type] = NULL;
+}
+
+/*
+ * Very simple-minded ACL module support.
+ */
+/* XXX just one for now */
+static	const struct ieee80211_aclator *acl = NULL;
+
+void
+ieee80211_aclator_register(const struct ieee80211_aclator *iac)
+{
+	printf("wlan: %s acl policy registered\n", iac->iac_name);
+	acl = iac;
+}
+
+void
+ieee80211_aclator_unregister(const struct ieee80211_aclator *iac)
+{
+	if (acl == iac)
+		acl = NULL;
+	printf("wlan: %s acl policy unregistered\n", iac->iac_name);
+}
+
+const struct ieee80211_aclator *
+ieee80211_aclator_get(const char *name)
+{
+	if (acl == NULL)
+		ieee80211_load_module("wlan_acl");
+	return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL;
+}
+
+void
+ieee80211_print_essid(const u_int8_t *essid, int len)
+{
+	const u_int8_t *p; 
 	int i;
-	uint8_t *p; 
 
 	if (len > IEEE80211_NWID_LEN)
 		len = IEEE80211_NWID_LEN;
@@ -146,28 +257,33 @@
 }
 
 void
-ieee80211_dump_pkt(uint8_t *buf, int len, int rate, int rssi)
+ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi)
 {
-	struct ieee80211_frame *wh;
+	const struct ieee80211_frame *wh;
 	int i;
 
-	wh = (struct ieee80211_frame *)buf;
+	wh = (const struct ieee80211_frame *)buf;
 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
 	case IEEE80211_FC1_DIR_NODS:
-		printf("NODS %6D->%6D(%6D)", wh->i_addr2, ":",
-		       wh->i_addr1, ":", wh->i_addr3, ":");
+		printf("NODS %6D", (wh->i_addr2), ":");
+		printf("->%6D", (wh->i_addr1), ":");
+		printf("(%6D)", (wh->i_addr3), ":");
 		break;
 	case IEEE80211_FC1_DIR_TODS:
-		printf("TODS %6D->%6D(%6D)", wh->i_addr2, ":",
-		       wh->i_addr3, ":", wh->i_addr1, ":");
+		printf("TODS %6D", (wh->i_addr2), ":");
+		printf("->%6D", (wh->i_addr3), ":");
+		printf("(%6D)", (wh->i_addr1), ":");
 		break;
 	case IEEE80211_FC1_DIR_FROMDS:
-		printf("FRDS %6D->%6D(%6D)", wh->i_addr3, ":",
-		       wh->i_addr1, ":", wh->i_addr2, ":");
+		printf("FRDS %6D", (wh->i_addr3), ":");
+		printf("->%6D", (wh->i_addr1), ":");
+		printf("(%6D)", (wh->i_addr2), ":");
 		break;
 	case IEEE80211_FC1_DIR_DSTODS:
-		printf("DSDS %6D->%6D(%6D->%6D)", (uint8_t *)&wh[1], ":",
-		       wh->i_addr3, ":", wh->i_addr2, ":", wh->i_addr1, ":");
+		printf("DSDS %6D", ((const u_int8_t *)&wh[1]), ":");
+		printf("->%6D", (wh->i_addr3), ":");
+		printf("(%6D", (wh->i_addr2), ":");
+		printf("->%6D)", (wh->i_addr1), ":");
 		break;
 	}
 	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
@@ -183,8 +299,13 @@
 		printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
 		break;
 	}
-	if (wh->i_fc[1] & IEEE80211_FC1_WEP)
-		printf(" WEP");
+	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		int i;
+		printf(" WEP [IV");
+		for (i = 0; i < IEEE80211_WEP_IVLEN; i++)
+			printf(" %.02x", buf[sizeof(*wh)+i]);
+		printf(" KID %u]", buf[sizeof(*wh)+i] >> 6);
+	}
 	if (rate >= 0)
 		printf(" %dM", rate / 2);
 	if (rssi >= 0)
@@ -205,12 +326,18 @@
 {
 #define	RV(v)	((v) & IEEE80211_RATE_VAL)
 	int i, j, ignore, error;
-	int okrate, badrate;
+	int okrate, badrate, fixedrate;
 	struct ieee80211_rateset *srs, *nrs;
-	uint8_t r;
+	u_int8_t r;
 
+	/*
+	 * If the fixed rate check was requested but no
+	 * fixed has been defined then just remove it.
+	 */
+	if ((flags & IEEE80211_F_DOFRATE) && ic->ic_fixed_rate < 0)
+		flags &= ~IEEE80211_F_DOFRATE;
 	error = 0;
-	okrate = badrate = 0;
+	okrate = badrate = fixedrate = 0;
 	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
 	nrs = &ni->ni_rates;
 	for (i = 0; i < nrs->rs_nrates; ) {
@@ -231,17 +358,10 @@
 		badrate = r;
 		if (flags & IEEE80211_F_DOFRATE) {
 			/*
-			 * Apply fixed rate constraint.  Note that we do
-			 * not apply the constraint to basic rates as
-			 * otherwise we may not be able to associate if
-			 * the rate set we submit to the AP is invalid
-			 * (e.g. fix rate at 36Mb/s which is not a basic
-			 * rate for 11a operation).
+			 * Check any fixed rate is included. 
 			 */
-			if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 &&
-			    ic->ic_fixed_rate >= 0 &&
-			    r != RV(srs->rs_rates[ic->ic_fixed_rate]))
-				ignore++;
+			if (r == RV(srs->rs_rates[ic->ic_fixed_rate]))
+				fixedrate = r;
 		}
 		if (flags & IEEE80211_F_DONEGO) {
 			/*
@@ -291,24 +411,425 @@
 			okrate = nrs->rs_rates[i];
 		i++;
 	}
-	if (okrate == 0 || error != 0)
+	if (okrate == 0 || error != 0 ||
+	    ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
 		return badrate | IEEE80211_RATE_BASIC;
 	else
 		return RV(okrate);
 #undef RV
 }
 
+/*
+ * Reset 11g-related state.
+ */
+void
+ieee80211_reset_erp(struct ieee80211com *ic)
+{
+	ic->ic_flags &= ~IEEE80211_F_USEPROT;
+	ic->ic_nonerpsta = 0;
+	ic->ic_longslotsta = 0;
+	/*
+	 * Short slot time is enabled only when operating in 11g
+	 * and not in an IBSS.  We must also honor whether or not
+	 * the driver is capable of doing it.
+	 */
+	ieee80211_set_shortslottime(ic,
+		ic->ic_curmode == IEEE80211_MODE_11A ||
+		(ic->ic_curmode == IEEE80211_MODE_11G &&
+		ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		(ic->ic_caps & IEEE80211_C_SHSLOT)));
+	/*
+	 * Set short preamble and ERP barker-preamble flags.
+	 */
+	if (ic->ic_curmode == IEEE80211_MODE_11A ||
+	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
+		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+	} else {
+		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+		ic->ic_flags |= IEEE80211_F_USEBARKER;
+	}
+}
+
+/*
+ * Set the short slot time state and notify the driver.
+ */
+void
+ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
+{
+	if (onoff)
+		ic->ic_flags |= IEEE80211_F_SHSLOT;
+	else
+		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+	/* notify driver */
+	if (ic->ic_updateslot != NULL)
+		ic->ic_updateslot(ic->ic_ifp);
+}
+
+/*
+ * Check if the specified rate set supports ERP.
+ * NB: the rate set is assumed to be sorted.
+ */
+int
+ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
+{
+#define N(a)	(sizeof(a) / sizeof(a[0]))
+	static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
+	int i, j;
+
+	if (rs->rs_nrates < N(rates))
+		return 0;
+	for (i = 0; i < N(rates); i++) {
+		for (j = 0; j < rs->rs_nrates; j++) {
+			int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
+			if (rates[i] == r)
+				goto next;
+			if (r > rates[i])
+				return 0;
+		}
+		return 0;
+	next:
+		;
+	}
+	return 1;
+#undef N
+}
+
+/*
+ * Mark the basic rates for the 11g rate table based on the
+ * operating mode.  For real 11g we mark all the 11b rates
+ * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
+ * 11b rates.  There's also a pseudo 11a-mode used to mark only
+ * the basic OFDM rates.
+ */
+void
+ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+{
+	static const struct ieee80211_rateset basic[] = {
+	    { 0 },			/* IEEE80211_MODE_AUTO */
+	    { 3, { 12, 24, 48 } },	/* IEEE80211_MODE_11A */
+	    { 2, { 2, 4 } },		/* IEEE80211_MODE_11B */
+	    { 4, { 2, 4, 11, 22 } },	/* IEEE80211_MODE_11G (mixed b/g) */
+	    { 0 },			/* IEEE80211_MODE_FH */
+					/* IEEE80211_MODE_PUREG (not yet) */
+	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+	};
+	int i, j;
+
+	for (i = 0; i < rs->rs_nrates; i++) {
+		rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+		for (j = 0; j < basic[mode].rs_nrates; j++)
+			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
+				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
+				break;
+			}
+	}
+}
+
+/*
+ * WME protocol support.  The following parameters come from the spec.
+ */
+typedef struct phyParamType {
+	u_int8_t aifsn; 
+	u_int8_t logcwmin;
+	u_int8_t logcwmax; 
+	u_int16_t txopLimit;
+	u_int8_t acm;
+} paramType;
+
+static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
+	{ 3, 4, 6 },		/* IEEE80211_MODE_AUTO */
+	{ 3, 4, 6 },		/* IEEE80211_MODE_11A */ 
+	{ 3, 5, 7 },		/* IEEE80211_MODE_11B */ 
+	{ 3, 4, 6 },		/* IEEE80211_MODE_11G */ 
+	{ 3, 5, 7 },		/* IEEE80211_MODE_FH */ 
+	{ 2, 3, 5 },		/* IEEE80211_MODE_TURBO_A */ 
+	{ 2, 3, 5 },		/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
+	{ 7, 4, 10 },		/* IEEE80211_MODE_AUTO */
+	{ 7, 4, 10 },		/* IEEE80211_MODE_11A */ 
+	{ 7, 5, 10 },		/* IEEE80211_MODE_11B */ 
+	{ 7, 4, 10 },		/* IEEE80211_MODE_11G */ 
+	{ 7, 5, 10 },		/* IEEE80211_MODE_FH */ 
+	{ 7, 3, 10 },		/* IEEE80211_MODE_TURBO_A */ 
+	{ 7, 3, 10 },		/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
+	{ 1, 3, 4,  94 },	/* IEEE80211_MODE_AUTO */
+	{ 1, 3, 4,  94 },	/* IEEE80211_MODE_11A */ 
+	{ 1, 4, 5, 188 },	/* IEEE80211_MODE_11B */ 
+	{ 1, 3, 4,  94 },	/* IEEE80211_MODE_11G */ 
+	{ 1, 4, 5, 188 },	/* IEEE80211_MODE_FH */ 
+	{ 1, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 1, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
+	{ 1, 2, 3,  47 },	/* IEEE80211_MODE_AUTO */
+	{ 1, 2, 3,  47 },	/* IEEE80211_MODE_11A */ 
+	{ 1, 3, 4, 102 },	/* IEEE80211_MODE_11B */ 
+	{ 1, 2, 3,  47 },	/* IEEE80211_MODE_11G */ 
+	{ 1, 3, 4, 102 },	/* IEEE80211_MODE_FH */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+
+static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = {
+	{ 3, 4, 10 },		/* IEEE80211_MODE_AUTO */
+	{ 3, 4, 10 },		/* IEEE80211_MODE_11A */ 
+	{ 3, 5, 10 },		/* IEEE80211_MODE_11B */ 
+	{ 3, 4, 10 },		/* IEEE80211_MODE_11G */ 
+	{ 3, 5, 10 },		/* IEEE80211_MODE_FH */ 
+	{ 2, 3, 10 },		/* IEEE80211_MODE_TURBO_A */ 
+	{ 2, 3, 10 },		/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = {
+	{ 2, 3, 4,  94 },	/* IEEE80211_MODE_AUTO */
+	{ 2, 3, 4,  94 },	/* IEEE80211_MODE_11A */ 
+	{ 2, 4, 5, 188 },	/* IEEE80211_MODE_11B */ 
+	{ 2, 3, 4,  94 },	/* IEEE80211_MODE_11G */ 
+	{ 2, 4, 5, 188 },	/* IEEE80211_MODE_FH */ 
+	{ 2, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 2, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
+	{ 2, 2, 3,  47 },	/* IEEE80211_MODE_AUTO */
+	{ 2, 2, 3,  47 },	/* IEEE80211_MODE_11A */ 
+	{ 2, 3, 4, 102 },	/* IEEE80211_MODE_11B */ 
+	{ 2, 2, 3,  47 },	/* IEEE80211_MODE_11G */ 
+	{ 2, 3, 4, 102 },	/* IEEE80211_MODE_FH */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+
+void
+ieee80211_wme_initparams(struct ieee80211com *ic)
+{
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	const paramType *pPhyParam, *pBssPhyParam;
+	struct wmeParams *wmep;
+	int i;
+
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		return;
+
+	for (i = 0; i < WME_NUM_AC; i++) {
+		switch (i) {
+		case WME_AC_BK:
+			pPhyParam = &phyParamForAC_BK[ic->ic_curmode];
+			pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode];
+			break;
+		case WME_AC_VI:
+			pPhyParam = &phyParamForAC_VI[ic->ic_curmode];
+			pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode];
+			break;
+		case WME_AC_VO:
+			pPhyParam = &phyParamForAC_VO[ic->ic_curmode];
+			pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode];
+			break;
+		case WME_AC_BE:
+		default:
+			pPhyParam = &phyParamForAC_BE[ic->ic_curmode];
+			pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode];
+			break;
+		}
+
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			wmep->wmep_acm = pPhyParam->acm;
+			wmep->wmep_aifsn = pPhyParam->aifsn;	
+			wmep->wmep_logcwmin = pPhyParam->logcwmin;	
+			wmep->wmep_logcwmax = pPhyParam->logcwmax;		
+			wmep->wmep_txopLimit = pPhyParam->txopLimit;
+		} else {
+			wmep->wmep_acm = pBssPhyParam->acm;
+			wmep->wmep_aifsn = pBssPhyParam->aifsn;	
+			wmep->wmep_logcwmin = pBssPhyParam->logcwmin;	
+			wmep->wmep_logcwmax = pBssPhyParam->logcwmax;		
+			wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+
+		}	
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s chan [acm %u aifsn %u log2(cwmin) %u "
+			"log2(cwmax) %u txpoLimit %u]\n", __func__
+			, ieee80211_wme_acnames[i]
+			, wmep->wmep_acm
+			, wmep->wmep_aifsn
+			, wmep->wmep_logcwmin
+			, wmep->wmep_logcwmax
+			, wmep->wmep_txopLimit
+		);
+
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+		wmep->wmep_acm = pBssPhyParam->acm;
+		wmep->wmep_aifsn = pBssPhyParam->aifsn;	
+		wmep->wmep_logcwmin = pBssPhyParam->logcwmin;	
+		wmep->wmep_logcwmax = pBssPhyParam->logcwmax;		
+		wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s  bss [acm %u aifsn %u log2(cwmin) %u "
+			"log2(cwmax) %u txpoLimit %u]\n", __func__
+			, ieee80211_wme_acnames[i]
+			, wmep->wmep_acm
+			, wmep->wmep_aifsn
+			, wmep->wmep_logcwmin
+			, wmep->wmep_logcwmax
+			, wmep->wmep_txopLimit
+		);
+	}
+	/* NB: check ic_bss to avoid NULL deref on initial attach */
+	if (ic->ic_bss != NULL) {
+		/*
+		 * Calculate agressive mode switching threshold based
+		 * on beacon interval.  This doesn't need locking since
+		 * we're only called before entering the RUN state at
+		 * which point we start sending beacon frames.
+		 */
+		wme->wme_hipri_switch_thresh =
+			(HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100;
+		ieee80211_wme_updateparams(ic);
+	}
+}
+
+/*
+ * Update WME parameters for ourself and the BSS.
+ */
+void
+ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
+{
+	static const paramType phyParam[IEEE80211_MODE_MAX] = {
+		{ 2, 4, 10, 64 },	/* IEEE80211_MODE_AUTO */ 
+		{ 2, 4, 10, 64 },	/* IEEE80211_MODE_11A */ 
+		{ 2, 5, 10, 64 },	/* IEEE80211_MODE_11B */ 
+		{ 2, 4, 10, 64 },	/* IEEE80211_MODE_11G */ 
+		{ 2, 5, 10, 64 },	/* IEEE80211_MODE_FH */ 
+		{ 1, 3, 10, 64 },	/* IEEE80211_MODE_TURBO_A */ 
+		{ 1, 3, 10, 64 },	/* IEEE80211_MODE_TURBO_G */ 
+	};
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	const struct wmeParams *wmep;
+	struct wmeParams *chanp, *bssp;
+	int i;
+
+       	/* set up the channel access parameters for the physical device */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		chanp = &wme->wme_chanParams.cap_wmeParams[i];
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+		chanp->wmep_aifsn = wmep->wmep_aifsn;
+		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+
+		chanp = &wme->wme_bssChanParams.cap_wmeParams[i];
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+		chanp->wmep_aifsn = wmep->wmep_aifsn;
+		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+	}
+
+	/*
+	 * This implements agressive mode as found in certain
+	 * vendors' AP's.  When there is significant high
+	 * priority (VI/VO) traffic in the BSS throttle back BE
+	 * traffic by using conservative parameters.  Otherwise
+	 * BE uses agressive params to optimize performance of
+	 * legacy/non-QoS traffic.
+	 */
+        if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	     (wme->wme_flags & WME_F_AGGRMODE) == 0) ||
+	    (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+	     (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
+	    (ic->ic_flags & IEEE80211_F_WME) == 0) {
+		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+		chanp->wmep_aifsn = bssp->wmep_aifsn =
+			phyParam[ic->ic_curmode].aifsn;
+		chanp->wmep_logcwmin = bssp->wmep_logcwmin =
+			phyParam[ic->ic_curmode].logcwmin;
+		chanp->wmep_logcwmax = bssp->wmep_logcwmax =
+			phyParam[ic->ic_curmode].logcwmax;
+		chanp->wmep_txopLimit = bssp->wmep_txopLimit =
+			(ic->ic_caps & IEEE80211_C_BURST) ?
+				phyParam[ic->ic_curmode].txopLimit : 0;		
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s [acm %u aifsn %u log2(cwmin) %u "
+			"log2(cwmax) %u txpoLimit %u]\n", __func__
+			, ieee80211_wme_acnames[WME_AC_BE]
+			, chanp->wmep_acm
+			, chanp->wmep_aifsn
+			, chanp->wmep_logcwmin
+			, chanp->wmep_logcwmax
+			, chanp->wmep_txopLimit
+		);
+	}
+	
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) == 0) {
+        	static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = {
+              		3,	/* IEEE80211_MODE_AUTO */
+              		3,	/* IEEE80211_MODE_11A */
+              		4,	/* IEEE80211_MODE_11B */
+              		3,	/* IEEE80211_MODE_11G */
+              		4,	/* IEEE80211_MODE_FH */
+              		3,	/* IEEE80211_MODE_TURBO_A */
+              		3,	/* IEEE80211_MODE_TURBO_G */
+		};
+		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+		chanp->wmep_logcwmin = bssp->wmep_logcwmin = 
+			logCwMin[ic->ic_curmode];
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s log2(cwmin) %u\n", __func__
+			, ieee80211_wme_acnames[WME_AC_BE]
+			, chanp->wmep_logcwmin
+		);
+    	}	
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {	/* XXX ibss? */
+		/*
+		 * Arrange for a beacon update and bump the parameter
+		 * set number so associated stations load the new values.
+		 */
+		wme->wme_bssChanParams.cap_info =
+			(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
+		ic->ic_flags |= IEEE80211_F_WMEUPDATE;
+	}
+
+	wme->wme_update(ic);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+		"%s: WME params updated, cap_info 0x%x\n", __func__,
+		ic->ic_opmode == IEEE80211_M_STA ?
+			wme->wme_wmeChanParams.cap_info :
+			wme->wme_bssChanParams.cap_info);
+}
+
+void
+ieee80211_wme_updateparams(struct ieee80211com *ic)
+{
+	IEEE80211_LOCK_INFO;
+
+	if (ic->ic_caps & IEEE80211_C_WME) {
+		IEEE80211_BEACON_LOCK(ic);
+		ieee80211_wme_updateparams_locked(ic);
+		IEEE80211_BEACON_UNLOCK(ic);
+	}
+}
+
 static int
-ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt)
+ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
 	enum ieee80211_state ostate;
-	struct lwkt_tokref ilock;
+	IEEE80211_LOCK_INFO;
 
 	ostate = ic->ic_state;
-	IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__,
-		ieee80211_state_name[ostate], ieee80211_state_name[nstate]));
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
+		ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
 	ic->ic_state = nstate;			/* state transition */
 	ni = ic->ic_bss;			/* NB: no reference held */
 	switch (nstate) {
@@ -322,22 +843,24 @@
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_DISASSOC,
 				    IEEE80211_REASON_ASSOC_LEAVE);
+				ieee80211_sta_leave(ic, ni);
 				break;
 			case IEEE80211_M_HOSTAP:
-				lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+				nt = &ic->ic_sta;
+				IEEE80211_NODE_LOCK(nt);
+				TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
 					if (ni->ni_associd == 0)
 						continue;
 					IEEE80211_SEND_MGMT(ic, ni,
 					    IEEE80211_FC0_SUBTYPE_DISASSOC,
 					    IEEE80211_REASON_ASSOC_LEAVE);
 				}
-				lwkt_reltoken(&ilock);
+				IEEE80211_NODE_UNLOCK(nt);
 				break;
 			default:
 				break;
 			}
-			/* FALLTHRU */
+			goto reset;
 		case IEEE80211_S_ASSOC:
 			switch (ic->ic_opmode) {
 			case IEEE80211_M_STA:
@@ -346,42 +869,38 @@
 				    IEEE80211_REASON_AUTH_LEAVE);
 				break;
 			case IEEE80211_M_HOSTAP:
-				lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+				nt = &ic->ic_sta;
+				IEEE80211_NODE_LOCK(nt);
+				TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
 					IEEE80211_SEND_MGMT(ic, ni,
 					    IEEE80211_FC0_SUBTYPE_DEAUTH,
 					    IEEE80211_REASON_AUTH_LEAVE);
 				}
-				lwkt_reltoken(&ilock);
+				IEEE80211_NODE_UNLOCK(nt);
 				break;
 			default:
 				break;
 			}
-			/* FALLTHRU */
-		case IEEE80211_S_AUTH:
+			goto reset;
 		case IEEE80211_S_SCAN:
+			ieee80211_cancel_scan(ic);
+			goto reset;
+		case IEEE80211_S_AUTH:
+		reset:
 			ic->ic_mgt_timer = 0;
 			IF_DRAIN(&ic->ic_mgtq);
-			if (ic->ic_wep_ctx != NULL) {
-				free(ic->ic_wep_ctx, M_DEVBUF);
-				ic->ic_wep_ctx = NULL;
-			}
-			ieee80211_free_allnodes(ic);
+			ieee80211_reset_bss(ic);
 			break;
 		}
+		if (ic->ic_auth->ia_detach != NULL)
+			ic->ic_auth->ia_detach(ic);
 		break;
 	case IEEE80211_S_SCAN:
-		ic->ic_flags &= ~IEEE80211_F_SIBSS;
-		/* initialize bss for probe request */
-		IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
-		IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
-		ni->ni_rates = ic->ic_sup_rates[
-			ieee80211_chan2mode(ic, ni->ni_chan)];
-		ni->ni_associd = 0;
-		ni->ni_rstamp = 0;
 		switch (ostate) {
 		case IEEE80211_S_INIT:
-			if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+			if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+			     ic->ic_opmode == IEEE80211_M_IBSS ||
+			     ic->ic_opmode == IEEE80211_M_AHDEMO) &&
 			    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
 				/*
 				 * AP operation and we already have a channel;
@@ -389,51 +908,53 @@
 				 */
 				ieee80211_create_ibss(ic, ic->ic_des_chan);
 			} else {
-				ieee80211_begin_scan(ifp);
+				ieee80211_begin_scan(ic, arg);
 			}
 			break;
 		case IEEE80211_S_SCAN:
-			/* scan next */
-			if (ic->ic_flags & IEEE80211_F_ASCAN) {
+			/*
+			 * Scan next. If doing an active scan and the
+			 * channel is not marked passive-only then send
+			 * a probe request.  Otherwise just listen for
+			 * beacons on the channel.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
+			    (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
 			}
 			break;
 		case IEEE80211_S_RUN:
 			/* beacon miss */
-			if (ifp->if_flags & IFF_DEBUG) {
-				/* XXX bssid clobbered above */
-				if_printf(ifp, "no recent beacons from %6D;"
-				    " rescanning\n",
-				    ic->ic_bss->ni_bssid, ":");
-			}
-			ieee80211_free_allnodes(ic);
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE,
+				"no recent beacons from %6D; rescanning\n",
+				(ic->ic_bss->ni_bssid), ":");
+			ieee80211_sta_leave(ic, ni);
+			ic->ic_flags &= ~IEEE80211_F_SIBSS;	/* XXX */
 			/* FALLTHRU */
 		case IEEE80211_S_AUTH:
 		case IEEE80211_S_ASSOC:
 			/* timeout restart scan */
-			ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
+			ni = ieee80211_find_node(&ic->ic_scan,
+				ic->ic_bss->ni_macaddr);
 			if (ni != NULL) {
 				ni->ni_fails++;
 				ieee80211_unref_node(&ni);
 			}
-			ieee80211_begin_scan(ifp);
+			ieee80211_begin_scan(ic, arg);
 			break;
 		}
 		break;
 	case IEEE80211_S_AUTH:
 		switch (ostate) {
 		case IEEE80211_S_INIT:
-			IEEE80211_DPRINTF(("%s: invalid transition\n",
-				__func__));
-			break;
 		case IEEE80211_S_SCAN:
 			IEEE80211_SEND_MGMT(ic, ni,
 			    IEEE80211_FC0_SUBTYPE_AUTH, 1);
 			break;
 		case IEEE80211_S_AUTH:
 		case IEEE80211_S_ASSOC:
-			switch (mgt) {
+			switch (arg) {
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				/* ??? */
 				IEEE80211_SEND_MGMT(ic, ni,
@@ -445,7 +966,7 @@
 			}
 			break;
 		case IEEE80211_S_RUN:
-			switch (mgt) {
+			switch (arg) {
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
@@ -455,6 +976,7 @@
 				/* try to reauth */
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_AUTH, 1);
+				ieee80211_sta_leave(ic, ni);
 				break;
 			}
 			break;
@@ -465,8 +987,8 @@
 		case IEEE80211_S_INIT:
 		case IEEE80211_S_SCAN:
 		case IEEE80211_S_ASSOC:
-			IEEE80211_DPRINTF(("%s: invalid transition\n",
-				__func__));
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				"%s: invalid transition\n", __func__);
 			break;
 		case IEEE80211_S_AUTH:
 			IEEE80211_SEND_MGMT(ic, ni,
@@ -475,39 +997,76 @@
 		case IEEE80211_S_RUN:
 			IEEE80211_SEND_MGMT(ic, ni,
 			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+			ieee80211_sta_leave(ic, ni);
 			break;
 		}
 		break;
 	case IEEE80211_S_RUN:
+		if (ic->ic_flags & IEEE80211_F_WPA) {
+			/* XXX validate prerequisites */
+		}
 		switch (ostate) {
 		case IEEE80211_S_INIT:
+			if (ic->ic_opmode == IEEE80211_M_MONITOR)
+				break;
+			/* fall thru... */
 		case IEEE80211_S_AUTH:
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				"%s: invalid transition\n", __func__);
+			/* fall thru... */
 		case IEEE80211_S_RUN:
-			IEEE80211_DPRINTF(("%s: invalid transition\n",
-				__func__));
 			break;
 		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
 		case IEEE80211_S_ASSOC:		/* infra mode */
 			KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
 				("%s: bogus xmit rate %u setup\n", __func__,
 					ni->ni_txrate));
-			if (ifp->if_flags & IFF_DEBUG) {
-				if_printf(ifp, " ");
+#ifdef IEEE80211_DEBUG
+			if (ieee80211_msg_debug(ic)) {
 				if (ic->ic_opmode == IEEE80211_M_STA)
-					printf("associated ");
+					if_printf(ifp, "associated ");
 				else
-					printf("synchronized ");
-				printf("with %6D ssid ", ni->ni_bssid, ":");
+					if_printf(ifp, "synchronized ");
+				printf("with %6D ssid ",
+				    (ni->ni_bssid), ":");
 				ieee80211_print_essid(ic->ic_bss->ni_essid,
 				    ni->ni_esslen);
 				printf(" channel %d start %uMb\n",
 					ieee80211_chan2ieee(ic, ni->ni_chan),
 					IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
 			}
+#endif
 			ic->ic_mgt_timer = 0;
-			(*ifp->if_start)(ifp);
+			if (ic->ic_opmode == IEEE80211_M_STA)
+				ieee80211_notify_node_join(ic, ni, 
+					arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+			(*ifp->if_start)(ifp);	/* XXX not authorized yet */
 			break;
 		}
+		/*
+		 * Start/stop the authenticator when operating as an
+		 * AP.  We delay until here to allow configuration to
+		 * happen out of order.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */
+		    ic->ic_auth->ia_attach != NULL) {
+			/* XXX check failure */
+			ic->ic_auth->ia_attach(ic);
+		} else if (ic->ic_auth->ia_detach != NULL) {
+			ic->ic_auth->ia_detach(ic);
+		}
+		/*
+		 * When 802.1x is not in use mark the port authorized
+		 * at this point so traffic can flow.
+		 */
+		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+			ieee80211_node_authorize(ic, ni);
+		/*
+		 * Enable inactivity processing.
+		 * XXX
+		 */
+		ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
+		ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT;
 		break;
 	}
 	return 0;
Index: src/sys/netproto/802_11/ieee80211_proto.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_proto.h,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_proto.h
--- src/sys/netproto/802_11/ieee80211_proto.h	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_proto.h	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,10 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.4 2003/08/19 22:17:03 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_PROTO_H_
-#define	_NETPROTO_802_11_IEEE80211_PROTO_H_
+#ifndef _NET80211_IEEE80211_PROTO_H_
+#define _NET80211_IEEE80211_PROTO_H_
 
 /*
  * 802.11 protocol implementation definitions.
@@ -53,28 +51,194 @@
 	((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
 
 extern	const char *ieee80211_mgt_subtype_name[];
+extern	const char *ieee80211_phymode_name[];
 
-extern	void ieee80211_proto_attach(struct ifnet *);
-extern	void ieee80211_proto_detach(struct ifnet *);
+void	ieee80211_proto_attach(struct ieee80211com *);
+void	ieee80211_proto_detach(struct ieee80211com *);
 
 struct ieee80211_node;
-extern	void ieee80211_input(struct ifnet *, struct mbuf *,
-		struct ieee80211_node *, int, uint32_t);
-extern	void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
-		struct ieee80211_node *, int, int, uint32_t);
-extern	int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
+int	ieee80211_input(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *, int, u_int32_t);
+void	ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *, int, int, u_int32_t);
+int	ieee80211_send_nulldata(struct ieee80211com *, struct ieee80211_node *);
+int	ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
 		int, int);
-extern	struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
-		struct ieee80211_node **);
-extern	struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *);
-extern	uint8_t *ieee80211_add_rates(uint8_t *frm,
-		const struct ieee80211_rateset *);
+int	ieee80211_classify(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *);
+struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *);
+void	ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, 
+		struct mbuf *);
+
+void	ieee80211_reset_erp(struct ieee80211com *);
+void	ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
+int	ieee80211_iserp_rateset(struct ieee80211com *,
+		struct ieee80211_rateset *);
+void	ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+		enum ieee80211_phymode);
+
+/*
+ * Return the size of the 802.11 header for a management or data frame.
+ */
+static __inline int
+ieee80211_hdrsize(const void *data)
+{
+	const struct ieee80211_frame *wh = data;
+	int size = sizeof(struct ieee80211_frame);
+
+	/* NB: we don't handle control frames */
+	KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL,
+		("%s: control frame", __func__));
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+		size += IEEE80211_ADDR_LEN;
+	if (IEEE80211_QOS_HAS_SEQ(wh))
+		size += sizeof(u_int16_t);
+	return size;
+}
+
+/*
+ * Return the size of the 802.11 header; handles any type of frame.
+ */
+static __inline int
+ieee80211_anyhdrsize(const void *data)
+{
+	const struct ieee80211_frame *wh = data;
+
+	if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+		switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
+		case IEEE80211_FC0_SUBTYPE_CTS:
+		case IEEE80211_FC0_SUBTYPE_ACK:
+			return sizeof(struct ieee80211_frame_ack);
+		}
+		return sizeof(struct ieee80211_frame_min);
+	} else
+		return ieee80211_hdrsize(data);
+}
+
+/*
+ * Template for an in-kernel authenticator.  Authenticators
+ * register with the protocol code and are typically loaded
+ * as separate modules as needed.
+ */
+struct ieee80211_authenticator {
+	const char *ia_name;		/* printable name */
+	int	(*ia_attach)(struct ieee80211com *);
+	void	(*ia_detach)(struct ieee80211com *);
+	void	(*ia_node_join)(struct ieee80211com *,
+				struct ieee80211_node *);
+	void	(*ia_node_leave)(struct ieee80211com *,
+				struct ieee80211_node *);
+};
+void	ieee80211_authenticator_register(int type,
+		const struct ieee80211_authenticator *);
+void	ieee80211_authenticator_unregister(int type);
+const struct ieee80211_authenticator *ieee80211_authenticator_get(int auth);
+
+/*
+ * Template for an MAC ACL policy module.  Such modules
+ * register with the protocol code and are passed the sender's
+ * address of each received frame for validation.
+ */
+struct ieee80211_aclator {
+	const char *iac_name;		/* printable name */
+	int	(*iac_attach)(struct ieee80211com *);
+	void	(*iac_detach)(struct ieee80211com *);
+	int	(*iac_check)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_add)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_remove)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_flush)(struct ieee80211com *);
+	int	(*iac_setpolicy)(struct ieee80211com *, int);
+	int	(*iac_getpolicy)(struct ieee80211com *);
+};
+void	ieee80211_aclator_register(const struct ieee80211_aclator *);
+void	ieee80211_aclator_unregister(const struct ieee80211_aclator *);
+const struct ieee80211_aclator *ieee80211_aclator_get(const char *name);
+
+/* flags for ieee80211_fix_rate() */
+#define	IEEE80211_F_DOSORT	0x00000001	/* sort rate list */
+#define	IEEE80211_F_DOFRATE	0x00000002	/* use fixed rate */
+#define	IEEE80211_F_DONEGO	0x00000004	/* calc negotiated rate */
+#define	IEEE80211_F_DODEL	0x00000008	/* delete ignore rate */
+int	ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int);
+
+/*
+ * WME/WMM support.
+ */
+struct wmeParams {
+	u_int8_t	wmep_acm;
+	u_int8_t	wmep_aifsn;
+	u_int8_t	wmep_logcwmin;		/* log2(cwmin) */
+	u_int8_t	wmep_logcwmax;		/* log2(cwmax) */
+	u_int8_t	wmep_txopLimit;
+	u_int8_t	wmep_noackPolicy;	/* 0 (ack), 1 (no ack) */
+};
+#define	IEEE80211_TXOP_TO_US(_txop)	((_txop)<<5)
+#define	IEEE80211_US_TO_TXOP(_us)	((_us)>>5)
+
+struct chanAccParams {
+	u_int8_t	cap_info;		/* version of the current set */
+	struct wmeParams cap_wmeParams[WME_NUM_AC];
+};
+
+struct ieee80211_wme_state {
+	u_int	wme_flags;
+#define	WME_F_AGGRMODE	0x00000001	/* STATUS: WME agressive mode */
+	u_int	wme_hipri_traffic;	/* VI/VO frames in beacon interval */
+	u_int	wme_hipri_switch_thresh;/* agressive mode switch thresh */
+	u_int	wme_hipri_switch_hysteresis;/* agressive mode switch hysteresis */
+
+	struct wmeParams wme_params[4];		/* from assoc resp for each AC*/
+	struct chanAccParams wme_wmeChanParams;	/* WME params applied to self */
+	struct chanAccParams wme_wmeBssChanParams;/* WME params bcast to stations */
+	struct chanAccParams wme_chanParams;	/* params applied to self */
+	struct chanAccParams wme_bssChanParams;	/* params bcast to stations */
+
+	int	(*wme_update)(struct ieee80211com *);
+};
+
+void	ieee80211_wme_initparams(struct ieee80211com *);
+void	ieee80211_wme_updateparams(struct ieee80211com *);
+void	ieee80211_wme_updateparams_locked(struct ieee80211com *);
+
 #define	ieee80211_new_state(_ic, _nstate, _arg) \
 	(((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
-extern	uint8_t *ieee80211_add_xrates(uint8_t *frm,
-		const struct ieee80211_rateset *);
-extern	void ieee80211_print_essid(uint8_t *, int);
-extern	void ieee80211_dump_pkt(uint8_t *, int, int, int);
+void	ieee80211_print_essid(const u_int8_t *, int);
+void	ieee80211_dump_pkt(const u_int8_t *, int, int, int);
 
 extern	const char *ieee80211_state_name[IEEE80211_S_MAX];
-#endif /* _NETPROTO_802_11_IEEE80211_PROTO_H_ */
+extern	const char *ieee80211_wme_acnames[];
+
+/*
+ * Beacon frames constructed by ieee80211_beacon_alloc
+ * have the following structure filled in so drivers
+ * can update the frame later w/ minimal overhead.
+ */
+struct ieee80211_beacon_offsets {
+	u_int16_t	*bo_caps;	/* capabilities */
+	u_int8_t	*bo_tim;	/* start of atim/dtim */
+	u_int8_t	*bo_wme;	/* start of WME parameters */
+	u_int8_t	*bo_trailer;	/* start of fixed-size trailer */
+	u_int16_t	bo_tim_len;	/* atim/dtim length in bytes */
+	u_int16_t	bo_trailer_len;	/* trailer length in bytes */
+};
+struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *,
+		struct ieee80211_node *, struct ieee80211_beacon_offsets *);
+int	ieee80211_beacon_update(struct ieee80211com *,
+		struct ieee80211_node *, struct ieee80211_beacon_offsets *,
+		struct mbuf *, int broadcast);
+
+/*
+ * Notification methods called from the 802.11 state machine.
+ * Note that while these are defined here, their implementation
+ * is OS-specific.
+ */
+void	ieee80211_notify_node_join(struct ieee80211com *,
+		struct ieee80211_node *, int newassoc);
+void	ieee80211_notify_node_leave(struct ieee80211com *,
+		struct ieee80211_node *);
+void	ieee80211_notify_scan_done(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_PROTO_H_ */
Index: src/sys/netproto/802_11/ieee80211_radiotap.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_radiotap.h,v
retrieving revision 1.1
diff -u -r1.1 ieee80211_radiotap.h
--- src/sys/netproto/802_11/ieee80211_radiotap.h	26 Jul 2004 16:30:17 -0000	1.1
+++ src/sys/netproto/802_11/ieee80211_radiotap.h	22 Apr 2005 19:56:56 -0000
@@ -1,8 +1,7 @@
-/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.3 2004/04/05 22:13:21 sam Exp $ */
-/* $NetBSD: ieee80211_radiotap.h,v 1.3 2003/11/16 09:02:42 dyoung Exp $ */
-/* $DragonFly: src/sys/netproto/802_11/ieee80211_radiotap.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ */
+/* $FreeBSD$ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.10 2005/01/04 00:34:58 dyoung Exp $ */
 
-/*
+/*-
  * Copyright (c) 2003, 2004 David Young.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,9 +29,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211RADIOTAP_H_
-#define	_NETPROTO_802_11_IEEE80211RADIOTAP_H_
+#ifndef _NET_IF_IEEE80211RADIOTAP_H_
+#define _NET_IF_IEEE80211RADIOTAP_H_
 
 /* A generic radio capture format is desirable. There is one for
  * Linux, but it is neither rigidly defined (there were not even
@@ -48,26 +46,31 @@
  * function of...") that I cannot set false expectations for lawyerly
  * readers.
  */
-#ifdef _KERNEL
+#if defined(__KERNEL__) || defined(_KERNEL)
 #ifndef DLT_IEEE802_11_RADIO
 #define	DLT_IEEE802_11_RADIO	127	/* 802.11 plus WLAN header */
 #endif
-#endif /* _KERNEL */
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+
+/* XXX tcpdump/libpcap do not tolerate variable-length headers,
+ * yet, so we pad every radiotap header to 64 bytes. Ugh.
+ */
+#define IEEE80211_RADIOTAP_HDRLEN	64
 
 /* The radio capture header precedes the 802.11 header. */
 struct ieee80211_radiotap_header {
-	uint8_t		it_version;	/* Version 0. Only increases
+	u_int8_t	it_version;	/* Version 0. Only increases
 					 * for drastic changes,
 					 * introduction of compatible
 					 * new fields does not count.
 					 */
-	uint8_t		it_pad;
-	uint16_t	it_len;         /* length of the whole
+	u_int8_t	it_pad;
+	u_int16_t       it_len;         /* length of the whole
 					 * header in bytes, including
 					 * it_version, it_pad,
 					 * it_len, and data fields.
 					 */
-	uint32_t	it_present;     /* A bitmap telling which
+	u_int32_t       it_present;     /* A bitmap telling which
 					 * fields are present. Set bit 31
 					 * (0x80000000) to extend the
 					 * bitmap by another 32 bits.
@@ -76,87 +79,87 @@
 					 */
 } __attribute__((__packed__));
 
-/* Name                                 Data type	Units
- * ----                                 ---------	-----
+/* Name                                 Data type       Units
+ * ----                                 ---------       -----
  *
- * IEEE80211_RADIOTAP_TSFT              uint64_t	microseconds
+ * IEEE80211_RADIOTAP_TSFT              u_int64_t       microseconds
  *
  *      Value in microseconds of the MAC's 64-bit 802.11 Time
  *      Synchronization Function timer when the first bit of the
  *      MPDU arrived at the MAC. For received frames, only.
  *
- * IEEE80211_RADIOTAP_CHANNEL           2 x uint16_t	MHz, bitmap
+ * IEEE80211_RADIOTAP_CHANNEL           2 x u_int16_t   MHz, bitmap
  *
  *      Tx/Rx frequency in MHz, followed by flags (see below).
  *
- * IEEE80211_RADIOTAP_FHSS              uint16_t	see below
+ * IEEE80211_RADIOTAP_FHSS              u_int16_t       see below
  *
  *      For frequency-hopping radios, the hop set (first byte)
  *      and pattern (second byte).
  *
- * IEEE80211_RADIOTAP_RATE              uint8_t		500kb/s
+ * IEEE80211_RADIOTAP_RATE              u_int8_t        500kb/s
  *
  *      Tx/Rx data rate
  *
- * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     int8_t		decibels from
- *							one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     int8_t          decibels from
+ *                                                      one milliwatt (dBm)
  *
  *      RF signal power at the antenna, decibel difference from
  *      one milliwatt.
  *
- * IEEE80211_RADIOTAP_DBM_ANTNOISE      int8_t		decibels from
- *							one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE      int8_t          decibels from
+ *                                                      one milliwatt (dBm)
  *
  *      RF noise power at the antenna, decibel difference from one
  *      milliwatt.
  *
- * IEEE80211_RADIOTAP_DB_ANTSIGNAL      uint8_t		decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u_int8_t        decibel (dB)
  *
  *      RF signal power at the antenna, decibel difference from an
  *      arbitrary, fixed reference.
  *
- * IEEE80211_RADIOTAP_DB_ANTNOISE       uint8_t		decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTNOISE       u_int8_t        decibel (dB)
  *
  *      RF noise power at the antenna, decibel difference from an
  *      arbitrary, fixed reference point.
  *
- * IEEE80211_RADIOTAP_BARKER_CODE_LOCK  uint16_t	unitless
+ * IEEE80211_RADIOTAP_LOCK_QUALITY      u_int16_t       unitless
  *
  *      Quality of Barker code lock. Unitless. Monotonically
  *      nondecreasing with "better" lock strength. Called "Signal
  *      Quality" in datasheets.  (Is there a standard way to measure
  *      this?)
  *
- * IEEE80211_RADIOTAP_TX_ATTENUATION    uint16_t	unitless
+ * IEEE80211_RADIOTAP_TX_ATTENUATION    u_int16_t       unitless
  *
  *      Transmit power expressed as unitless distance from max
  *      power set at factory calibration.  0 is max power.
  *      Monotonically nondecreasing with lower power levels.
  *
- * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t	decibels (dB)
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u_int16_t       decibels (dB)
  *
  *      Transmit power expressed as decibel distance from max power
  *      set at factory calibration.  0 is max power.  Monotonically
  *      nondecreasing with lower power levels.
  *
- * IEEE80211_RADIOTAP_DBM_TX_POWER      int8_t		decibels from
- *							one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_TX_POWER      int8_t          decibels from
+ *                                                      one milliwatt (dBm)
  *
  *      Transmit power expressed as dBm (decibels from a 1 milliwatt
  *      reference). This is the absolute power level measured at
  *      the antenna port.
  *
- * IEEE80211_RADIOTAP_FLAGS             uint8_t		bitmap
+ * IEEE80211_RADIOTAP_FLAGS             u_int8_t        bitmap
  *
  *      Properties of transmitted and received frames. See flags
  *      defined below.
  *
- * IEEE80211_RADIOTAP_ANTENNA           uint8_t		antenna index
+ * IEEE80211_RADIOTAP_ANTENNA           u_int8_t        antenna index
  *
  *      Unitless indication of the Rx/Tx antenna for this packet.
  *      The first antenna is antenna 0.
  *
- * IEEE80211_RADIOTAP_FCS           	uint32_t	data
+ * IEEE80211_RADIOTAP_FCS           	u_int32_t       data
  *
  *	FCS from frame in network byte order.
  */
@@ -175,18 +178,17 @@
 	IEEE80211_RADIOTAP_ANTENNA = 11,
 	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
 	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
-	IEEE80211_RADIOTAP_FCS = 14,
 	IEEE80211_RADIOTAP_EXT = 31,
 };
 
 #ifndef _KERNEL
 /* Channel flags. */
-#define	IEEE80211_CHAN_TURBO    0x0010  /* Turbo channel */
-#define	IEEE80211_CHAN_CCK      0x0020  /* CCK channel */
-#define	IEEE80211_CHAN_OFDM     0x0040  /* OFDM channel */
-#define	IEEE80211_CHAN_2GHZ     0x0080  /* 2 GHz spectrum channel. */
-#define	IEEE80211_CHAN_5GHZ     0x0100  /* 5 GHz spectrum channel */
-#define	IEEE80211_CHAN_PASSIVE  0x0200  /* Only passive scan allowed */
+#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */
+#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */
+#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */
+#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */
+#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */
+#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
 #define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
 #define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
 #endif /* !_KERNEL */
@@ -205,5 +207,10 @@
 #define	IEEE80211_RADIOTAP_F_FRAG	0x08	/* sent/received
 						 * with fragmentation
 						 */
+#define	IEEE80211_RADIOTAP_F_FCS	0x10	/* frame includes FCS */
+#define	IEEE80211_RADIOTAP_F_DATAPAD	0x20	/* frame has padding between
+						 * 802.11 header and payload
+						 * (to 32-bit boundary)
+						 */
 
-#endif /* _NETPROTO_802_11_IEEE80211RADIOTAP_H_ */
+#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */
Index: src/sys/netproto/802_11/ieee80211_var.h
===================================================================
RCS file: /usr/dfly/repo/src/sys/netproto/802_11/ieee80211_var.h,v
retrieving revision 1.2
diff -u -r1.2 ieee80211_var.h
--- src/sys/netproto/802_11/ieee80211_var.h	26 Jan 2005 00:37:40 -0000	1.2
+++ src/sys/netproto/802_11/ieee80211_var.h	22 Apr 2005 19:56:56 -0000
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,249 +29,240 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.15 2004/04/05 22:10:26 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_var.h,v 1.2 2005/01/26 00:37:40 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_VAR_H_
-#define	_NETPROTO_802_11_IEEE80211_VAR_H_
+#ifndef _NET80211_IEEE80211_VAR_H_
+#define _NET80211_IEEE80211_VAR_H_
 
 /*
  * Definitions for IEEE 802.11 drivers.
  */
+#define	IEEE80211_DEBUG
+#undef	IEEE80211_DEBUG_REFCNT			/* node refcnt stuff */
 
+/* NB: portability glue must go first */
+#ifdef __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#elif __FreeBSD__
+#include <net80211/ieee80211_freebsd.h>
+#elif __DragonFly__
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#elif __linux__
+#include <net80211/ieee80211_linux.h>
+#else
+#error	"No support for your operating system!"
+#endif
+
+#include <netproto/802_11/_ieee80211.h>
 #include <netproto/802_11/ieee80211.h>
 #include <netproto/802_11/ieee80211_crypto.h>
 #include <netproto/802_11/ieee80211_ioctl.h>		/* for ieee80211_stats */
 #include <netproto/802_11/ieee80211_node.h>
 #include <netproto/802_11/ieee80211_proto.h>
 
-#define	IEEE80211_CHAN_MAX	255
-#define	IEEE80211_CHAN_ANY	0xffff		/* token for ``any channel'' */
-#define	IEEE80211_CHAN_ANYC \
-	((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
-
-#define	IEEE80211_TXPOWER_MAX	100	/* max power */
-#define	IEEE80211_TXPOWER_MIN	0	/* kill radio (if possible) */
-
-enum ieee80211_phytype {
-	IEEE80211_T_DS,			/* direct sequence spread spectrum */
-	IEEE80211_T_FH,			/* frequency hopping */
-	IEEE80211_T_OFDM,		/* frequency division multiplexing */
-	IEEE80211_T_TURBO,		/* high rate OFDM, aka turbo mode */
-};
-#define	IEEE80211_T_CCK	IEEE80211_T_DS	/* more common nomenclature */
-
-/* XXX not really a mode; there are really multiple PHY's */
-enum ieee80211_phymode {
-	IEEE80211_MODE_AUTO	= 0,	/* autoselect */
-	IEEE80211_MODE_11A	= 1,	/* 5GHz, OFDM */
-	IEEE80211_MODE_11B	= 2,	/* 2GHz, CCK */
-	IEEE80211_MODE_11G	= 3,	/* 2GHz, OFDM */
-	IEEE80211_MODE_FH	= 4,	/* 2GHz, GFSK */
-	IEEE80211_MODE_TURBO	= 5,	/* 5GHz, OFDM, 2x clock */
-};
-#define	IEEE80211_MODE_MAX	(IEEE80211_MODE_TURBO+1)
+#define	IEEE80211_TXPOWER_MAX	100	/* .5 dbM (XXX units?) */
+#define	IEEE80211_TXPOWER_MIN	0	/* kill radio */
 
-enum ieee80211_opmode {
-	IEEE80211_M_STA		= 1,	/* infrastructure station */
-	IEEE80211_M_IBSS 	= 0,	/* IBSS (adhoc) station */
-	IEEE80211_M_AHDEMO	= 3,	/* Old lucent compatible adhoc demo */
-	IEEE80211_M_HOSTAP	= 6,	/* Software Access Point */
-	IEEE80211_M_MONITOR	= 8	/* Monitor mode */
-};
+#define	IEEE80211_DTIM_MAX	15	/* max DTIM period */
+#define	IEEE80211_DTIM_MIN	1	/* min DTIM period */
+#define	IEEE80211_DTIM_DEFAULT	1	/* default DTIM period */
 
-/*
- * 802.11g protection mode.
- */
-enum ieee80211_protmode {
-	IEEE80211_PROT_NONE	= 0,	/* no protection */
-	IEEE80211_PROT_CTSONLY	= 1,	/* CTS to self */
-	IEEE80211_PROT_RTSCTS	= 2,	/* RTS-CTS */
-};
+#define	IEEE80211_BINTVAL_MAX	500	/* max beacon interval (TU's) */
+#define	IEEE80211_BINTVAL_MIN	25	/* min beacon interval (TU's) */
+#define	IEEE80211_BINTVAL_DEFAULT 100	/* default beacon interval (TU's) */
 
-/*
- * Channels are specified by frequency and attributes.
- */
-struct ieee80211_channel {
-	uint16_t	ic_freq;	/* setting in Mhz */
-	uint16_t	ic_flags;	/* see below */
-};
+#define	IEEE80211_PS_SLEEP	0x1	/* STA is in power saving mode */
+#define	IEEE80211_PS_MAX_QUEUE	50	/* maximum saved packets */
 
-/* bits 0-3 are for private use by drivers */
-/* channel attributes */
-#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */
-#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */
-#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */
-#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */
-#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */
-#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
-#define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
-#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+#define	IEEE80211_MS_TO_TU(x)	(((x) * 1000) / 1024)
+#define	IEEE80211_TU_TO_MS(x)	(((x) * 1024) / 1000)
 
-/*
- * Useful combinations of channel characteristics.
- */
-#define	IEEE80211_CHAN_FHSS \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
-#define	IEEE80211_CHAN_A \
-	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
-#define	IEEE80211_CHAN_B \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
-#define	IEEE80211_CHAN_PUREG \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
-#define	IEEE80211_CHAN_G \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
-#define	IEEE80211_CHAN_T \
-	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
-
-#define	IEEE80211_IS_CHAN_FHSS(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
-#define	IEEE80211_IS_CHAN_A(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
-#define	IEEE80211_IS_CHAN_B(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
-#define	IEEE80211_IS_CHAN_PUREG(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
-#define	IEEE80211_IS_CHAN_G(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
-#define	IEEE80211_IS_CHAN_T(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
-
-#define	IEEE80211_IS_CHAN_2GHZ(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
-#define	IEEE80211_IS_CHAN_5GHZ(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
-#define	IEEE80211_IS_CHAN_OFDM(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0)
-#define	IEEE80211_IS_CHAN_CCK(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0)
-#define	IEEE80211_IS_CHAN_GFSK(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
-
-/* ni_chan encoding for FH phy */
-#define	IEEE80211_FH_CHANMOD	80
-#define	IEEE80211_FH_CHAN(set,pat)	(((set)-1)*IEEE80211_FH_CHANMOD+(pat))
-#define	IEEE80211_FH_CHANSET(chan)	((chan)/IEEE80211_FH_CHANMOD+1)
-#define	IEEE80211_FH_CHANPAT(chan)	((chan)%IEEE80211_FH_CHANMOD)
+struct ieee80211_aclator;
+struct sysctl_ctx_list;
 
 struct ieee80211com {
-	struct arpcom		ic_ac;
+	SLIST_ENTRY(ieee80211com) ic_next;
+	struct ifnet		*ic_ifp;	/* associated device */
+	struct ieee80211_stats	ic_stats;	/* statistics */
+	struct sysctl_ctx_list	*ic_sysctl;	/* dynamic sysctl context */
+	u_int32_t		ic_debug;	/* debug msg flags */
+	int			ic_vap;		/* virtual AP index */
+	ieee80211_beacon_lock_t	ic_beaconlock;	/* beacon update lock */
+
+	int			(*ic_reset)(struct ifnet *);
 	void			(*ic_recv_mgmt)(struct ieee80211com *,
 				    struct mbuf *, struct ieee80211_node *,
-				    int, int, uint32_t);
+				    int, int, u_int32_t);
 	int			(*ic_send_mgmt)(struct ieee80211com *,
 				    struct ieee80211_node *, int, int);
 	int			(*ic_newstate)(struct ieee80211com *,
 				    enum ieee80211_state, int);
 	void			(*ic_newassoc)(struct ieee80211com *,
 				    struct ieee80211_node *, int);
-	uint8_t			ic_myaddr[IEEE80211_ADDR_LEN];
+	void			(*ic_updateslot)(struct ifnet *);
+	void			(*ic_set_tim)(struct ieee80211com *,
+				    struct ieee80211_node *, int);
+	u_int8_t		ic_myaddr[IEEE80211_ADDR_LEN];
 	struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
 	struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
-	u_char			ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)];
-	u_char			ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)];
-	u_char			ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)];
+	u_int8_t		ic_chan_avail[IEEE80211_CHAN_BYTES];
+	u_int8_t		ic_chan_active[IEEE80211_CHAN_BYTES];
+	u_int8_t		ic_chan_scan[IEEE80211_CHAN_BYTES];
+	struct ieee80211_node_table ic_scan;	/* scan candidates */
 	struct ifqueue		ic_mgtq;
-	uint32_t		ic_flags;	/* state flags */
-	uint32_t		ic_caps;	/* capabilities */
-	uint16_t		ic_modecaps;	/* set of mode capabilities */
-	uint16_t		ic_curmode;	/* current mode */
+	u_int32_t		ic_flags;	/* state flags */
+	u_int32_t		ic_caps;	/* capabilities */
+	u_int16_t		ic_modecaps;	/* set of mode capabilities */
+	u_int16_t		ic_curmode;	/* current mode */
 	enum ieee80211_phytype	ic_phytype;	/* XXX wrong for multi-mode */
 	enum ieee80211_opmode	ic_opmode;	/* operation mode */
 	enum ieee80211_state	ic_state;	/* 802.11 state */
 	enum ieee80211_protmode	ic_protmode;	/* 802.11g protection mode */
+	enum ieee80211_roamingmode ic_roaming;	/* roaming mode */
+	struct ieee80211_node_table ic_sta;	/* stations/neighbors */
+	u_int32_t		*ic_aid_bitmap;	/* association id map */
+	u_int16_t		ic_max_aid;
+	u_int16_t		ic_sta_assoc;	/* stations associated */
+	u_int16_t		ic_ps_sta;	/* stations in power save */
+	u_int16_t		ic_ps_pending;	/* ps sta's w/ pending frames */
+	u_int8_t		*ic_tim_bitmap;	/* power-save stations w/ data*/
+	u_int16_t		ic_tim_len;	/* ic_tim_bitmap size (bytes) */
+	u_int16_t		ic_dtim_period;	/* DTIM period */
 	struct ifmedia		ic_media;	/* interface media config */
 	struct bpf_if		*ic_rawbpf;	/* packet filter structure */
 	struct ieee80211_node	*ic_bss;	/* information for this node */
 	struct ieee80211_channel *ic_ibss_chan;
 	int			ic_fixed_rate;	/* index to ic_sup_rates[] */
-	uint16_t		ic_rtsthreshold;
-	uint16_t		ic_fragthreshold;
-	struct lwkt_token	ic_nodetoken;	/* on node table */
-	u_int			ic_scangen;	/* gen# for timeout scan */
-	struct ieee80211_node	*(*ic_node_alloc)(struct ieee80211com *);
-	void			(*ic_node_free)(struct ieee80211com *,
-					struct ieee80211_node *);
-	void			(*ic_node_copy)(struct ieee80211com *,
-					struct ieee80211_node *,
-					const struct ieee80211_node *);
-	uint8_t			(*ic_node_getrssi)(struct ieee80211com *,
-					struct ieee80211_node *);
-	TAILQ_HEAD(, ieee80211_node) ic_node;	/* information of all nodes */
-	LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE];
-	uint16_t		ic_lintval;	/* listen interval */
-	uint16_t		ic_holdover;	/* PM hold over duration */
-	uint16_t		ic_txmin;	/* min tx retry count */
-	uint16_t		ic_txmax;	/* max tx retry count */
-	uint16_t		ic_txlifetime;	/* tx lifetime */
-	uint16_t		ic_txpower;	/* tx power setting (dbM) */
-	uint16_t		ic_bmisstimeout;/* beacon miss threshold (ms) */
+	u_int16_t		ic_rtsthreshold;
+	u_int16_t		ic_fragthreshold;
+	struct ieee80211_node	*(*ic_node_alloc)(struct ieee80211_node_table*);
+	void			(*ic_node_free)(struct ieee80211_node *);
+	void			(*ic_node_cleanup)(struct ieee80211_node *);
+	u_int8_t		(*ic_node_getrssi)(const struct ieee80211_node*);
+	u_int16_t		ic_lintval;	/* listen interval */
+	u_int16_t		ic_holdover;	/* PM hold over duration */
+	u_int16_t		ic_txmin;	/* min tx retry count */
+	u_int16_t		ic_txmax;	/* max tx retry count */
+	u_int16_t		ic_txlifetime;	/* tx lifetime */
+	u_int16_t		ic_txpowlimit;	/* global tx power limit */
+	u_int16_t		ic_bmisstimeout;/* beacon miss threshold (ms) */
+	u_int16_t		ic_nonerpsta;	/* # non-ERP stations */
+	u_int16_t		ic_longslotsta;	/* # long slot time stations */
 	int			ic_mgt_timer;	/* mgmt timeout */
 	int			ic_inact_timer;	/* inactivity timer wait */
 	int			ic_des_esslen;
-	uint8_t			ic_des_essid[IEEE80211_NWID_LEN];
+	u_int8_t		ic_des_essid[IEEE80211_NWID_LEN];
 	struct ieee80211_channel *ic_des_chan;	/* desired channel */
-	uint8_t			ic_des_bssid[IEEE80211_ADDR_LEN];
-	struct ieee80211_wepkey	ic_nw_keys[IEEE80211_WEP_NKID];
-	int			ic_wep_txkey;	/* default tx key index */
-	void			*ic_wep_ctx;	/* wep crypt context */
-	uint32_t		ic_iv;		/* initial vector for wep */
-	struct ieee80211_stats	ic_stats;	/* statistics */
+	u_int8_t		ic_des_bssid[IEEE80211_ADDR_LEN];
+	void			*ic_opt_ie;	/* user-specified IE's */
+	u_int16_t		ic_opt_ie_len;	/* length of ni_opt_ie */
+	/*
+	 * Inactivity timer settings for nodes.
+	 */
+	int			ic_inact_init;	/* initial setting */
+	int			ic_inact_auth;	/* auth but not assoc setting */
+	int			ic_inact_run;	/* authorized setting */
+	int			ic_inact_probe;	/* inactive probe time */
+
+	/*
+	 * WME/WMM state.
+	 */
+	struct ieee80211_wme_state ic_wme;
+
+	/*
+	 * Cipher state/configuration.
+	 */
+	struct ieee80211_crypto_state ic_crypto;
+#define	ic_nw_keys	ic_crypto.cs_nw_keys	/* XXX compatibility */
+#define	ic_def_txkey	ic_crypto.cs_def_txkey	/* XXX compatibility */
+
+	/*
+	 * 802.1x glue.  When an authenticator attaches it
+	 * fills in this section.  We assume that when ic_ec
+	 * is setup that the methods are safe to call.
+	 */
+	const struct ieee80211_authenticator *ic_auth;
+	struct eapolcom		*ic_ec;	
+
+	/*
+	 * Access control glue.  When a control agent attaches
+	 * it fills in this section.  We assume that when ic_ac
+	 * is setup that the methods are safe to call.
+	 */
+	const struct ieee80211_aclator *ic_acl;
+	void			*ic_as;
 };
-#define	ic_if		ic_ac.ac_if
-#define	ic_softc	ic_if.if_softc
 
 #define	IEEE80211_ADDR_EQ(a1,a2)	(memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
 #define	IEEE80211_ADDR_COPY(dst,src)	memcpy(dst,src,IEEE80211_ADDR_LEN)
 
 /* ic_flags */
-#define	IEEE80211_F_ASCAN	0x00000001	/* STATUS: active scan */
-#define	IEEE80211_F_SIBSS	0x00000002	/* STATUS: start IBSS */
-#define	IEEE80211_F_WEPON	0x00000100	/* CONF: WEP enabled */
-#define	IEEE80211_F_IBSSON	0x00000200	/* CONF: IBSS creation enable */
-#define	IEEE80211_F_PMGTON	0x00000400	/* CONF: Power mgmt enable */
-#define	IEEE80211_F_DESBSSID	0x00000800	/* CONF: des_bssid is set */
-#define	IEEE80211_F_SCANAP	0x00001000	/* CONF: Scanning AP */
-#define	IEEE80211_F_ROAMING	0x00002000	/* CONF: roaming enabled */
-#define	IEEE80211_F_SWRETRY	0x00004000	/* CONF: sw tx retry enabled */
-#define	IEEE80211_F_TXPMGT	0x00018000	/* STATUS: tx power */
-#define	IEEE80211_F_TXPOW_OFF	0x00000000	/* TX Power: radio disabled */
-#define	IEEE80211_F_TXPOW_FIXED	0x00008000	/* TX Power: fixed rate */
-#define	IEEE80211_F_TXPOW_AUTO	0x00010000	/* TX Power: undefined */
-#define	IEEE80211_F_SHSLOT	0x00020000	/* CONF: short slot time */
-#define	IEEE80211_F_SHPREAMBLE	0x00040000	/* CONF: short preamble */
+/* NB: bits 0x6f available */
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */
+#define	IEEE80211_F_PRIVACY	0x00000010	/* CONF: privacy enabled */
+#define	IEEE80211_F_SCAN	0x00000080	/* STATUS: scanning */
+#define	IEEE80211_F_ASCAN	0x00000100	/* STATUS: active scan */
+#define	IEEE80211_F_SIBSS	0x00000200	/* STATUS: start IBSS */
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */
+#define	IEEE80211_F_SHSLOT	0x00000400	/* STATUS: use short slot time*/
+#define	IEEE80211_F_PMGTON	0x00000800	/* CONF: Power mgmt enable */
+#define	IEEE80211_F_DESBSSID	0x00001000	/* CONF: des_bssid is set */
+#define	IEEE80211_F_WME		0x00002000	/* CONF: enable WME use */
+#define	IEEE80211_F_ROAMING	0x00004000	/* CONF: roaming enabled (???)*/
+#define	IEEE80211_F_SWRETRY	0x00008000	/* CONF: sw tx retry enabled */
+#define IEEE80211_F_TXPOW_FIXED	0x00010000	/* TX Power: fixed rate */
+#define	IEEE80211_F_IBSSON	0x00020000	/* CONF: IBSS creation enable */
+#define	IEEE80211_F_SHPREAMBLE	0x00040000	/* STATUS: use short preamble */
+#define	IEEE80211_F_DATAPAD	0x00080000	/* CONF: do alignment pad */
 #define	IEEE80211_F_USEPROT	0x00100000	/* STATUS: protection enabled */
 #define	IEEE80211_F_USEBARKER	0x00200000	/* STATUS: use barker preamble*/
+#define	IEEE80211_F_TIMUPDATE	0x00400000	/* STATUS: update beacon tim */
+#define	IEEE80211_F_WPA1	0x00800000	/* CONF: WPA enabled */
+#define	IEEE80211_F_WPA2	0x01000000	/* CONF: WPA2 enabled */
+#define	IEEE80211_F_WPA		0x01800000	/* CONF: WPA/WPA2 enabled */
+#define	IEEE80211_F_DROPUNENC	0x02000000	/* CONF: drop unencrypted */
+#define	IEEE80211_F_COUNTERM	0x04000000	/* CONF: TKIP countermeasures */
+#define	IEEE80211_F_HIDESSID	0x08000000	/* CONF: hide SSID in beacon */
+#define	IEEE80211_F_NOBRIDGE	0x10000000	/* CONF: dis. internal bridge */
+#define	IEEE80211_F_WMEUPDATE	0x20000000	/* STATUS: update beacon wme */
 
 /* ic_caps */
 #define	IEEE80211_C_WEP		0x00000001	/* CAPABILITY: WEP available */
-#define	IEEE80211_C_IBSS	0x00000002	/* CAPABILITY: IBSS available */
-#define	IEEE80211_C_PMGT	0x00000004	/* CAPABILITY: Power mgmt */
-#define	IEEE80211_C_HOSTAP	0x00000008	/* CAPABILITY: HOSTAP avail */
-#define	IEEE80211_C_AHDEMO	0x00000010	/* CAPABILITY: Old Adhoc Demo */
-#define	IEEE80211_C_SWRETRY	0x00000020	/* CAPABILITY: sw tx retry */
-#define	IEEE80211_C_TXPMGT	0x00000040	/* CAPABILITY: tx power mgmt */
-#define	IEEE80211_C_SHSLOT	0x00000080	/* CAPABILITY: short slottime */
-#define	IEEE80211_C_SHPREAMBLE	0x00000100	/* CAPABILITY: short preamble */
-#define	IEEE80211_C_MONITOR	0x00000200	/* CAPABILITY: monitor mode */
-
-/* flags for ieee80211_fix_rate() */
-#define	IEEE80211_F_DOSORT	0x00000001	/* sort rate list */
-#define	IEEE80211_F_DOFRATE	0x00000002	/* use fixed rate */
-#define	IEEE80211_F_DONEGO	0x00000004	/* calc negotiated rate */
-#define	IEEE80211_F_DODEL	0x00000008	/* delete ignore rate */
-
-void	ieee80211_ifattach(struct ifnet *);
-void	ieee80211_ifdetach(struct ifnet *);
-void	ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t);
+#define	IEEE80211_C_TKIP	0x00000002	/* CAPABILITY: TKIP available */
+#define	IEEE80211_C_AES		0x00000004	/* CAPABILITY: AES OCB avail */
+#define	IEEE80211_C_AES_CCM	0x00000008	/* CAPABILITY: AES CCM avail */
+#define	IEEE80211_C_CKIP	0x00000020	/* CAPABILITY: CKIP available */
+#define	IEEE80211_C_IBSS	0x00000100	/* CAPABILITY: IBSS available */
+#define	IEEE80211_C_PMGT	0x00000200	/* CAPABILITY: Power mgmt */
+#define	IEEE80211_C_HOSTAP	0x00000400	/* CAPABILITY: HOSTAP avail */
+#define	IEEE80211_C_AHDEMO	0x00000800	/* CAPABILITY: Old Adhoc Demo */
+#define	IEEE80211_C_SWRETRY	0x00001000	/* CAPABILITY: sw tx retry */
+#define	IEEE80211_C_TXPMGT	0x00002000	/* CAPABILITY: tx power mgmt */
+#define	IEEE80211_C_SHSLOT	0x00004000	/* CAPABILITY: short slottime */
+#define	IEEE80211_C_SHPREAMBLE	0x00008000	/* CAPABILITY: short preamble */
+#define	IEEE80211_C_MONITOR	0x00010000	/* CAPABILITY: monitor mode */
+#define	IEEE80211_C_TKIPMIC	0x00020000	/* CAPABILITY: TKIP MIC avail */
+#define	IEEE80211_C_WPA1	0x00800000	/* CAPABILITY: WPA1 avail */
+#define	IEEE80211_C_WPA2	0x01000000	/* CAPABILITY: WPA2 avail */
+#define	IEEE80211_C_WPA		0x01800000	/* CAPABILITY: WPA1+WPA2 avail*/
+#define	IEEE80211_C_BURST	0x02000000	/* CAPABILITY: frame bursting */
+#define	IEEE80211_C_WME		0x04000000	/* CAPABILITY: WME avail */
+/* XXX protection/barker? */
+
+#define	IEEE80211_C_CRYPTO	0x0000002f	/* CAPABILITY: crypto alg's */
+
+void	ieee80211_ifattach(struct ieee80211com *);
+void	ieee80211_ifdetach(struct ieee80211com *);
+void	ieee80211_announce(struct ieee80211com *);
+void	ieee80211_media_init(struct ieee80211com *,
+		ifm_change_cb_t, ifm_stat_cb_t);
+struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]);
 int	ieee80211_media_change(struct ifnet *);
 void	ieee80211_media_status(struct ifnet *, struct ifmediareq *);
-int	ieee80211_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
-int	ieee80211_cfgget(struct ifnet *, u_long, caddr_t, struct ucred *);
-int	ieee80211_cfgset(struct ifnet *, u_long, caddr_t);
-void	ieee80211_watchdog(struct ifnet *);
-int	ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int);
+int	ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t, struct ucred *);
+int	ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t, struct ucred *);
+int	ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t);
+void	ieee80211_watchdog(struct ieee80211com *);
 int	ieee80211_rate2media(struct ieee80211com *, int,
 		enum ieee80211_phymode);
 int	ieee80211_media2rate(int);
@@ -282,14 +273,95 @@
 enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,
 		struct ieee80211_channel *);
 
-#define	IEEE80211_DEBUG
+/* 
+ * Key update synchronization methods.  XXX should not be visible.
+ */
+static __inline void
+ieee80211_key_update_begin(struct ieee80211com *ic)
+{
+	ic->ic_crypto.cs_key_update_begin(ic);
+}
+static __inline void
+ieee80211_key_update_end(struct ieee80211com *ic)
+{
+	ic->ic_crypto.cs_key_update_end(ic);
+}
+
+/*
+ * XXX these need to be here for IEEE80211_F_DATAPAD
+ */
+
+/*
+ * Return the space occupied by the 802.11 header and any
+ * padding required by the driver.  This works for a
+ * management or data frame.
+ */
+static __inline int
+ieee80211_hdrspace(struct ieee80211com *ic, const void *data)
+{
+	int size = ieee80211_hdrsize(data);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		size = roundup(size, sizeof(u_int32_t));
+	return size;
+}
+
+/*
+ * Like ieee80211_hdrspace, but handles any type of frame.
+ */
+static __inline int
+ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data)
+{
+	int size = ieee80211_anyhdrsize(data);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		size = roundup(size, sizeof(u_int32_t));
+	return size;
+}
+
+#define	IEEE80211_MSG_DEBUG	0x40000000	/* IFF_DEBUG equivalent */
+#define	IEEE80211_MSG_DUMPPKTS	0x20000000	/* IFF_LINK2 equivalant */
+#define	IEEE80211_MSG_CRYPTO	0x10000000	/* crypto work */
+#define	IEEE80211_MSG_INPUT	0x08000000	/* input handling */
+#define	IEEE80211_MSG_XRATE	0x04000000	/* rate set handling */
+#define	IEEE80211_MSG_ELEMID	0x02000000	/* element id parsing */
+#define	IEEE80211_MSG_NODE	0x01000000	/* node handling */
+#define	IEEE80211_MSG_ASSOC	0x00800000	/* association handling */
+#define	IEEE80211_MSG_AUTH	0x00400000	/* authentication handling */
+#define	IEEE80211_MSG_SCAN	0x00200000	/* scanning */
+#define	IEEE80211_MSG_OUTPUT	0x00100000	/* output handling */
+#define	IEEE80211_MSG_STATE	0x00080000	/* state machine */
+#define	IEEE80211_MSG_POWER	0x00040000	/* power save handling */
+#define	IEEE80211_MSG_DOT1X	0x00020000	/* 802.1x authenticator */
+#define	IEEE80211_MSG_DOT1XSM	0x00010000	/* 802.1x state machine */
+#define	IEEE80211_MSG_RADIUS	0x00008000	/* 802.1x radius client */
+#define	IEEE80211_MSG_RADDUMP	0x00004000	/* dump 802.1x radius packets */
+#define	IEEE80211_MSG_RADKEYS	0x00002000	/* dump 802.1x keys */
+#define	IEEE80211_MSG_WPA	0x00001000	/* WPA/RSN protocol */
+#define	IEEE80211_MSG_ACL	0x00000800	/* ACL handling */
+#define	IEEE80211_MSG_WME	0x00000400	/* WME protocol */
+
+#define	IEEE80211_MSG_ANY	0xffffffff	/* anything */
+
 #ifdef IEEE80211_DEBUG
-extern	int ieee80211_debug;
-#define	IEEE80211_DPRINTF(X)	if (ieee80211_debug) printf X
-#define	IEEE80211_DPRINTF2(X)	if (ieee80211_debug>1) printf X
+#define	IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do {	\
+	if (_ic->ic_debug & (_m))			\
+		printf(_fmt, __VA_ARGS__);		\
+} while (0)
+#define	ieee80211_msg_debug(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_DEBUG)
+#define	ieee80211_msg_dumppkts(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_DUMPPKTS)
+#define	ieee80211_msg_input(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_INPUT)
+#define	ieee80211_msg_radius(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_RADIUS)
+#define	ieee80211_msg_dumpradius(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_RADDUMP)
+#define	ieee80211_msg_dumpradkeys(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_RADKEYS)
+#define	ieee80211_msg_scan(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_SCAN)
 #else
-#define	IEEE80211_DPRINTF(X)
-#define	IEEE80211_DPRINTF2(X)
+#define	IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
 #endif
 
-#endif /* _NETPROTO_802_11_IEEE80211_VAR_H_ */
+#endif /* _NET80211_IEEE80211_VAR_H_ */
