[Midnightbsd-cvs] src: sys/net80211: sync with freebsd

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Wed Oct 1 12:38:09 EDT 2008


Log Message:
-----------
sync with freebsd

Modified Files:
--------------
    src/sys/net80211:
        _ieee80211.h (r1.1.1.1 -> r1.2)
        ieee80211.c (r1.2 -> r1.3)
        ieee80211.h (r1.1.1.1 -> r1.2)
        ieee80211_acl.c (r1.1.1.1 -> r1.2)
        ieee80211_crypto.c (r1.1.1.1 -> r1.2)
        ieee80211_crypto.h (r1.1.1.1 -> r1.2)
        ieee80211_crypto_ccmp.c (r1.1.1.2 -> r1.2)
        ieee80211_crypto_none.c (r1.1.1.1 -> r1.2)
        ieee80211_crypto_tkip.c (r1.1.1.2 -> r1.2)
        ieee80211_crypto_wep.c (r1.1.1.2 -> r1.2)
        ieee80211_freebsd.c (r1.1.1.2 -> r1.2)
        ieee80211_freebsd.h (r1.1.1.1 -> r1.2)
        ieee80211_input.c (r1.2 -> r1.3)
        ieee80211_ioctl.c (r1.2 -> r1.3)
        ieee80211_ioctl.h (r1.1.1.2 -> r1.2)
        ieee80211_node.c (r1.2 -> r1.3)
        ieee80211_node.h (r1.2 -> r1.3)
        ieee80211_output.c (r1.2 -> r1.3)
        ieee80211_proto.c (r1.2 -> r1.3)
        ieee80211_proto.h (r1.1.1.2 -> r1.2)
        ieee80211_radiotap.h (r1.1.1.2 -> r1.2)
        ieee80211_var.h (r1.2 -> r1.3)
        ieee80211_xauth.c (r1.1.1.1 -> r1.2)
    src/sys/opencrypto:
        cast.c (r1.1.1.1 -> r1.2)
        criov.c (r1.1.1.1 -> r1.2)
        crypto.c (r1.2 -> r1.3)
        cryptodev.c (r1.2 -> r1.3)
        cryptodev.h (r1.1.1.1 -> r1.2)
        cryptosoft.c (r1.1.1.1 -> r1.2)
        cryptosoft.h (r1.1.1.1 -> r1.2)
        xform.c (r1.1.1.1 -> r1.2)
        xform.h (r1.1.1.1 -> r1.2)

Added Files:
-----------
    src/sys/net80211:
        ieee80211_amrr.c (r1.1)
        ieee80211_amrr.h (r1.1)
        ieee80211_ht.c (r1.1)
        ieee80211_ht.h (r1.1)
        ieee80211_power.c (r1.1)
        ieee80211_power.h (r1.1)
        ieee80211_regdomain.c (r1.1)
        ieee80211_regdomain.h (r1.1)
        ieee80211_scan.c (r1.1)
        ieee80211_scan.h (r1.1)
        ieee80211_scan_ap.c (r1.1)
        ieee80211_scan_sta.c (r1.1)
    src/sys/opencrypto:
        cryptodev_if.m (r1.1)

Removed Files:
-------------
    src/sys/opencrypto:
        crypto_if.m

-------------- next part --------------
Index: ieee80211_acl.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_acl.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_acl.c -L sys/net80211/ieee80211_acl.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_acl.c
+++ sys/net80211/ieee80211_acl.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -30,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_acl.c,v 1.3.2.1 2005/09/03 22:40:02 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_acl.c,v 1.6 2007/06/11 03:36:54 sam Exp $");
 
 /*
  * IEEE 802.11 MAC ACL support.
@@ -70,7 +64,7 @@
 struct acl {
 	TAILQ_ENTRY(acl)	acl_list;
 	LIST_ENTRY(acl)		acl_hash;
-	u_int8_t		acl_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t			acl_macaddr[IEEE80211_ADDR_LEN];
 };
 struct aclstate {
 	acl_lock_t		as_lock;
@@ -83,7 +77,7 @@
 
 /* simple hash is enough for variation of macaddr */
 #define	ACL_HASH(addr)	\
-	(((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
+	(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
 
 MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
 
@@ -118,7 +112,7 @@
 }
 
 static __inline struct acl *
-_find_acl(struct aclstate *as, const u_int8_t *macaddr)
+_find_acl(struct aclstate *as, const uint8_t *macaddr)
 {
 	struct acl *acl;
 	int hash;
@@ -143,7 +137,7 @@
 }
 
 static int
-acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
 {
 	struct aclstate *as = ic->ic_as;
 
@@ -159,7 +153,7 @@
 }
 
 static int
-acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
 {
 	struct aclstate *as = ic->ic_as;
 	struct acl *acl, *new;
@@ -197,7 +191,7 @@
 }
 
 static int
-acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
 {
 	struct aclstate *as = ic->ic_as;
 	struct acl *acl;
--- /dev/null
+++ sys/net80211/ieee80211_scan.h
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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_scan.h,v 1.3 2007/06/30 21:39:21 thompsa Exp $
+ */
+#ifndef _NET80211_IEEE80211_SCAN_H_
+#define _NET80211_IEEE80211_SCAN_H_
+
+#define	IEEE80211_SCAN_MAX	IEEE80211_CHAN_MAX
+
+struct ieee80211_scanner;
+
+struct ieee80211_scan_ssid {
+	int		len;				/* length in bytes */
+	uint8_t		ssid[IEEE80211_NWID_LEN];	/* ssid contents */
+};
+#define	IEEE80211_SCAN_MAX_SSID	1
+
+struct ieee80211_scan_state {
+	struct ieee80211com *ss_ic;
+	const struct ieee80211_scanner *ss_ops;	/* policy hookup, see below */
+	void		*ss_priv;		/* scanner private state */
+	uint16_t	ss_flags;
+#define	IEEE80211_SCAN_NOPICK	0x0001		/* scan only, no selection */
+#define	IEEE80211_SCAN_ACTIVE	0x0002		/* active scan (probe req) */
+#define	IEEE80211_SCAN_PICK1ST	0x0004		/* ``hey sailor'' mode */
+#define	IEEE80211_SCAN_BGSCAN	0x0008		/* bg scan, exit ps at end */
+#define	IEEE80211_SCAN_ONCE	0x0010		/* do one complete pass */
+#define	IEEE80211_SCAN_GOTPICK	0x1000		/* got candidate, can stop */
+	uint8_t		ss_nssid;		/* # ssid's to probe/match */
+	struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID];
+						/* ssid's to probe/match */
+						/* ordered channel set */
+	struct ieee80211_channel *ss_chans[IEEE80211_SCAN_MAX];
+	uint16_t	ss_next;		/* ix of next chan to scan */
+	uint16_t	ss_last;		/* ix+1 of last chan to scan */
+	unsigned long	ss_mindwell;		/* min dwell on channel */
+	unsigned long	ss_maxdwell;		/* max dwell on channel */
+};
+
+/*
+ * The upper 16 bits of the flags word is used to communicate
+ * information to the scanning code that is NOT recorded in
+ * ss_flags.  It might be better to split this stuff out into
+ * a separate variable to avoid confusion.
+ */
+#define	IEEE80211_SCAN_FLUSH	0x10000		/* flush candidate table */
+#define	IEEE80211_SCAN_NOSSID	0x20000		/* don't update ssid list */
+
+struct ieee80211com;
+void	ieee80211_scan_attach(struct ieee80211com *);
+void	ieee80211_scan_detach(struct ieee80211com *);
+
+void	ieee80211_scan_dump_channels(const struct ieee80211_scan_state *);
+
+int	ieee80211_scan_update(struct ieee80211com *);
+#define	IEEE80211_SCAN_FOREVER	0x7fffffff
+int	ieee80211_start_scan(struct ieee80211com *, int flags, u_int duration,
+		u_int nssid, const struct ieee80211_scan_ssid ssids[]);
+int	ieee80211_check_scan(struct ieee80211com *, int flags, u_int duration,
+		u_int nssid, const struct ieee80211_scan_ssid ssids[]);
+int	ieee80211_bg_scan(struct ieee80211com *);
+void	ieee80211_cancel_scan(struct ieee80211com *);
+void	ieee80211_scan_next(struct ieee80211com *);
+void	ieee80211_scan_done(struct ieee80211com *);
+
+struct ieee80211_scanparams;
+void	ieee80211_add_scan(struct ieee80211com *,
+		const struct ieee80211_scanparams *,
+		const struct ieee80211_frame *,
+		int subtype, int rssi, int noise, int rstamp);
+void	ieee80211_scan_timeout(struct ieee80211com *);
+
+void	ieee80211_scan_assoc_success(struct ieee80211com *,
+		const uint8_t mac[IEEE80211_ADDR_LEN]);
+enum {
+	IEEE80211_SCAN_FAIL_TIMEOUT	= 1,	/* no response to mgmt frame */
+	IEEE80211_SCAN_FAIL_STATUS	= 2	/* negative response to " " */
+};
+void	ieee80211_scan_assoc_fail(struct ieee80211com *,
+		const uint8_t mac[IEEE80211_ADDR_LEN], int reason);
+void	ieee80211_scan_flush(struct ieee80211com *);
+
+struct ieee80211_scan_entry;
+typedef void ieee80211_scan_iter_func(void *,
+		const struct ieee80211_scan_entry *);
+void	ieee80211_scan_iterate(struct ieee80211com *,
+		ieee80211_scan_iter_func, void *);
+
+/*
+ * Parameters supplied when adding/updating an entry in a
+ * scan cache.  Pointer variables should be set to NULL
+ * if no data is available.  Pointer references can be to
+ * local data; any information that is saved will be copied.
+ * All multi-byte values must be in host byte order.
+ */
+struct ieee80211_scanparams {
+	uint16_t	capinfo;	/* 802.11 capabilities */
+	uint16_t	fhdwell;	/* FHSS dwell interval */
+	struct ieee80211_channel *curchan;
+	uint8_t		bchan;		/* chan# advertised inside beacon */
+	uint8_t		fhindex;
+	uint8_t		erp;
+	uint16_t	bintval;
+	uint8_t		timoff;
+	uint8_t		*tim;
+	uint8_t		*tstamp;
+	uint8_t		*country;
+	uint8_t		*ssid;
+	uint8_t		*rates;
+	uint8_t		*xrates;
+	uint8_t		*doth;
+	uint8_t		*wpa;
+	uint8_t		*rsn;
+	uint8_t		*wme;
+	uint8_t		*htcap;
+	uint8_t		*htinfo;
+	uint8_t		*ath;
+};
+
+/*
+ * Scan cache entry format used when exporting data from a policy
+ * module; this data may be represented some other way internally.
+ */
+struct ieee80211_scan_entry {
+	uint8_t		se_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t		se_bssid[IEEE80211_ADDR_LEN];
+	uint8_t		se_ssid[2+IEEE80211_NWID_LEN];
+	uint8_t		se_rates[2+IEEE80211_RATE_MAXSIZE];
+	uint8_t		se_xrates[2+IEEE80211_RATE_MAXSIZE];
+	uint32_t	se_rstamp;	/* recv timestamp */
+	union {
+		uint8_t		data[8];
+		uint64_t	tsf;
+	} se_tstamp;			/* from last rcv'd beacon */
+	uint16_t	se_intval;	/* beacon interval (host byte order) */
+	uint16_t	se_capinfo;	/* capabilities (host byte order) */
+	struct ieee80211_channel *se_chan;/* channel where sta found */
+	uint16_t	se_timoff;	/* byte offset to TIM ie */
+	uint16_t	se_fhdwell;	/* FH only (host byte order) */
+	uint8_t		se_fhindex;	/* FH only */
+	uint8_t		se_erp;		/* ERP from beacon/probe resp */
+	int8_t		se_rssi;	/* avg'd recv ssi */
+	int8_t		se_noise;	/* noise floor */
+	uint8_t		se_dtimperiod;	/* DTIM period */
+	uint8_t		*se_wpa_ie;	/* captured WPA ie */
+	uint8_t		*se_rsn_ie;	/* captured RSN ie */
+	uint8_t		*se_wme_ie;	/* captured WME ie */
+	uint8_t		*se_htcap_ie;	/* captured HTP cap ie */
+	uint8_t		*se_htinfo_ie;	/* captured HTP info ie */
+	uint8_t		*se_ath_ie;	/* captured Atheros ie */
+	u_int		se_age;		/* age of entry (0 on create) */
+};
+MALLOC_DECLARE(M_80211_SCAN);
+
+/*
+ * Template for an in-kernel scan policy module.
+ * Modules register with the scanning code and are
+ * typically loaded as needed.
+ */
+struct ieee80211_scanner {
+	const char *scan_name;		/* printable name */
+	int	(*scan_attach)(struct ieee80211_scan_state *);
+	int	(*scan_detach)(struct ieee80211_scan_state *);
+	int	(*scan_start)(struct ieee80211_scan_state *,
+			struct ieee80211com *);
+	int	(*scan_restart)(struct ieee80211_scan_state *,
+			struct ieee80211com *);
+	int	(*scan_cancel)(struct ieee80211_scan_state *,
+			struct ieee80211com *);
+	int	(*scan_end)(struct ieee80211_scan_state *,
+			struct ieee80211com *);
+	int	(*scan_flush)(struct ieee80211_scan_state *);
+	/* add an entry to the cache */
+	int	(*scan_add)(struct ieee80211_scan_state *,
+			const struct ieee80211_scanparams *,
+			const struct ieee80211_frame *,
+			int subtype, int rssi, int noise, int rstamp);
+	/* age and/or purge entries in the cache */
+	void	(*scan_age)(struct ieee80211_scan_state *);
+	/* note that association failed for an entry */
+	void	(*scan_assoc_fail)(struct ieee80211_scan_state *,
+			const uint8_t macaddr[IEEE80211_ADDR_LEN],
+			int reason);
+	/* note that association succeed for an entry */
+	void	(*scan_assoc_success)(struct ieee80211_scan_state *,
+			const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+	/* iterate over entries in the scan cache */
+	void	(*scan_iterate)(struct ieee80211_scan_state *,
+			ieee80211_scan_iter_func *, void *);
+};
+void	ieee80211_scanner_register(enum ieee80211_opmode,
+		const struct ieee80211_scanner *);
+void	ieee80211_scanner_unregister(enum ieee80211_opmode,
+		const struct ieee80211_scanner *);
+void	ieee80211_scanner_unregister_all(const struct ieee80211_scanner *);
+const struct ieee80211_scanner *ieee80211_scanner_get(enum ieee80211_opmode);
+#endif /* _NET80211_IEEE80211_SCAN_H_ */
Index: ieee80211_output.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_output.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_output.c -L sys/net80211/ieee80211_output.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_output.c
+++ sys/net80211/ieee80211_output.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.26.2.7 2006/03/23 23:28:43 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.54.2.3 2007/12/07 05:46:08 kmacy Exp $");
 
 #include "opt_inet.h"
 
@@ -51,6 +45,7 @@
 #include <net/if_vlan_var.h>
 
 #include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
 
 #ifdef INET
 #include <netinet/in.h> 
@@ -59,6 +54,16 @@
 #include <netinet/ip.h>
 #endif
 
+#define	ETHER_HEADER_COPY(dst, src) \
+	memcpy(dst, src, sizeof(struct ether_header))
+
+static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic,
+	struct mbuf *m1, const struct ether_header *eh1,
+	struct mbuf *m2, const struct ether_header *eh2);
+static int ieee80211_fragment(struct ieee80211com *, struct mbuf *,
+	u_int hdrsize, u_int ciphdrsize, u_int mtu);
+static	void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
+
 #ifdef IEEE80211_DEBUG
 /*
  * Decide if an outbound management frame should be
@@ -88,9 +93,9 @@
 	struct ieee80211_node *ni,
 	struct ieee80211_frame *wh,
 	int type,
-	const u_int8_t sa[IEEE80211_ADDR_LEN],
-	const u_int8_t da[IEEE80211_ADDR_LEN],
-	const u_int8_t bssid[IEEE80211_ADDR_LEN])
+	const uint8_t sa[IEEE80211_ADDR_LEN],
+	const uint8_t da[IEEE80211_ADDR_LEN],
+	const uint8_t bssid[IEEE80211_ADDR_LEN])
 {
 #define	WH4(wh)	((struct ieee80211_frame_addr4 *)wh)
 
@@ -116,6 +121,14 @@
 			IEEE80211_ADDR_COPY(wh->i_addr2, bssid);
 			IEEE80211_ADDR_COPY(wh->i_addr3, sa);
 			break;
+		case IEEE80211_M_WDS:
+			wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+			/* XXX cheat, bssid holds RA */
+			IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
+			IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+			IEEE80211_ADDR_COPY(wh->i_addr3, da);
+			IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
+			break;
 		case IEEE80211_M_MONITOR:	/* NB: to quiet compiler */
 			break;
 		}
@@ -125,11 +138,11 @@
 		IEEE80211_ADDR_COPY(wh->i_addr2, sa);
 		IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
 	}
-	*(u_int16_t *)&wh->i_dur[0] = 0;
+	*(uint16_t *)&wh->i_dur[0] = 0;
 	/* NB: use non-QoS tid */
-	*(u_int16_t *)&wh->i_seq[0] =
-	    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
-	ni->ni_txseqs[0]++;
+	*(uint16_t *)&wh->i_seq[0] =
+	    htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT);
+	ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
 #undef WH4
 }
 
@@ -140,9 +153,9 @@
  * dispatched to the driver, then it is responsible for freeing the
  * reference (and potentially free'ing up any associated storage).
  */
-static int
+int
 ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
-    struct mbuf *m, int type, int timer)
+    struct mbuf *m, int type)
 {
 	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_frame *wh;
@@ -178,6 +191,11 @@
 			ether_sprintf(wh->i_addr1), __func__);
 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
 	}
+	if (ni->ni_flags & IEEE80211_NODE_QOS) {
+		/* NB: force all management frames to the highest queue */
+		M_WME_SETAC(m, WME_AC_VO);
+	} else
+		M_WME_SETAC(m, WME_AC_BE);
 #ifdef IEEE80211_DEBUG
 	/* avoid printing too many frames */
 	if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
@@ -192,15 +210,134 @@
 #endif
 	IEEE80211_NODE_STAT(ni, tx_mgmt);
 	IF_ENQUEUE(&ic->ic_mgtq, m);
-	if (timer) {
+	if_start(ifp);
+	ifp->if_opackets++;
+
+	return 0;
+}
+
+/*
+ * Raw packet transmit stub for legacy drivers.
+ * Send the packet through the mgt q so we bypass
+ * the normal encapsulation work.
+ */
+int
+ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+	const struct ieee80211_bpf_params *params)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ifnet *ifp = ic->ic_ifp;
+
+	m->m_pkthdr.rcvif = (void *) ni;
+	IF_ENQUEUE(&ic->ic_mgtq, m);
+	if_start(ifp);
+	ifp->if_opackets++;
+
+	return 0;
+}
+
+/*
+ * 802.11 output routine. This is (currently) used only to
+ * connect bpf write calls to the 802.11 layer for injecting
+ * raw 802.11 frames.  Note we locate the ieee80211com from
+ * the ifnet using a spare field setup at attach time.  This
+ * will go away when the virtual ap support comes in.
+ */
+int
+ieee80211_output(struct ifnet *ifp, struct mbuf *m,
+	struct sockaddr *dst, struct rtentry *rt0)
+{
+#define senderr(e) do { error = (e); goto bad;} while (0)
+	struct ieee80211com *ic = ifp->if_llsoftc;	/* XXX */
+	struct ieee80211_node *ni = NULL;
+	struct ieee80211_frame *wh;
+	int error;
+
+	/*
+	 * Hand to the 802.3 code if not tagged as
+	 * a raw 802.11 frame.
+	 */
+	if (dst->sa_family != AF_IEEE80211)
+		return ether_output(ifp, m, dst, rt0);
+#ifdef MAC
+	error = mac_check_ifnet_transmit(ifp, m);
+	if (error)
+		senderr(error);
+#endif
+	if (ifp->if_flags & IFF_MONITOR)
+		senderr(ENETDOWN);
+	if ((ifp->if_flags & IFF_UP) == 0)
+		senderr(ENETDOWN);
+
+	/* XXX bypass bridge, pfil, carp, etc. */
+
+	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
+		senderr(EIO);	/* XXX */
+	wh = mtod(m, struct ieee80211_frame *);
+	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+	    IEEE80211_FC0_VERSION_0)
+		senderr(EIO);	/* XXX */
+
+	/* locate destination node */
+	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+	case IEEE80211_FC1_DIR_NODS:
+	case IEEE80211_FC1_DIR_FROMDS:
+		ni = ieee80211_find_txnode(ic, wh->i_addr1);
+		break;
+	case IEEE80211_FC1_DIR_TODS:
+	case IEEE80211_FC1_DIR_DSTODS:
+		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
+			senderr(EIO);	/* XXX */
+		ni = ieee80211_find_txnode(ic, wh->i_addr3);
+		break;
+	default:
+		senderr(EIO);	/* XXX */
+	}
+	if (ni == NULL) {
 		/*
-		 * Set the mgt frame timeout.
+		 * Permit packets w/ bpf params through regardless
+		 * (see below about sa_len).
 		 */
-		ic->ic_mgt_timer = timer;
-		ifp->if_timer = 1;
+		if (dst->sa_len == 0)
+			senderr(EHOSTUNREACH);
+		ni = ieee80211_ref_node(ic->ic_bss);
 	}
-	if_start(ifp);
-	return 0;
+
+	/* XXX ctrl frames should go through */
+	if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+	    (m->m_flags & M_PWR_SAV) == 0) {
+		/*
+		 * Station in power save mode; pass the frame
+		 * to the 802.11 layer and continue.  We'll get
+		 * the frame back when the time is right.
+		 */
+		ieee80211_pwrsave(ni, m);
+		error = 0;
+		goto reclaim;
+	}
+
+	/* calculate priority so drivers can find the tx queue */
+	/* XXX assumes an 802.3 frame */
+	if (ieee80211_classify(ic, m, ni))
+		senderr(EIO);		/* XXX */
+
+	BPF_MTAP(ifp, m);
+	/*
+	 * NB: DLT_IEEE802_11_RADIO identifies the parameters are
+	 * present by setting the sa_len field of the sockaddr (yes,
+	 * this is a hack).
+	 * NB: we assume sa_data is suitably aligned to cast.
+	 */
+	return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *)
+		(dst->sa_len ? dst->sa_data : NULL));
+bad:
+	if (m != NULL)
+		m_freem(m);
+reclaim:
+	if (ni != NULL)
+		ieee80211_free_node(ni);
+	return error;
+#undef senderr
 }
 
 /*
@@ -218,13 +355,14 @@
 	struct mbuf *m;
 	struct ieee80211_frame *wh;
 
-	MGETHDR(m, M_NOWAIT, MT_HEADER);
+	MGETHDR(m, M_NOWAIT, MT_DATA);
 	if (m == NULL) {
 		/* XXX debug msg */
-		ic->ic_stats.is_tx_nobuf++;
 		ieee80211_unref_node(&ni);
+		ic->ic_stats.is_tx_nobuf++;
 		return ENOMEM;
 	}
+	MH_ALIGN(m, sizeof(struct ieee80211_frame));
 	m->m_pkthdr.rcvif = (void *) ni;
 
 	wh = mtod(m, struct ieee80211_frame *);
@@ -233,9 +371,11 @@
 		ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
 	/* NB: power management bit is never sent by an AP */
 	if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
-	    ic->ic_opmode != IEEE80211_M_HOSTAP)
+	    ic->ic_opmode != IEEE80211_M_HOSTAP &&
+	    ic->ic_opmode != IEEE80211_M_WDS)
 		wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
 	m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+	M_WME_SETAC(m, WME_AC_BE);
 
 	IEEE80211_NODE_STAT(ni, tx_data);
 
@@ -276,64 +416,32 @@
 	 */
 	v_wme_ac = 0;
 	if (ni->ni_vlan != 0) {
-		 struct m_tag *mtag = VLAN_OUTPUT_TAG(ic->ic_ifp, m);
-		 if (mtag == NULL) {
+		 if ((m->m_flags & M_VLANTAG) == 0) {
 			IEEE80211_NODE_STAT(ni, tx_novlantag);
 			return 1;
 		}
-		if (EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)) !=
+		if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) !=
 		    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;
-		}
+		v_wme_ac = TID_TO_WME_AC(EVL_PRIOFTAG(ni->ni_vlan));
 	}
 
 #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));
+		uint8_t tos;
 		/*
-		 * IP frame, map the TOS field.
+		 * IP frame, map the DSCP bits from 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;
-		}
+		/* XXX m_copydata may be too slow for fast path */
+		/* NB: ip header may not be in first mbuf */
+		m_copydata(m, sizeof(struct ether_header) +
+		    offsetof(struct ip, ip_tos), sizeof(tos), &tos);
+		tos >>= 5;		/* NB: ECN + low 3 bits of DSCP */
+		d_wme_ac = TID_TO_WME_AC(tos);
 	} else {
 #endif /* INET */
 		d_wme_ac = WME_AC_BE;
@@ -378,7 +486,7 @@
 	struct ieee80211_key *key, struct mbuf *m)
 {
 #define	TO_BE_RECLAIMED	(sizeof(struct ether_header) - sizeof(struct llc))
-	int needed_space = hdrsize;
+	int needed_space = ic->ic_headroom + hdrsize;
 
 	if (key != NULL) {
 		/* XXX belongs in crypto code? */
@@ -450,7 +558,6 @@
 #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
@@ -459,9 +566,9 @@
 static __inline struct ieee80211_key *
 ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
-	if (KEY_UNDEFINED(ni->ni_ucastkey)) {
+	if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
 		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
-		    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+		    IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
 			return NULL;
 		return &ic->ic_nw_keys[ic->ic_def_txkey];
 	} else {
@@ -478,7 +585,7 @@
 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]))
+	    IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
 		return NULL;
 	return &ic->ic_nw_keys[ic->ic_def_txkey];
 }
@@ -497,8 +604,13 @@
 	struct ieee80211_frame *wh;
 	struct ieee80211_key *key;
 	struct llc *llc;
-	int hdrsize, datalen, addqos;
+	int hdrsize, datalen, addqos, txfrag, isff;
 
+	/*
+	 * Copy existing Ethernet header to a safe place.  The
+	 * rest of the code assumes it's ok to strip it when
+	 * reorganizing state for the final encapsulation.
+	 */
 	KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
 	memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
 
@@ -526,6 +638,7 @@
 			    ether_sprintf(eh.ether_dhost), __func__,
 			    ic->ic_def_txkey);
 			ic->ic_stats.is_tx_nodefkey++;
+			goto bad;
 		}
 	} else
 		key = NULL;
@@ -537,29 +650,83 @@
 	 * once negotiated in which case we'll need to make this
 	 * configurable.
 	 */
-	addqos = (ni->ni_flags & IEEE80211_NODE_QOS) &&
+	addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) &&
 		 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;
-	}
+		hdrsize = roundup(hdrsize, sizeof(uint32_t));
 
-	/* 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;
-	llc->llc_control = LLC_UI;
-	llc->llc_snap.org_code[0] = 0;
-	llc->llc_snap.org_code[1] = 0;
-	llc->llc_snap.org_code[2] = 0;
-	llc->llc_snap.ether_type = eh.ether_type;
+	if ((isff = m->m_flags & M_FF) != 0) {
+		struct mbuf *m2;
+		struct ether_header eh2;
+
+		/*
+		 * Fast frame encapsulation.  There must be two packets
+		 * chained with m_nextpkt.  We do header adjustment for
+		 * each, add the tunnel encapsulation, and then concatenate
+		 * the mbuf chains to form a single frame for transmission.
+		 */
+		m2 = m->m_nextpkt;
+		if (m2 == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+				"%s: only one frame\n", __func__);
+			goto bad;
+		}
+		m->m_nextpkt = NULL;
+		/*
+		 * Include fast frame headers in adjusting header
+		 * layout; this allocates space according to what
+		 * ieee80211_encap_fastframe will do.
+		 */
+		m = ieee80211_mbuf_adjust(ic,
+			hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 +
+			    sizeof(struct ether_header),
+			key, m);
+		if (m == NULL) {
+			/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+			m_freem(m2);
+			goto bad;
+		}
+		/*
+		 * Copy second frame's Ethernet header out of line
+		 * and adjust for encapsulation headers.  Note that
+		 * we make room for padding in case there isn't room
+		 * at the end of first frame.
+		 */
+		KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
+		memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header));
+		m2 = ieee80211_mbuf_adjust(ic,
+			ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header),
+			NULL, m2);
+		if (m2 == NULL) {
+			/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+			goto bad;
+		}
+		m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2);
+		if (m == NULL)
+			goto bad;
+	} else {
+		/*
+		 * Normal frame.
+		 */
+		m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+		if (m == NULL) {
+			/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+			goto bad;
+		}
+		/* NB: this could be optimized 'cuz 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;
+		llc->llc_control = LLC_UI;
+		llc->llc_snap.org_code[0] = 0;
+		llc->llc_snap.org_code[1] = 0;
+		llc->llc_snap.org_code[2] = 0;
+		llc->llc_snap.ether_type = eh.ether_type;
+	}
 	datalen = m->m_pkthdr.len;		/* NB: w/o 802.11 header */
 
 	M_PREPEND(m, hdrsize, M_DONTWAIT);
@@ -569,7 +736,7 @@
 	}
 	wh = mtod(m, struct ieee80211_frame *);
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
-	*(u_int16_t *)wh->i_dur = 0;
+	*(uint16_t *)wh->i_dur = 0;
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
 		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
@@ -595,6 +762,7 @@
 		IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
 		break;
 	case IEEE80211_M_MONITOR:
+	case IEEE80211_M_WDS:
 		goto bad;
 	}
 	if (m->m_flags & M_MORE_DATA)
@@ -608,19 +776,53 @@
 		/* map from access class/queue to 11e header priorty value */
 		tid = WME_AC_TO_TID(ac);
 		qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+		/*
+		 * Check if A-MPDU tx aggregation is setup or if we
+		 * should try to enable it.  The sta must be associated
+		 * with HT and A-MPDU enabled for use.  On the first
+		 * frame that goes out We issue an ADDBA request and
+		 * wait for a reply.  The frame being encapsulated
+		 * will go out w/o using A-MPDU, or possibly it might
+		 * be collected by the driver and held/retransmit.
+		 * ieee80211_ampdu_request handles staggering requests
+		 * in case the receiver NAK's us or we are otherwise
+		 * unable to establish a BA stream.
+		 */
+		if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
+		    (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
+			struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
+
+			if (IEEE80211_AMPDU_RUNNING(tap)) {
+				/*
+				 * Operational, mark frame for aggregation.
+				 */
+				qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
+			} else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
+				/*
+				 * Not negotiated yet, request service.
+				 */
+				ieee80211_ampdu_request(ni, tap);
+			}
+		}
+		/* XXX works even when BA marked above */
 		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
-			qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
+			qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
 		qwh->i_qos[1] = 0;
 		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
 
-		*(u_int16_t *)wh->i_seq =
+		*(uint16_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]++;
-	}
+		*(uint16_t *)wh->i_seq =
+		    htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT);
+		ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
+	}
+	/* check if xmit fragmentation is required */
+	txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold &&
+	    !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+	    (ic->ic_caps & IEEE80211_C_TXFRAG) &&
+	    !isff);		/* NB: don't fragment ff's */
 	if (key != NULL) {
 		/*
 		 * IEEE 802.1X: send EAPOL frames always in the clear.
@@ -629,10 +831,10 @@
 		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)))) {
+		      !IEEE80211_KEY_UNDEFINED(key) :
+		      !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) {
 			wh->i_fc[1] |= IEEE80211_FC1_WEP;
-			/* XXX do fragmentation */
-			if (!ieee80211_crypto_enmic(ic, key, m, 0)) {
+			if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) {
 				IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
 				    "[%s] enmic failed, discard frame\n",
 				    ether_sprintf(eh.ether_dhost));
@@ -641,8 +843,21 @@
 			}
 		}
 	}
+	/*
+	 * NB: frag flags may leak from above; they should only
+	 *     be set on return to the caller if we fragment at
+	 *     the 802.11 layer.
+	 */
+	m->m_flags &= ~(M_FRAG | M_FIRSTFRAG);
+	if (txfrag && !ieee80211_fragment(ic, m, hdrsize,
+	    key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold))
+		goto bad;
 
 	IEEE80211_NODE_STAT(ni, tx_data);
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+		IEEE80211_NODE_STAT(ni, tx_mcast);
+	else
+		IEEE80211_NODE_STAT(ni, tx_ucast);
 	IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
 
 	return m;
@@ -653,10 +868,231 @@
 }
 
 /*
+ * Do Ethernet-LLC encapsulation for each payload in a fast frame
+ * tunnel encapsulation.  The frame is assumed to have an Ethernet
+ * header at the front that must be stripped before prepending the
+ * LLC followed by the Ethernet header passed in (with an Ethernet
+ * type that specifies the payload size).
+ */
+static struct mbuf *
+ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
+	const struct ether_header *eh)
+{
+	struct llc *llc;
+	uint16_t payload;
+
+	/* XXX optimize by combining m_adj+M_PREPEND */
+	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
+	llc = mtod(m, struct llc *);
+	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+	llc->llc_control = LLC_UI;
+	llc->llc_snap.org_code[0] = 0;
+	llc->llc_snap.org_code[1] = 0;
+	llc->llc_snap.org_code[2] = 0;
+	llc->llc_snap.ether_type = eh->ether_type;
+	payload = m->m_pkthdr.len;		/* NB: w/o Ethernet header */
+
+	M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
+	if (m == NULL) {		/* XXX cannot happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+			"%s: no space for ether_header\n", __func__);
+		ic->ic_stats.is_tx_nobuf++;
+		return NULL;
+	}
+	ETHER_HEADER_COPY(mtod(m, void *), eh);
+	mtod(m, struct ether_header *)->ether_type = htons(payload);
+	return m;
+}
+
+/*
+ * Do fast frame tunnel encapsulation.  The two frames and
+ * Ethernet headers are supplied.  The caller is assumed to
+ * have arrange for space in the mbuf chains for encapsulating
+ * headers (to avoid major mbuf fragmentation).
+ *
+ * The encapsulated frame is returned or NULL if there is a
+ * problem (should not happen).
+ */
+static struct mbuf *
+ieee80211_encap_fastframe(struct ieee80211com *ic,
+	struct mbuf *m1, const struct ether_header *eh1,
+	struct mbuf *m2, const struct ether_header *eh2)
+{
+	struct llc *llc;
+	struct mbuf *m;
+	int pad;
+
+	/*
+	 * First, each frame gets a standard encapsulation.
+	 */
+	m1 = ieee80211_encap1(ic, m1, eh1);
+	if (m1 == NULL) {
+		m_freem(m2);
+		return NULL;
+	}
+	m2 = ieee80211_encap1(ic, m2, eh2);
+	if (m2 == NULL) {
+		m_freem(m1);
+		return NULL;
+	}
+
+	/*
+	 * Pad leading frame to a 4-byte boundary.  If there
+	 * is space at the end of the first frame, put it
+	 * there; otherwise prepend to the front of the second
+	 * frame.  We know doing the second will always work
+	 * because we reserve space above.  We prefer appending
+	 * as this typically has better DMA alignment properties.
+	 */
+	for (m = m1; m->m_next != NULL; m = m->m_next)
+		;
+	pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len;
+	if (pad) {
+		if (M_TRAILINGSPACE(m) < pad) {		/* prepend to second */
+			m2->m_data -= pad;
+			m2->m_len += pad;
+			m2->m_pkthdr.len += pad;
+		} else {				/* append to first */
+			m->m_len += pad;
+			m1->m_pkthdr.len += pad;
+		}
+	}
+
+	/*
+	 * Now, stick 'em together and prepend the tunnel headers;
+	 * first the Atheros tunnel header (all zero for now) and
+	 * then a special fast frame LLC.
+	 *
+	 * XXX optimize by prepending together
+	 */
+	m->m_next = m2;			/* NB: last mbuf from above */
+	m1->m_pkthdr.len += m2->m_pkthdr.len;
+	M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT);
+	if (m1 == NULL) {		/* XXX cannot happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+			"%s: no space for tunnel header\n", __func__);
+		ic->ic_stats.is_tx_nobuf++;
+		return NULL;
+	}
+	memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
+
+	M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
+	if (m1 == NULL) {		/* XXX cannot happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+			"%s: no space for llc header\n", __func__);
+		ic->ic_stats.is_tx_nobuf++;
+		return NULL;
+	}
+	llc = mtod(m1, struct llc *);
+	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+	llc->llc_control = LLC_UI;
+	llc->llc_snap.org_code[0] = ATH_FF_SNAP_ORGCODE_0;
+	llc->llc_snap.org_code[1] = ATH_FF_SNAP_ORGCODE_1;
+	llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2;
+	llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE);
+
+	ic->ic_stats.is_ff_encap++;
+
+	return m1;
+}
+
+/*
+ * Fragment the frame according to the specified mtu.
+ * The size of the 802.11 header (w/o padding) is provided
+ * so we don't need to recalculate it.  We create a new
+ * mbuf for each fragment and chain it through m_nextpkt;
+ * we might be able to optimize this by reusing the original
+ * packet's mbufs but that is significantly more complicated.
+ */
+static int
+ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
+	u_int hdrsize, u_int ciphdrsize, u_int mtu)
+{
+	struct ieee80211_frame *wh, *whf;
+	struct mbuf *m, *prev, *next;
+	u_int totalhdrsize, fragno, fragsize, off, remainder, payload;
+
+	KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?"));
+	KASSERT(m0->m_pkthdr.len > mtu,
+		("pktlen %u mtu %u", m0->m_pkthdr.len, mtu));
+
+	wh = mtod(m0, struct ieee80211_frame *);
+	/* NB: mark the first frag; it will be propagated below */
+	wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG;
+	totalhdrsize = hdrsize + ciphdrsize;
+	fragno = 1;
+	off = mtu - ciphdrsize;
+	remainder = m0->m_pkthdr.len - off;
+	prev = m0;
+	do {
+		fragsize = totalhdrsize + remainder;
+		if (fragsize > mtu)
+			fragsize = mtu;
+		KASSERT(fragsize < MCLBYTES,
+			("fragment size %u too big!", fragsize));
+		if (fragsize > MHLEN)
+			m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+		else
+			m = m_gethdr(M_DONTWAIT, MT_DATA);
+		if (m == NULL)
+			goto bad;
+		/* leave room to prepend any cipher header */
+		m_align(m, fragsize - ciphdrsize);
+
+		/*
+		 * Form the header in the fragment.  Note that since
+		 * we mark the first fragment with the MORE_FRAG bit
+		 * it automatically is propagated to each fragment; we
+		 * need only clear it on the last fragment (done below).
+		 */
+		whf = mtod(m, struct ieee80211_frame *);
+		memcpy(whf, wh, hdrsize);
+		*(uint16_t *)&whf->i_seq[0] |= htole16(
+			(fragno & IEEE80211_SEQ_FRAG_MASK) <<
+				IEEE80211_SEQ_FRAG_SHIFT);
+		fragno++;
+
+		payload = fragsize - totalhdrsize;
+		/* NB: destination is known to be contiguous */
+		m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize);
+		m->m_len = hdrsize + payload;
+		m->m_pkthdr.len = hdrsize + payload;
+		m->m_flags |= M_FRAG;
+
+		/* chain up the fragment */
+		prev->m_nextpkt = m;
+		prev = m;
+
+		/* deduct fragment just formed */
+		remainder -= payload;
+		off += payload;
+	} while (remainder != 0);
+	whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG;
+
+	/* strip first mbuf now that everything has been copied */
+	m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize)));
+	m0->m_flags |= M_FIRSTFRAG | M_FRAG;
+
+	ic->ic_stats.is_tx_fragframes++;
+	ic->ic_stats.is_tx_frags += fragno-1;
+
+	return 1;
+bad:
+	/* reclaim fragments but leave original frame for caller to free */
+	for (m = m0->m_nextpkt; m != NULL; m = next) {
+		next = m->m_nextpkt;
+		m->m_nextpkt = NULL;		/* XXX paranoid */
+		m_freem(m);
+	}
+	m0->m_nextpkt = NULL;
+	return 0;
+}
+
+/*
  * Add a supported rates element id to a frame.
  */
-static u_int8_t *
-ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+static uint8_t *
+ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
 {
 	int nrates;
 
@@ -672,8 +1108,8 @@
 /*
  * Add an extended supported rates element id to a frame.
  */
-static u_int8_t *
-ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+static uint8_t *
+ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
 {
 	/*
 	 * Add an extended supported rates element if operating in 11g mode.
@@ -691,8 +1127,8 @@
 /* 
  * Add an ssid elemet to a frame.
  */
-static u_int8_t *
-ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
+static uint8_t *
+ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
 {
 	*frm++ = IEEE80211_ELEMID_SSID;
 	*frm++ = len;
@@ -703,10 +1139,10 @@
 /*
  * Add an erp element to a frame.
  */
-static u_int8_t *
-ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
+static uint8_t *
+ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
 {
-	u_int8_t erp;
+	uint8_t erp;
 
 	*frm++ = IEEE80211_ELEMID_ERP;
 	*frm++ = 1;
@@ -721,8 +1157,8 @@
 	return frm;
 }
 
-static u_int8_t *
-ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie)
+static uint8_t *
+ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie)
 {
 #define	WPA_OUI_BYTES		0x00, 0x50, 0xf2
 #define	ADDSHORT(frm, v) do {			\
@@ -734,8 +1170,8 @@
 	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] = {
+	static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
+	static const uint8_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 */
@@ -743,15 +1179,15 @@
 		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
 		{ WPA_OUI_BYTES, WPA_CSE_NULL },
 	};
-	static const u_int8_t wep104_suite[4] =
+	static const uint8_t wep104_suite[4] =
 		{ WPA_OUI_BYTES, WPA_CSE_WEP104 };
-	static const u_int8_t key_mgt_unspec[4] =
+	static const uint8_t key_mgt_unspec[4] =
 		{ WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
-	static const u_int8_t key_mgt_psk[4] =
+	static const uint8_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;
+	uint8_t *frm = ie;
+	uint8_t *selcnt;
 
 	*frm++ = IEEE80211_ELEMID_VENDOR;
 	*frm++ = 0;				/* length filled in below */
@@ -807,8 +1243,8 @@
 #undef WPA_OUI_BYTES
 }
 
-static u_int8_t *
-ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie)
+static uint8_t *
+ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie)
 {
 #define	RSN_OUI_BYTES		0x00, 0x0f, 0xac
 #define	ADDSHORT(frm, v) do {			\
@@ -820,7 +1256,7 @@
 	memcpy(frm, sel, 4);			\
 	frm += 4;				\
 } while (0)
-	static const u_int8_t cipher_suite[][4] = {
+	static const uint8_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 },
@@ -828,15 +1264,15 @@
 		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
 		{ RSN_OUI_BYTES, RSN_CSE_NULL },
 	};
-	static const u_int8_t wep104_suite[4] =
+	static const uint8_t wep104_suite[4] =
 		{ RSN_OUI_BYTES, RSN_CSE_WEP104 };
-	static const u_int8_t key_mgt_unspec[4] =
+	static const uint8_t key_mgt_unspec[4] =
 		{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
-	static const u_int8_t key_mgt_psk[4] =
+	static const uint8_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;
+	uint8_t *frm = ie;
+	uint8_t *selcnt;
 
 	*frm++ = IEEE80211_ELEMID_RSN;
 	*frm++ = 0;				/* length filled in below */
@@ -893,8 +1329,8 @@
 /*
  * Add a WPA/RSN element to a frame.
  */
-static u_int8_t *
-ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic)
+static uint8_t *
+ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic)
 {
 
 	KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
@@ -909,8 +1345,8 @@
 /*
  * 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 uint8_t *
+ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme)
 {
 	static const struct ieee80211_wme_info info = {
 		.wme_id		= IEEE80211_ELEMID_VENDOR,
@@ -928,8 +1364,8 @@
 /*
  * 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)
+static uint8_t *
+ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme)
 {
 #define	SM(_v, _f)	(((_v) << _f##_S) & _f)
 #define	ADDSHORT(frm, v) do {			\
@@ -970,23 +1406,48 @@
 }
 #undef WME_OUI_BYTES
 
+#define	ATH_OUI_BYTES		0x00, 0x03, 0x7f
+/*
+ * Add a WME information element to a frame.
+ */
+static uint8_t *
+ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix)
+{
+	static const struct ieee80211_ath_ie info = {
+		.ath_id		= IEEE80211_ELEMID_VENDOR,
+		.ath_len	= sizeof(struct ieee80211_ath_ie) - 2,
+		.ath_oui	= { ATH_OUI_BYTES },
+		.ath_oui_type	= ATH_OUI_TYPE,
+		.ath_oui_subtype= ATH_OUI_SUBTYPE,
+		.ath_version	= ATH_OUI_VERSION,
+	};
+	struct ieee80211_ath_ie *ath = (struct ieee80211_ath_ie *) frm;
+
+	memcpy(frm, &info, sizeof(info));
+	ath->ath_capability = caps;
+	ath->ath_defkeyix[0] = (defkeyix & 0xff);
+	ath->ath_defkeyix[1] = ((defkeyix >> 8) & 0xff);
+	return frm + sizeof(info); 
+}
+#undef ATH_OUI_BYTES
+
 /*
  * Send a probe request frame with the specified ssid
  * and any optional information element data.
  */
 int
 ieee80211_send_probereq(struct ieee80211_node *ni,
-	const u_int8_t sa[IEEE80211_ADDR_LEN],
-	const u_int8_t da[IEEE80211_ADDR_LEN],
-	const u_int8_t bssid[IEEE80211_ADDR_LEN],
-	const u_int8_t *ssid, size_t ssidlen,
+	const uint8_t sa[IEEE80211_ADDR_LEN],
+	const uint8_t da[IEEE80211_ADDR_LEN],
+	const uint8_t bssid[IEEE80211_ADDR_LEN],
+	const uint8_t *ssid, size_t ssidlen,
 	const void *optie, size_t optielen)
 {
 	struct ieee80211com *ic = ni->ni_ic;
-	enum ieee80211_phymode mode;
 	struct ieee80211_frame *wh;
+	const struct ieee80211_rateset *rs;
 	struct mbuf *m;
-	u_int8_t *frm;
+	uint8_t *frm;
 
 	/*
 	 * Hold a reference on the node so it doesn't go away until after
@@ -1008,6 +1469,7 @@
 	 *	[tlv] user-specified ie's
 	 */
 	m = ieee80211_getmgtframe(&frm,
+		 ic->ic_headroom + sizeof(struct ieee80211_frame),
 		 2 + IEEE80211_NWID_LEN
 	       + 2 + IEEE80211_RATE_SIZE
 	       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
@@ -1020,15 +1482,15 @@
 	}
 
 	frm = ieee80211_add_ssid(frm, ssid, ssidlen);
-	mode = ieee80211_chan2mode(ic, ic->ic_curchan);
-	frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
-	frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
+	rs = ieee80211_get_suprates(ic, ic->ic_curchan);
+	frm = ieee80211_add_rates(frm, rs);
+	frm = ieee80211_add_xrates(frm, rs);
 
 	if (optie != NULL) {
 		memcpy(frm, optie, optielen);
 		frm += optielen;
 	}
-	m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
 
 	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
 	if (m == NULL)
@@ -1058,10 +1520,10 @@
 /*
  * Calculate capability information for mgt frames.
  */
-static u_int16_t
+static uint16_t
 getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
 {
-	u_int16_t capinfo;
+	uint16_t capinfo;
 
 	KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode"));
 
@@ -1090,11 +1552,13 @@
 ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
 	int type, int arg)
 {
+#define	HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT)
 #define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+	const struct ieee80211_rateset *rs;
 	struct mbuf *m;
-	u_int8_t *frm;
-	u_int16_t capinfo;
-	int has_challenge, is_shared_key, ret, timer, status;
+	uint8_t *frm;
+	uint16_t capinfo;
+	int has_challenge, is_shared_key, ret, status;
 
 	KASSERT(ni != NULL, ("null node"));
 
@@ -1110,7 +1574,6 @@
 		ieee80211_node_refcnt(ni)+1);
 	ieee80211_ref_node(ni);
 
-	timer = 0;
 	switch (type) {
 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
 		/*
@@ -1126,11 +1589,17 @@
 		 *	[tlv] extended supported rates
 		 *	[tlv] WPA
 		 *	[tlv] WME (optional)
+		 *	[tlv] HT capabilities
+		 *	[tlv] HT information
+		 *	[tlv] Vendor OUI HT capabilities (optional)
+		 *	[tlv] Vendor OUI HT information (optional)
+		 *	[tlv] Atheros capabilities
 		 */
 		m = ieee80211_getmgtframe(&frm,
+			 ic->ic_headroom + sizeof(struct ieee80211_frame),
 			 8
-		       + sizeof(u_int16_t)
-		       + sizeof(u_int16_t)
+		       + sizeof(uint16_t)
+		       + sizeof(uint16_t)
 		       + 2 + IEEE80211_NWID_LEN
 		       + 2 + IEEE80211_RATE_SIZE
 		       + 7	/* max(7,3) */
@@ -1141,23 +1610,28 @@
 		       + (ic->ic_flags & IEEE80211_F_WPA ?
 				2*sizeof(struct ieee80211_ie_wpa) : 0)
 		       + sizeof(struct ieee80211_wme_param)
+		       /* XXX check for cluster requirement */
+		       + 2*sizeof(struct ieee80211_ie_htcap) + 4
+		       + 2*sizeof(struct ieee80211_ie_htinfo) + 4
+		       + sizeof(struct ieee80211_ath_ie)
 		);
 		if (m == NULL)
 			senderr(ENOMEM, is_tx_nobuf);
 
 		memset(frm, 0, 8);	/* timestamp should be filled later */
 		frm += 8;
-		*(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
+		*(uint16_t *)frm = htole16(ic->ic_bss->ni_intval);
 		frm += 2;
 		capinfo = getcapinfo(ic, ic->ic_curchan);
-		*(u_int16_t *)frm = htole16(capinfo);
+		*(uint16_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, &ni->ni_rates);
+		rs = ieee80211_get_suprates(ic, ic->ic_curchan);
+		frm = ieee80211_add_rates(frm, rs);
 
-		if (ic->ic_phytype == IEEE80211_T_FH) {
+		if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
                         *frm++ = IEEE80211_ELEMID_FHPARMS;
                         *frm++ = 5;
                         *frm++ = ni->ni_fhdwell & 0x00ff;
@@ -1180,12 +1654,32 @@
 		}
 		if (ic->ic_flags & IEEE80211_F_WPA)
 			frm = ieee80211_add_wpa(frm, ic);
-		if (ic->ic_curmode == IEEE80211_MODE_11G)
+		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
 			frm = ieee80211_add_erp(frm, ic);
-		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+		frm = ieee80211_add_xrates(frm, rs);
+		/*
+		 * NB: legacy 11b clients do not get certain ie's.
+		 *     The caller identifies such clients by passing
+		 *     a token in arg to us.  Could expand this to be
+		 *     any legacy client for stuff like HT ie's.
+		 */
+		if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
+		    arg != IEEE80211_SEND_LEGACY_11B) {
+			frm = ieee80211_add_htcap(frm, ni);
+			frm = ieee80211_add_htinfo(frm, ni);
+		}
 		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 *);
+		if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
+		    (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
+		    arg != IEEE80211_SEND_LEGACY_11B) {
+			frm = ieee80211_add_htcap_vendor(frm, ni);
+			frm = ieee80211_add_htinfo_vendor(frm, ni);
+		}
+		if (ni->ni_ath_ie != NULL)
+			frm = ieee80211_add_ath(frm, ni->ni_ath_flags,
+				ni->ni_ath_defkeyix);
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_AUTH:
@@ -1208,27 +1702,28 @@
 		      ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
 
 		m = ieee80211_getmgtframe(&frm,
-			  3 * sizeof(u_int16_t)
+			  ic->ic_headroom + sizeof(struct ieee80211_frame),
+			  3 * sizeof(uint16_t)
 			+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
-				sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0)
+				sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0)
 		);
 		if (m == NULL)
 			senderr(ENOMEM, is_tx_nobuf);
 
-		((u_int16_t *)frm)[0] =
+		((uint16_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 */
+		((uint16_t *)frm)[1] = htole16(arg);	/* sequence number */
+		((uint16_t *)frm)[2] = htole16(status);/* status */
 
 		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
-			((u_int16_t *)frm)[3] =
+			((uint16_t *)frm)[3] =
 			    htole16((IEEE80211_CHALLENGE_LEN << 8) |
 			    IEEE80211_ELEMID_CHALLENGE);
-			memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
+			memcpy(&((uint16_t *)frm)[4], ni->ni_challenge,
 			    IEEE80211_CHALLENGE_LEN);
 			m->m_pkthdr.len = m->m_len =
-				4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN;
+				4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN;
 			if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
 				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
 				    "[%s] request encrypt frame (%s)\n",
@@ -1236,7 +1731,7 @@
 				m->m_flags |= M_LINK0; /* WEP-encrypt, please */
 			}
 		} else
-			m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t);
+			m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t);
 
 		/* XXX not right for shared key */
 		if (status == IEEE80211_STATUS_SUCCESS)
@@ -1245,18 +1740,21 @@
 			IEEE80211_NODE_STAT(ni, tx_auth_fail);
 
 		if (ic->ic_opmode == IEEE80211_M_STA)
-			timer = IEEE80211_TRANS_WAIT;
+			ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
+				(void *) ic->ic_state);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
 			"[%s] send station deauthenticate (reason %d)\n",
 			ether_sprintf(ni->ni_macaddr), arg);
-		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
+		m = ieee80211_getmgtframe(&frm,
+			ic->ic_headroom + sizeof(struct ieee80211_frame),
+			sizeof(uint16_t));
 		if (m == NULL)
 			senderr(ENOMEM, is_tx_nobuf);
-		*(u_int16_t *)frm = htole16(arg);	/* reason */
-		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+		*(uint16_t *)frm = htole16(arg);	/* reason */
+		m->m_pkthdr.len = m->m_len = sizeof(uint16_t);
 
 		IEEE80211_NODE_STAT(ni, tx_deauth);
 		IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
@@ -1275,16 +1773,22 @@
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
 		 *	[tlv] WME
+		 *	[tlv] HT capabilities
+		 *	[tlv] Vendor OUI HT capabilities (optional)
+		 *	[tlv] Atheros capabilities (if negotiated)
 		 *	[tlv] user-specified ie's
 		 */
 		m = ieee80211_getmgtframe(&frm,
-			 sizeof(u_int16_t)
-		       + sizeof(u_int16_t)
+			 ic->ic_headroom + sizeof(struct ieee80211_frame),
+			 sizeof(uint16_t)
+		       + sizeof(uint16_t)
 		       + IEEE80211_ADDR_LEN
 		       + 2 + IEEE80211_NWID_LEN
 		       + 2 + IEEE80211_RATE_SIZE
 		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
 		       + sizeof(struct ieee80211_wme_info)
+		       + 2*sizeof(struct ieee80211_ie_htcap) + 4
+		       + sizeof(struct ieee80211_ath_ie)
 		       + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
 		);
 		if (m == NULL)
@@ -1302,13 +1806,19 @@
 		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
 		    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
+		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
 		    (ic->ic_caps & IEEE80211_C_SHSLOT))
 			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
-		*(u_int16_t *)frm = htole16(capinfo);
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) &&
+		    (ic->ic_flags & IEEE80211_F_DOTH))
+			capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
+		*(uint16_t *)frm = htole16(capinfo);
 		frm += 2;
 
-		*(u_int16_t *)frm = htole16(ic->ic_lintval);
+		KASSERT(ic->ic_bss->ni_intval != 0,
+			("beacon interval is zero!"));
+		*(uint16_t *)frm = htole16(howmany(ic->ic_lintval,
+						   ic->ic_bss->ni_intval));
 		frm += 2;
 
 		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
@@ -1319,48 +1829,71 @@
 		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);
+		if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
+		    ni->ni_htcap_ie != NULL &&
+		    ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+			frm = ieee80211_add_htcap(frm, ni);
 		if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
 			frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+		if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
+		    ni->ni_htcap_ie != NULL &&
+		    ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+			frm = ieee80211_add_htcap_vendor(frm, ni);
+		if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+			frm = ieee80211_add_ath(frm,
+				IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
+				(ic->ic_flags & IEEE80211_F_WPA) == 0 &&
+				ni->ni_authmode != IEEE80211_AUTH_8021X &&
+				ic->ic_def_txkey != IEEE80211_KEYIX_NONE ?
+				ic->ic_def_txkey : 0x7fff);
 		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 *);
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
 
-		timer = IEEE80211_TRANS_WAIT;
+		ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
+			(void *) ic->ic_state);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
 		/*
-		 * asreq frame format
+		 * asresp frame format
 		 *	[2] capability information
 		 *	[2] status
 		 *	[2] association ID
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
 		 *	[tlv] WME (if enabled and STA enabled)
+		 *	[tlv] HT capabilities (standard or vendor OUI)
+		 *	[tlv] HT information (standard or vendor OUI)
+		 *	[tlv] Atheros capabilities (if enabled and STA enabled)
 		 */
 		m = ieee80211_getmgtframe(&frm,
-			 sizeof(u_int16_t)
-		       + sizeof(u_int16_t)
-		       + sizeof(u_int16_t)
+			 ic->ic_headroom + sizeof(struct ieee80211_frame),
+			 sizeof(uint16_t)
+		       + sizeof(uint16_t)
+		       + sizeof(uint16_t)
 		       + 2 + IEEE80211_RATE_SIZE
 		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
 		       + sizeof(struct ieee80211_wme_param)
+		       + sizeof(struct ieee80211_ie_htcap) + 4
+		       + sizeof(struct ieee80211_ie_htinfo) + 4
+		       + sizeof(struct ieee80211_ath_ie)
 		);
 		if (m == NULL)
 			senderr(ENOMEM, is_tx_nobuf);
 
 		capinfo = getcapinfo(ic, ic->ic_curchan);
-		*(u_int16_t *)frm = htole16(capinfo);
+		*(uint16_t *)frm = htole16(capinfo);
 		frm += 2;
 
-		*(u_int16_t *)frm = htole16(arg);	/* status */
+		*(uint16_t *)frm = htole16(arg);	/* status */
 		frm += 2;
 
 		if (arg == IEEE80211_STATUS_SUCCESS) {
-			*(u_int16_t *)frm = htole16(ni->ni_associd);
+			*(uint16_t *)frm = htole16(ni->ni_associd);
 			IEEE80211_NODE_STAT(ni, tx_assoc);
 		} else
 			IEEE80211_NODE_STAT(ni, tx_assoc_fail);
@@ -1368,20 +1901,35 @@
 
 		frm = ieee80211_add_rates(frm, &ni->ni_rates);
 		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+		/* NB: respond according to what we received */
+		if ((ni->ni_flags & HTFLAGS) == IEEE80211_NODE_HT) {
+			frm = ieee80211_add_htcap(frm, ni);
+			frm = ieee80211_add_htinfo(frm, ni);
+		}
 		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 *);
+		if ((ni->ni_flags & HTFLAGS) == HTFLAGS) {
+			frm = ieee80211_add_htcap_vendor(frm, ni);
+			frm = ieee80211_add_htinfo_vendor(frm, ni);
+		}
+		if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+			frm = ieee80211_add_ath(frm,
+				IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
+				ni->ni_ath_defkeyix);
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
 			"[%s] send station disassociate (reason %d)\n",
 			ether_sprintf(ni->ni_macaddr), arg);
-		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
+		m = ieee80211_getmgtframe(&frm,
+			ic->ic_headroom + sizeof(struct ieee80211_frame),
+			sizeof(uint16_t));
 		if (m == NULL)
 			senderr(ENOMEM, is_tx_nobuf);
-		*(u_int16_t *)frm = htole16(arg);	/* reason */
-		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+		*(uint16_t *)frm = htole16(arg);	/* reason */
+		m->m_pkthdr.len = m->m_len = sizeof(uint16_t);
 
 		IEEE80211_NODE_STAT(ni, tx_disassoc);
 		IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg);
@@ -1394,28 +1942,72 @@
 		senderr(EINVAL, is_tx_unknownmgt);
 		/* NOTREACHED */
 	}
-	ret = ieee80211_mgmt_output(ic, ni, m, type, timer);
-	if (ret != 0) {
+
+	ret = ieee80211_mgmt_output(ic, ni, m, type);
+	if (ret != 0)
+		goto bad;
+	return 0;
 bad:
-		ieee80211_free_node(ni);
-	}
+	ieee80211_free_node(ni);
 	return ret;
 #undef senderr
+#undef HTFLAGS
+}
+
+static void
+ieee80211_tx_mgt_timeout(void *arg)
+{
+	struct ieee80211_node *ni = arg;
+	struct ieee80211com *ic	= ni->ni_ic;
+
+	if (ic->ic_state != IEEE80211_S_INIT &&
+	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+		/*
+		 * NB: it's safe to specify a timeout as the reason here;
+		 *     it'll only be used in the right state.
+		 */
+		ieee80211_new_state(ic, IEEE80211_S_SCAN,
+			IEEE80211_SCAN_FAIL_TIMEOUT);
+	}
+}
+
+static void
+ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	enum ieee80211_state ostate = (enum ieee80211_state) arg;
+
+	/*
+	 * Frame transmit completed; arrange timer callback.  If
+	 * transmit was successfuly we wait for response.  Otherwise
+	 * we arrange an immediate callback instead of doing the
+	 * callback directly since we don't know what state the driver
+	 * is in (e.g. what locks it is holding).  This work should
+	 * not be too time-critical and not happen too often so the
+	 * added overhead is acceptable.
+	 *
+	 * XXX what happens if !acked but response shows up before callback?
+	 */
+	if (ic->ic_state == ostate)
+		callout_reset(&ic->ic_mgtsend,
+			status == 0 ? IEEE80211_TRANS_WAIT*hz : 0,
+			ieee80211_tx_mgt_timeout, ni);
 }
 
 /*
  * Allocate a beacon frame and fillin the appropriate bits.
  */
 struct mbuf *
-ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni,
+ieee80211_beacon_alloc(struct ieee80211_node *ni,
 	struct ieee80211_beacon_offsets *bo)
 {
+	struct ieee80211com *ic = ni->ni_ic;
 	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_frame *wh;
 	struct mbuf *m;
 	int pktlen;
-	u_int8_t *frm, *efrm;
-	u_int16_t capinfo;
+	uint8_t *frm;
+	uint16_t capinfo;
 	struct ieee80211_rateset *rs;
 
 	/*
@@ -1427,29 +2019,39 @@
 	 *	[tlv] supported rates
 	 *	[3] parameter set (DS)
 	 *	[tlv] parameter set (IBSS/TIM)
+	 *	[tlv] country code
 	 *	[tlv] extended rate phy (ERP)
 	 *	[tlv] extended supported rates
 	 *	[tlv] WME parameters
 	 *	[tlv] WPA/RSN parameters
+	 *	[tlv] HT capabilities
+	 *	[tlv] HT information
+	 *	[tlv] Vendor OUI HT capabilities (optional)
+	 *	[tlv] Vendor OUI HT information (optional)
 	 * 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 */
+		 + sizeof(uint16_t)			/* beacon interval */
+		 + sizeof(uint16_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 */
+		 + sizeof(struct ieee80211_country_ie)	/* country code */
 		 + 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)
+		 /* XXX conditional? */
+		 + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
+		 + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
 		 ;
-	m = ieee80211_getmgtframe(&frm, pktlen);
+	m = ieee80211_getmgtframe(&frm,
+		ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
 	if (m == NULL) {
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
 			"%s: cannot get buf; size %u\n", __func__, pktlen);
@@ -1457,13 +2059,15 @@
 		return NULL;
 	}
 
+	memset(bo, 0, sizeof(*bo));
+
 	memset(frm, 0, 8);	/* XXX timestamp is set by hardware/driver */
 	frm += 8;
-	*(u_int16_t *)frm = htole16(ni->ni_intval);
+	*(uint16_t *)frm = htole16(ni->ni_intval);
 	frm += 2;
 	capinfo = getcapinfo(ic, ni->ni_chan);
-	bo->bo_caps = (u_int16_t *)frm;
-	*(u_int16_t *)frm = htole16(capinfo);
+	bo->bo_caps = (uint16_t *)frm;
+	*(uint16_t *)frm = htole16(capinfo);
 	frm += 2;
 	*frm++ = IEEE80211_ELEMID_SSID;
 	if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
@@ -1473,10 +2077,10 @@
 	} else
 		*frm++ = 0;
 	frm = ieee80211_add_rates(frm, rs);
-	if (ic->ic_curmode != IEEE80211_MODE_FH) {
+	if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) {
 		*frm++ = IEEE80211_ELEMID_DSPARMS;
 		*frm++ = 1;
-		*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+		*frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
 	}
 	bo->bo_tim = frm;
 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
@@ -1496,21 +2100,33 @@
 		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;
-	}
+	bo->bo_tim_trailer = frm;
+	if (ic->ic_flags & IEEE80211_F_DOTH)
+		frm = ieee80211_add_countryie(frm, ic,
+			ic->ic_countrycode, ic->ic_location);
 	if (ic->ic_flags & IEEE80211_F_WPA)
 		frm = ieee80211_add_wpa(frm, ic);
-	if (ic->ic_curmode == IEEE80211_MODE_11G) {
+	if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
 		bo->bo_erp = frm;
 		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 *);
+	frm = ieee80211_add_xrates(frm, rs);
+	if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) {
+		frm = ieee80211_add_htcap(frm, ni);
+		bo->bo_htinfo = frm;
+		frm = ieee80211_add_htinfo(frm, ni);
+	}
+	if (ic->ic_flags & IEEE80211_F_WME) {
+		bo->bo_wme = frm;
+		frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+	}
+	if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) &&
+	    (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
+		frm = ieee80211_add_htcap_vendor(frm, ni);
+		frm = ieee80211_add_htinfo_vendor(frm, ni);
+	}
+	bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer;
+	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
 
 	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
 	KASSERT(m != NULL, ("no space for 802.11 header?"));
@@ -1518,11 +2134,11 @@
 	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;
+	*(uint16_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;
+	*(uint16_t *)wh->i_seq = 0;
 
 	return m;
 }
@@ -1531,11 +2147,12 @@
  * Update the dynamic parts of a beacon frame based on the current state.
  */
 int
-ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni,
+ieee80211_beacon_update(struct ieee80211_node *ni,
 	struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
 {
+	struct ieee80211com *ic = ni->ni_ic;
 	int len_changed = 0;
-	u_int16_t capinfo;
+	uint16_t capinfo;
 
 	IEEE80211_BEACON_LOCK(ic);
 	/* XXX faster to recalculate entirely or just changes? */
@@ -1577,16 +2194,21 @@
 				wme->wme_hipri_traffic =
 					wme->wme_hipri_switch_hysteresis;
 		}
-		if (ic->ic_flags & IEEE80211_F_WMEUPDATE) {
+		if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) {
 			(void) ieee80211_add_wme_param(bo->bo_wme, wme);
-			ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
+			clrbit(bo->bo_flags, IEEE80211_BEACON_WME);
 		}
 	}
 
+	if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
+		ieee80211_ht_update_beacon(ic, bo);
+		clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO);
+	}
+
 	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) {
+		if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) {
 			u_int timlen, timoff, i;
 			/* 
 			 * ATIM/DTIM needs updating.  If it fits in the
@@ -1623,12 +2245,14 @@
 			if (timlen != bo->bo_tim_len) {
 				/* copy up/down trailer */
 				int adjust = tie->tim_bitmap+timlen
-					   - bo->bo_trailer;
-				ovbcopy(bo->bo_trailer, bo->bo_trailer+adjust,
-					bo->bo_trailer_len);
-				bo->bo_trailer += adjust;
+					   - bo->bo_tim_trailer;
+				ovbcopy(bo->bo_tim_trailer,
+				    bo->bo_tim_trailer+adjust,
+				    bo->bo_tim_trailer_len);
+				bo->bo_tim_trailer += adjust;
 				bo->bo_wme += adjust;
 				bo->bo_erp += adjust;
+				bo->bo_htinfo += adjust;
 				bo->bo_tim_len = timlen;
 
 				/* update information element */
@@ -1639,7 +2263,7 @@
 			memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
 				bo->bo_tim_len);
 
-			ic->ic_flags &= ~IEEE80211_F_TIMUPDATE;
+			clrbit(bo->bo_flags, IEEE80211_BEACON_TIM);
 
 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
 				"%s: TIM updated, pending %u, off %u, len %u\n",
@@ -1655,61 +2279,15 @@
 			tie->tim_bitctl |= 1;
 		else
 			tie->tim_bitctl &= ~1;
-		if (ic->ic_flags_ext & IEEE80211_FEXT_ERPUPDATE) {
+		if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
 			/*
 			 * ERP element needs updating.
 			 */
 			(void) ieee80211_add_erp(bo->bo_erp, ic);
-			ic->ic_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE;
+			clrbit(bo->bo_flags, IEEE80211_BEACON_ERP);
 		}
 	}
 	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,
-			"[%s] pwr save q overflow, drops %d (size %d)\n",
-			ether_sprintf(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_bintval) << 2) / 1024; /* TU -> secs */
-	_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
-	IEEE80211_NODE_SAVEQ_UNLOCK(ni);
-
-	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
-		"[%s] save frame with age %d, %u now queued\n",
-		ether_sprintf(ni->ni_macaddr), age, qlen);
-
-	if (qlen == 1)
-		ic->ic_set_tim(ni, 1);
-}
--- /dev/null
+++ sys/net80211/ieee80211_scan_ap.c
@@ -0,0 +1,408 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_scan_ap.c,v 1.1.2.1 2007/11/11 17:44:36 sam Exp $");
+
+/*
+ * IEEE 802.11 ap scanning support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/kernel.h>
+#include <sys/module.h>
+ 
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+struct ap_state {
+	int	as_maxrssi[IEEE80211_CHAN_MAX];
+};
+
+static int ap_flush(struct ieee80211_scan_state *);
+
+/* number of references from net80211 layer */
+static	int nrefs = 0;
+
+/*
+ * Attach prior to any scanning work.
+ */
+static int
+ap_attach(struct ieee80211_scan_state *ss)
+{
+	struct ap_state *as;
+
+	MALLOC(as, struct ap_state *, sizeof(struct ap_state),
+		M_80211_SCAN, M_NOWAIT);
+	ss->ss_priv = as;
+	ap_flush(ss);
+	nrefs++;			/* NB: we assume caller locking */
+	return 1;
+}
+
+/*
+ * Cleanup any private state.
+ */
+static int
+ap_detach(struct ieee80211_scan_state *ss)
+{
+	struct ap_state *as = ss->ss_priv;
+
+	if (as != NULL) {
+		KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+		nrefs--;		/* NB: we assume caller locking */
+		FREE(as, M_80211_SCAN);
+	}
+	return 1;
+}
+
+/*
+ * Flush all per-scan state.
+ */
+static int
+ap_flush(struct ieee80211_scan_state *ss)
+{
+	struct ap_state *as = ss->ss_priv;
+
+	memset(as->as_maxrssi, 0, sizeof(as->as_maxrssi));
+	ss->ss_last = 0;		/* insure no channel will be picked */
+	return 0;
+}
+
+static int
+find11gchannel(struct ieee80211com *ic, int i, int freq)
+{
+	const struct ieee80211_channel *c;
+	int j;
+
+	/*
+	 * The normal ordering in the channel list is b channel
+	 * immediately followed by g so optimize the search for
+	 * this.  We'll still do a full search just in case.
+	 */
+	for (j = i+1; j < ic->ic_nchans; j++) {
+		c = &ic->ic_channels[j];
+		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+			return 1;
+	}
+	for (j = 0; j < i; j++) {
+		c = &ic->ic_channels[j];
+		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Start an ap scan by populating the channel list.
+ */
+static int
+ap_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	struct ieee80211_channel *c;
+	int i;
+
+	ss->ss_last = 0;
+	if (ic->ic_des_mode == IEEE80211_MODE_AUTO) {
+		for (i = 0; i < ic->ic_nchans; i++) {
+			c = &ic->ic_channels[i];
+			if (IEEE80211_IS_CHAN_TURBO(c)) {
+#ifdef IEEE80211_F_XR
+				/* XR is not supported on turbo channels */
+				if (ic->ic_flags & IEEE80211_F_XR)
+					continue;
+#endif
+				/* dynamic channels are scanned in base mode */
+				if (!IEEE80211_IS_CHAN_ST(c))
+					continue;
+			} else if (IEEE80211_IS_CHAN_HT(c)) {
+				/* HT channels are scanned in legacy */
+				continue;
+			} else {
+				/*
+				 * Use any 11g channel instead of 11b one.
+				 */
+				if (IEEE80211_IS_CHAN_B(c) &&
+				    find11gchannel(ic, i, c->ic_freq))
+					continue;
+			}
+			if (ss->ss_last >= IEEE80211_SCAN_MAX)
+				break;
+			ss->ss_chans[ss->ss_last++] = c;
+		}
+	} else {
+		static const u_int chanflags[IEEE80211_MODE_MAX] = {
+			0,			/* IEEE80211_MODE_AUTO */
+			IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
+			IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
+			IEEE80211_CHAN_G,	/* IEEE80211_MODE_11G */
+			IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
+			IEEE80211_CHAN_108A,	/* IEEE80211_MODE_TURBO_A */
+			IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
+			IEEE80211_CHAN_ST,	/* IEEE80211_MODE_STURBO_A */
+			IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA */
+			IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG */
+		};
+		u_int modeflags;
+
+		modeflags = chanflags[ic->ic_des_mode];
+		if ((ic->ic_flags & IEEE80211_F_TURBOP) &&
+		    modeflags != IEEE80211_CHAN_ST) {
+			if (ic->ic_des_mode == IEEE80211_MODE_11G)
+				modeflags = IEEE80211_CHAN_108G;
+			else
+				modeflags = IEEE80211_CHAN_108A;
+		}
+		for (i = 0; i < ic->ic_nchans; i++) {
+			c = &ic->ic_channels[i];
+			if ((c->ic_flags & modeflags) != modeflags)
+				continue;
+#ifdef IEEE80211_F_XR
+			/* XR is not supported on turbo channels */
+			if (IEEE80211_IS_CHAN_TURBO(c) &&
+			    (ic->ic_flags & IEEE80211_F_XR))
+				continue;
+#endif
+			if (ss->ss_last >= IEEE80211_SCAN_MAX)
+				break;
+			/* 
+			 * Do not select static turbo channels if
+			 * the mode is not static turbo.
+			 */
+			if (IEEE80211_IS_CHAN_STURBO(c) &&
+			    ic->ic_des_mode != IEEE80211_MODE_STURBO_A)
+				continue;
+			ss->ss_chans[ss->ss_last++] = c;
+		}
+	}
+	ss->ss_next = 0;
+	/* XXX tunables */
+	ss->ss_mindwell = msecs_to_ticks(200);		/* 200ms */
+	ss->ss_maxdwell = msecs_to_ticks(300);		/* 300ms */
+
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic)) {
+		if_printf(ic->ic_ifp, "scan set ");
+		ieee80211_scan_dump_channels(ss);
+		printf(" dwell min %ld max %ld\n",
+			ss->ss_mindwell, ss->ss_maxdwell);
+	}
+#endif /* IEEE80211_DEBUG */
+
+	return 0;
+}
+
+/*
+ * Restart a bg scan.
+ */
+static int
+ap_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	return 0;
+}
+
+/*
+ * Cancel an ongoing scan.
+ */
+static int
+ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	return 0;
+}
+
+/*
+ * Record max rssi on channel.
+ */
+static int
+ap_add(struct ieee80211_scan_state *ss,
+	const struct ieee80211_scanparams *sp,
+	const struct ieee80211_frame *wh,
+	int subtype, int rssi, int noise, int rstamp)
+{
+	struct ap_state *as = ss->ss_priv;
+	struct ieee80211com *ic = ss->ss_ic;
+	int chan;
+
+	chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+	/* XXX better quantification of channel use? */
+	/* XXX count bss's? */
+	if (rssi > as->as_maxrssi[chan])
+		as->as_maxrssi[chan] = rssi;
+	/* XXX interference, turbo requirements */
+	return 1;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static int
+ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	struct ap_state *as = ss->ss_priv;
+	int i, chan, bestchan, bestchanix;
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP,
+		("wrong opmode %u", ic->ic_opmode));
+	/* XXX select channel more intelligently, e.g. channel spread, power */
+	bestchan = -1;
+	bestchanix = 0;		/* NB: silence compiler */
+	/* NB: use scan list order to preserve channel preference */
+	for (i = 0; i < ss->ss_last; 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.
+		 */
+		/* XXX channel have interference? */
+		chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]);
+
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: channel %u rssi %d bestchan %d bestchan rssi %d\n",
+		    __func__, chan, as->as_maxrssi[chan],
+		    bestchan, bestchan != -1 ? as->as_maxrssi[bestchan] : 0);
+
+		if (as->as_maxrssi[chan] == 0) {
+			bestchan = chan;
+			bestchanix = i;
+			/* XXX use other considerations */
+			break;
+		}
+		if (bestchan == -1 ||
+		    as->as_maxrssi[chan] < as->as_maxrssi[bestchan])
+			bestchan = chan;
+	}
+	if (bestchan == -1) {
+		/* no suitable channel, should not happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: no suitable channel! (should not happen)\n", __func__);
+		/* XXX print something? */
+		return 0;			/* restart scan */
+	} else {
+		struct ieee80211_channel *c;
+
+		/* XXX notify all vap's? */
+		/*
+		 * If this is a dynamic turbo frequency,
+		 * start with normal mode first.
+		 */
+		c = ss->ss_chans[bestchanix];
+		if (IEEE80211_IS_CHAN_TURBO(c) &&
+		    !IEEE80211_IS_CHAN_STURBO(c)) { 
+			c = ieee80211_find_channel(ic, c->ic_freq,
+				c->ic_flags & ~IEEE80211_CHAN_TURBO);
+			if (c == NULL) {
+				/* should never happen ?? */
+				return 0;
+			}
+		}
+		ieee80211_create_ibss(ic,
+		    ieee80211_ht_adjust_channel(ic, c, ic->ic_flags_ext));
+		return 1;
+	}
+}
+
+static void
+ap_age(struct ieee80211_scan_state *ss)
+{
+	/* XXX is there anything meaningful to do? */
+}
+
+static void
+ap_iterate(struct ieee80211_scan_state *ss,
+	ieee80211_scan_iter_func *f, void *arg)
+{
+	/* NB: nothing meaningful we can do */
+}
+
+static void
+ap_assoc_success(struct ieee80211_scan_state *ss,
+	const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+	/* should not be called */
+}
+
+static void
+ap_assoc_fail(struct ieee80211_scan_state *ss,
+	const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
+{
+	/* should not be called */
+}
+
+static const struct ieee80211_scanner ap_default = {
+	.scan_name		= "default",
+	.scan_attach		= ap_attach,
+	.scan_detach		= ap_detach,
+	.scan_start		= ap_start,
+	.scan_restart		= ap_restart,
+	.scan_cancel		= ap_cancel,
+	.scan_end		= ap_end,
+	.scan_flush		= ap_flush,
+	.scan_add		= ap_add,
+	.scan_age		= ap_age,
+	.scan_iterate		= ap_iterate,
+	.scan_assoc_success	= ap_assoc_success,
+	.scan_assoc_fail	= ap_assoc_fail,
+};
+
+/*
+ * Module glue.
+ */
+static int
+wlan_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		ieee80211_scanner_register(IEEE80211_M_HOSTAP, &ap_default);
+		return 0;
+	case MOD_UNLOAD:
+	case MOD_QUIESCE:
+		if (nrefs) {
+			printf("wlan_scan_ap: still in use (%u dynamic refs)\n",
+				nrefs);
+			return EBUSY;
+		}
+		if (type == MOD_UNLOAD)
+			ieee80211_scanner_unregister_all(&ap_default);
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t wlan_mod = {
+	"wlan_scan_ap",
+	wlan_modevent,
+	0
+};
+DECLARE_MODULE(wlan_scan_ap, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_scan_ap, 1);
+MODULE_DEPEND(wlan_scan_ap, wlan, 1, 1, 1);
Index: ieee80211_ioctl.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_ioctl.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_ioctl.c -L sys/net80211/ieee80211_ioctl.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_ioctl.c
+++ sys/net80211/ieee80211_ioctl.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,9 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.25.2.12 2006/04/03 17:21:05 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.57.2.1 2007/11/11 17:44:36 sam Exp $");
+
+#include "opt_compat.h"
 
 /*
  * IEEE 802.11 ioctl support (FreeBSD-specific)
@@ -43,12 +39,13 @@
 #include <sys/endian.h>
 #include <sys/param.h>
 #include <sys/kernel.h>
+#include <sys/priv.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
 #include <sys/systm.h>
  
 #include <net/if.h>
-#include <net/if_arp.h>
+#include <net/if_dl.h>
 #include <net/if_media.h>
 #include <net/ethernet.h>
 
@@ -65,752 +62,15 @@
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_ioctl.h>
 
-#include <dev/wi/if_wavelan_ieee.h>
-
 #define	IS_UP(_ic) \
 	(((_ic)->ic_ifp->if_flags & IFF_UP) &&			\
 	    ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
 #define	IS_UP_AUTO(_ic) \
 	(IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
+#define	RESCAN	1
 
-/*
- * 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 ieee80211com *ic, u_long cmd, caddr_t data)
-{
-	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;
-
-	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
-	if (error)
-		return error;
-	wreq.wi_len = 0;
-	switch (wreq.wi_type) {
-	case WI_RID_SERIALNO:
-		/* nothing appropriate */
-		break;
-	case WI_RID_NODENAME:
-		strcpy((char *)&wreq.wi_val[1], hostname);
-		wreq.wi_val[0] = htole16(strlen(hostname));
-		wreq.wi_len = (1 + strlen(hostname) + 1) / 2;
-		break;
-	case WI_RID_CURRENT_SSID:
-		if (ic->ic_state != IEEE80211_S_RUN) {
-			wreq.wi_val[0] = 0;
-			wreq.wi_len = 1;
-			break;
-		}
-		wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen);
-		memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid,
-		    ic->ic_bss->ni_esslen);
-		wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2;
-		break;
-	case WI_RID_OWN_SSID:
-	case WI_RID_DESIRED_SSID:
-		wreq.wi_val[0] = htole16(ic->ic_des_esslen);
-		memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen);
-		wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2;
-		break;
-	case WI_RID_CURRENT_BSSID:
-		if (ic->ic_state == IEEE80211_S_RUN)
-			IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid);
-		else
-			memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN);
-		wreq.wi_len = IEEE80211_ADDR_LEN / 2;
-		break;
-	case WI_RID_CHANNEL_LIST:
-		memset(wreq.wi_val, 0, sizeof(wreq.wi_val));
-		/*
-		 * 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++)
-			if (isset(ic->ic_chan_active, i)) {
-				setbit((u_int8_t *)wreq.wi_val, j);
-				wreq.wi_len = j / 16 + 1;
-			}
-		break;
-	case WI_RID_OWN_CHNL:
-		wreq.wi_val[0] = htole16(
-			ieee80211_chan2ieee(ic, ic->ic_ibss_chan));
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_CURRENT_CHAN:
-		wreq.wi_val[0] = htole16(
-			ieee80211_chan2ieee(ic, ic->ic_curchan));
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_COMMS_QUALITY:
-		wreq.wi_val[0] = 0;				/* quality */
-		wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss));
-		wreq.wi_val[2] = 0;				/* noise */
-		wreq.wi_len = 3;
-		break;
-	case WI_RID_PROMISC:
-		wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_PORTTYPE:
-		wreq.wi_val[0] = htole16(ic->ic_opmode);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_MAC_NODE:
-		IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr);
-		wreq.wi_len = IEEE80211_ADDR_LEN / 2;
-		break;
-	case WI_RID_TX_RATE:
-		if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
-			wreq.wi_val[0] = 0;	/* auto */
-		else
-			wreq.wi_val[0] = htole16(
-			    (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] &
-			    IEEE80211_RATE_VAL) / 2);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_CUR_TX_RATE:
-		wreq.wi_val[0] = htole16(
-		    (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] &
-		    IEEE80211_RATE_VAL) / 2);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_RTS_THRESH:
-		wreq.wi_val[0] = htole16(ic->ic_rtsthreshold);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_CREATE_IBSS:
-		wreq.wi_val[0] =
-		    htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_MICROWAVE_OVEN:
-		wreq.wi_val[0] = 0;	/* no ... not supported */
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_ROAMING_MODE:
-		wreq.wi_val[0] = htole16(ic->ic_roaming);	/* XXX map */
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_SYSTEM_SCALE:
-		wreq.wi_val[0] = htole16(1);	/* low density ... not supp */
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_PM_ENABLED:
-		wreq.wi_val[0] =
-		    htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_MAX_SLEEP:
-		wreq.wi_val[0] = htole16(ic->ic_lintval);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_CUR_BEACON_INT:
-		wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_WEP_AVAIL:
-		wreq.wi_val[0] = htole16(1);	/* always available */
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_CNFAUTHMODE:
-		wreq.wi_val[0] = htole16(1);	/* TODO: open system only */
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_ENCRYPTION:
-		wreq.wi_val[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_def_txkey);
-		wreq.wi_len = 1;
-		break;
-	case WI_RID_DEFLT_CRYPT_KEYS:
-		keys = (struct wi_ltv_keys *)&wreq;
-		/* do not show keys to non-root user */
-		error = suser(curthread);
-		if (error) {
-			memset(keys, 0, sizeof(*keys));
-			error = 0;
-			break;
-		}
-		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
-			keys->wi_keys[i].wi_keylen =
-			    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_keylen);
-		}
-		wreq.wi_len = sizeof(*keys) / 2;
-		break;
-	case WI_RID_MAX_DATALEN:
-		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:
-		/*
-		 * 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:
-		/* 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_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;
-	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;
-	}
-	if (error == 0) {
-		wreq.wi_len++;
-		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
-	}
-	return error;
-}
-
-static int
-findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
-{
-#define	IEEERATE(_ic,_m,_i) \
-	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
-	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
-	for (i = 0; i < nrates; i++)
-		if (IEEERATE(ic, mode, i) == rate)
-			return i;
-	return -1;
-#undef IEEERATE
-}
-
-/*
- * Prepare to do a user-initiated scan for AP's.  If no
- * current/default channel is setup or the current channel
- * is invalid then pick the first available channel from
- * the active list as the place to start the scan.
- */
-static int
-ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[])
-{
-
-	/*
-	 * 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;
-	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
-	/*
-	 * 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.
-	 */
-	/* XXX handle proberequest case */
-	ic->ic_state = IEEE80211_S_INIT;	/* XXX bypass state machine */
-	return 0;
-}
-
-int
-ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data)
-{
-	struct ifnet *ifp = ic->ic_ifp;
-	int i, j, len, error, rate;
-	struct ifreq *ifr = (struct ifreq *)data;
-	struct wi_ltv_keys *keys;
-	struct wi_req wreq;
-	u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)];
-
-	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
-	if (error)
-		return error;
-	len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0;
-	switch (wreq.wi_type) {
-	case WI_RID_SERIALNO:
-	case WI_RID_NODENAME:
-		return EPERM;
-	case WI_RID_CURRENT_SSID:
-		return EPERM;
-	case WI_RID_OWN_SSID:
-	case WI_RID_DESIRED_SSID:
-		if (le16toh(wreq.wi_val[0]) * 2 > len ||
-		    le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) {
-			error = ENOSPC;
-			break;
-		}
-		memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
-		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;
-		break;
-	case WI_RID_CURRENT_BSSID:
-		return EPERM;
-	case WI_RID_OWN_CHNL:
-		if (len != 2)
-			return EINVAL;
-		i = le16toh(wreq.wi_val[0]);
-		if (i < 0 ||
-		    i > IEEE80211_CHAN_MAX ||
-		    isclr(ic->ic_chan_active, i))
-			return EINVAL;
-		ic->ic_ibss_chan = &ic->ic_channels[i];
-		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:
-		return EPERM;
-	case WI_RID_COMMS_QUALITY:
-		return EPERM;
-	case WI_RID_PROMISC:
-		if (len != 2)
-			return EINVAL;
-		if (ifp->if_flags & IFF_PROMISC) {
-			if (wreq.wi_val[0] == 0) {
-				ifp->if_flags &= ~IFF_PROMISC;
-				error = ENETRESET;
-			}
-		} else {
-			if (wreq.wi_val[0] != 0) {
-				ifp->if_flags |= IFF_PROMISC;
-				error = ENETRESET;
-			}
-		}
-		break;
-	case WI_RID_PORTTYPE:
-		if (len != 2)
-			return EINVAL;
-		switch (le16toh(wreq.wi_val[0])) {
-		case IEEE80211_M_STA:
-			break;
-		case IEEE80211_M_IBSS:
-			if (!(ic->ic_caps & IEEE80211_C_IBSS))
-				return EINVAL;
-			break;
-		case IEEE80211_M_AHDEMO:
-			if (ic->ic_phytype != IEEE80211_T_DS ||
-			    !(ic->ic_caps & IEEE80211_C_AHDEMO))
-				return EINVAL;
-			break;
-		case IEEE80211_M_HOSTAP:
-			if (!(ic->ic_caps & IEEE80211_C_HOSTAP))
-				return EINVAL;
-			break;
-		default:
-			return EINVAL;
-		}
-		if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
-			ic->ic_opmode = le16toh(wreq.wi_val[0]);
-			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
-		}
-		break;
-#if 0
-	case WI_RID_MAC_NODE:
-		if (len != IEEE80211_ADDR_LEN)
-			return EINVAL;
-		IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val);
-		/* if_init will copy lladdr into ic_myaddr */
-		error = ENETRESET;
-		break;
-#endif
-	case WI_RID_TX_RATE:
-		if (len != 2)
-			return EINVAL;
-		if (wreq.wi_val[0] == 0) {
-			/* auto */
-			ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
-			break;
-		}
-		rate = 2 * le16toh(wreq.wi_val[0]);
-		if (ic->ic_curmode == IEEE80211_MODE_AUTO) {
-			/*
-			 * In autoselect mode search for the rate.  We take
-			 * the first instance which may not be right, but we
-			 * are limited by the interface.  Note that we also
-			 * lock the mode to insure the rate is meaningful
-			 * when it is used.
-			 */
-			for (j = IEEE80211_MODE_11A;
-			     j < IEEE80211_MODE_MAX; j++) {
-				if ((ic->ic_modecaps & (1<<j)) == 0)
-					continue;
-				i = findrate(ic, j, rate);
-				if (i != -1) {
-					/* lock mode too */
-					ic->ic_curmode = j;
-					goto setrate;
-				}
-			}
-		} else {
-			i = findrate(ic, ic->ic_curmode, rate);
-			if (i != -1)
-				goto setrate;
-		}
-		return EINVAL;
-	setrate:
-		ic->ic_fixed_rate = i;
-		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
-		break;
-	case WI_RID_CUR_TX_RATE:
-		return EPERM;
-	case WI_RID_RTS_THRESH:
-		if (len != 2)
-			return EINVAL;
-		if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN)
-			return EINVAL;		/* TODO: RTS */
-		break;
-	case WI_RID_CREATE_IBSS:
-		if (len != 2)
-			return EINVAL;
-		if (wreq.wi_val[0] != 0) {
-			if ((ic->ic_caps & IEEE80211_C_IBSS) == 0)
-				return EINVAL;
-			if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
-				ic->ic_flags |= IEEE80211_F_IBSSON;
-				if (ic->ic_opmode == IEEE80211_M_IBSS &&
-				    ic->ic_state == IEEE80211_S_SCAN)
-					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 = IS_UP_AUTO(ic) ? ENETRESET : 0;
-				}
-			}
-		}
-		break;
-	case WI_RID_MICROWAVE_OVEN:
-		if (len != 2)
-			return EINVAL;
-		if (wreq.wi_val[0] != 0)
-			return EINVAL;		/* not supported */
-		break;
-	case WI_RID_ROAMING_MODE:
-		if (len != 2)
-			return EINVAL;
-		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)
-			return EINVAL;
-		if (le16toh(wreq.wi_val[0]) != 1)
-			return EINVAL;		/* not supported */
-		break;
-	case WI_RID_PM_ENABLED:
-		if (len != 2)
-			return EINVAL;
-		if (wreq.wi_val[0] != 0) {
-			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
-				return EINVAL;
-			if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
-				ic->ic_flags |= IEEE80211_F_PMGTON;
-				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 = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
-			}
-		}
-		break;
-	case WI_RID_MAX_SLEEP:
-		if (len != 2)
-			return EINVAL;
-		ic->ic_lintval = le16toh(wreq.wi_val[0]);
-		if (ic->ic_flags & IEEE80211_F_PMGTON)
-			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
-		break;
-	case WI_RID_CUR_BEACON_INT:
-		return EPERM;
-	case WI_RID_WEP_AVAIL:
-		return EPERM;
-	case WI_RID_CNFAUTHMODE:
-		if (len != 2)
-			return EINVAL;
-		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)
-			return EINVAL;
-		if (wreq.wi_val[0] != 0) {
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
-				return EINVAL;
-			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
-				ic->ic_flags |= IEEE80211_F_PRIVACY;
-				error = ENETRESET;
-			}
-		} else {
-			if (ic->ic_flags & IEEE80211_F_PRIVACY) {
-				ic->ic_flags &= ~IEEE80211_F_PRIVACY;
-				error = ENETRESET;
-			}
-		}
-		break;
-	case WI_RID_TX_CRYPT_KEY:
-		if (len != 2)
-			return EINVAL;
-		i = le16toh(wreq.wi_val[0]);
-		if (i >= IEEE80211_WEP_NKID)
-			return EINVAL;
-		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))
-			return EINVAL;
-		keys = (struct wi_ltv_keys *)&wreq;
-		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
-			len = le16toh(keys->wi_keys[i].wi_keylen);
-			if (len != 0 && len < IEEE80211_WEP_KEYLEN)
-				return EINVAL;
-			if (len > IEEE80211_KEYBUF_SIZE)
-				return EINVAL;
-		}
-		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);
-			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;
-	case WI_RID_MAX_DATALEN:
-		if (len != 2)
-			return EINVAL;
-		len = le16toh(wreq.wi_val[0]);
-		if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
-			return EINVAL;
-		ic->ic_fragthreshold = len;
-		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
-		break;
-	case WI_RID_IFACE_STATS:
-		error = EPERM;
-		break;
-	case WI_RID_SCAN_REQ:			/* XXX wicontrol */
-		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
-			break;
-		error = ieee80211_setupscan(ic, ic->ic_chan_avail);
-		if (error == 0)
-			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
-		break;
-	case WI_RID_SCAN_APS:
-		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
-			break;
-		len--;			/* XXX: tx rate? */
-		/* FALLTHRU */
-	case WI_RID_CHANNEL_LIST:
-		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++) {
-			if ((j / 8) >= len)
-				break;
-			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)
-					continue;
-				if (isclr(ic->ic_chan_avail, i))
-					return EPERM;
-			}
-			setbit(chanlist, i);
-		}
-		error = ieee80211_setupscan(ic, chanlist);
-		if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
-			/* NB: ignore error from ieee80211_setupscan */
-			error = ENETRESET;
-		} else if (error == 0)
-			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
-		break;
-	default:
-		error = EINVAL;
-		break;
-	}
-	if (error == ENETRESET && !IS_UP_AUTO(ic))
-		error = 0;
-	return error;
-}
+static struct ieee80211_channel *findchannel(struct ieee80211com *,
+		int ieee, int mode);
 
 static int
 cap2cipher(int flag)
@@ -859,7 +119,7 @@
 	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(curthread) == 0) {
+	if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
 		/* NB: only root can read key data */
 		ik.ik_keyrsc = wk->wk_keyrsc;
 		ik.ik_keytsc = wk->wk_keytsc;
@@ -892,37 +152,21 @@
 static int
 ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
 {
-	struct ieee80211req_chaninfo chans;	/* XXX off stack? */
-	int i, space;
+	int 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]);
+			ic_chans[ic->ic_nchans]);
 	if (space > ireq->i_len)
 		space = ireq->i_len;
-	return copyout(&chans, ireq->i_data, space);
+	/* XXX assumes compatible layout */
+	return copyout(&ic->ic_nchans, ireq->i_data, space);
 }
 
 static int
-ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq)
+ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
 {
 	struct ieee80211_node *ni;
-	struct ieee80211req_wpaie wpaie;
+	struct ieee80211req_wpaie2 wpaie;
 	int error;
 
 	if (ireq->i_len < IEEE80211_ADDR_LEN)
@@ -932,7 +176,7 @@
 		return error;
 	ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
 	if (ni == NULL)
-		return EINVAL;		/* XXX */
+		return ENOENT;		/* XXX */
 	memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
 	if (ni->ni_wpa_ie != NULL) {
 		int ielen = ni->ni_wpa_ie[1] + 2;
@@ -940,9 +184,29 @@
 			ielen = sizeof(wpaie.wpa_ie);
 		memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
 	}
+	if (req == IEEE80211_IOC_WPAIE2) {
+		memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie));
+		if (ni->ni_rsn_ie != NULL) {
+			int ielen = ni->ni_rsn_ie[1] + 2;
+			if (ielen > sizeof(wpaie.rsn_ie))
+				ielen = sizeof(wpaie.rsn_ie);
+			memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen);
+		}
+		if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
+			ireq->i_len = sizeof(struct ieee80211req_wpaie2);
+	} else {
+		/* compatibility op, may overwrite wpa ie */
+		/* XXX check ic_flags? */
+		if (ni->ni_rsn_ie != NULL) {
+			int ielen = ni->ni_rsn_ie[1] + 2;
+			if (ielen > sizeof(wpaie.wpa_ie))
+				ielen = sizeof(wpaie.wpa_ie);
+			memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen);
+		}
+		if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
+			ireq->i_len = sizeof(struct ieee80211req_wpaie);
+	}
 	ieee80211_free_node(ni);
-	if (ireq->i_len > sizeof(wpaie))
-		ireq->i_len = sizeof(wpaie);
 	return copyout(&wpaie, ireq->i_data, ireq->i_len);
 }
 
@@ -950,7 +214,7 @@
 ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
 {
 	struct ieee80211_node *ni;
-	u_int8_t macaddr[IEEE80211_ADDR_LEN];
+	uint8_t macaddr[IEEE80211_ADDR_LEN];
 	const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
 	int error;
 
@@ -961,102 +225,276 @@
 		return error;
 	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
 	if (ni == NULL)
-		return EINVAL;		/* XXX */
+		return EINVAL;
 	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,
+	error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off,
 			ireq->i_len - off);
 	ieee80211_free_node(ni);
 	return error;
 }
 
+static __inline uint8_t *
+copyie(uint8_t *cp, const uint8_t *ie)
+{
+	if (ie != NULL) {
+		memcpy(cp, ie, 2+ie[1]);
+		cp += 2+ie[1];
+	}
+	return cp;
+}
+
+#ifdef COMPAT_FREEBSD6
+#define	IEEE80211_IOC_SCAN_RESULTS_OLD	24
+
+struct scan_result_old {
+	uint16_t	isr_len;		/* length (mult of 4) */
+	uint16_t	isr_freq;		/* MHz */
+	uint16_t	isr_flags;		/* channel flags */
+	uint8_t		isr_noise;
+	uint8_t		isr_rssi;
+	uint8_t		isr_intval;		/* beacon interval */
+	uint8_t		isr_capinfo;		/* capabilities */
+	uint8_t		isr_erp;		/* ERP element */
+	uint8_t		isr_bssid[IEEE80211_ADDR_LEN];
+	uint8_t		isr_nrates;
+	uint8_t		isr_rates[IEEE80211_RATE_MAXSIZE];
+	uint8_t		isr_ssid_len;		/* SSID length */
+	uint8_t		isr_ie_len;		/* IE length */
+	uint8_t		isr_pad[5];
+	/* variable length SSID followed by IE data */
+};
+
+struct oscanreq {
+	struct scan_result_old *sr;
+	size_t space;
+};
+
+static size_t
+old_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
+{
+	size_t len;
+
+	*ielen = 0;
+	if (se->se_wpa_ie != NULL)
+		*ielen += 2+se->se_wpa_ie[1];
+	if (se->se_wme_ie != NULL)
+		*ielen += 2+se->se_wme_ie[1];
+	/*
+	 * NB: ie's can be no more than 255 bytes and the max 802.11
+	 * packet is <3Kbytes so we are sure this doesn't overflow
+	 * 16-bits; if this is a concern we can drop the ie's.
+	 */
+	len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
+	return roundup(len, sizeof(uint32_t));
+}
+
 static void
-get_scan_result(struct ieee80211req_scan_result *sr,
-	const struct ieee80211_node *ni)
+old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
 {
-	struct ieee80211com *ic = ni->ni_ic;
-	u_int ielen = 0;
+	struct oscanreq *req = arg;
+	int ielen;
+
+	req->space += old_scan_space(se, &ielen);
+}
+
+static void
+old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
+{
+	struct oscanreq *req = arg;
+	struct scan_result_old *sr;
+	int ielen, len, nr, nxr;
+	uint8_t *cp;
+
+	len = old_scan_space(se, &ielen);
+	if (len > req->space)
+		return;
 
+	sr = req->sr;
 	memset(sr, 0, sizeof(*sr));
-	sr->isr_ssid_len = ni->ni_esslen;
-	if (ni->ni_wpa_ie != NULL)
-		ielen += 2+ni->ni_wpa_ie[1];
-	if (ni->ni_wme_ie != NULL)
-		ielen += 2+ni->ni_wme_ie[1];
+	sr->isr_ssid_len = se->se_ssid[1];
+	/* NB: beware of overflow, isr_ie_len is 8 bits */
+	sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
+	sr->isr_len = len;
+	sr->isr_freq = se->se_chan->ic_freq;
+	sr->isr_flags = se->se_chan->ic_flags;
+	sr->isr_rssi = se->se_rssi;
+	sr->isr_noise = se->se_noise;
+	sr->isr_intval = se->se_intval;
+	sr->isr_capinfo = se->se_capinfo;
+	sr->isr_erp = se->se_erp;
+	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
+	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
+	memcpy(sr->isr_rates, se->se_rates+2, nr);
+	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
+	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
+	sr->isr_nrates = nr + nxr;
+
+	cp = (uint8_t *)(sr+1);
+	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
+	cp += sr->isr_ssid_len;
+	if (sr->isr_ie_len) {
+		cp = copyie(cp, se->se_wpa_ie);
+		cp = copyie(cp, se->se_wme_ie);
+	}
+
+	req->space -= len;
+	req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
+}
+
+static int
+old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct oscanreq req;
+	int error;
+
+	if (ireq->i_len < sizeof(struct scan_result_old))
+		return EFAULT;
+
+	error = 0;
+	req.space = 0;
+	ieee80211_scan_iterate(ic, old_get_scan_space, &req);
+	if (req.space > ireq->i_len)
+		req.space = ireq->i_len;
+	if (req.space > 0) {
+		size_t space;
+		void *p;
+
+		space = req.space;
+		/* XXX M_WAITOK after driver lock released */
+		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
+		if (p == NULL)
+			return ENOMEM;
+		req.sr = p;
+		ieee80211_scan_iterate(ic, old_get_scan_result, &req);
+		ireq->i_len = space - req.space;
+		error = copyout(p, ireq->i_data, ireq->i_len);
+		FREE(p, M_TEMP);
+	} else
+		ireq->i_len = 0;
+
+	return error;
+}
+#endif /* COMPAT_FREEBSD6 */
 
+struct scanreq {
+	struct ieee80211req_scan_result *sr;
+	size_t space;
+};
+
+static size_t
+scan_space(const struct ieee80211_scan_entry *se, int *ielen)
+{
+	size_t len;
+
+	*ielen = 0;
+	if (se->se_wpa_ie != NULL)
+		*ielen += 2+se->se_wpa_ie[1];
+	if (se->se_rsn_ie != NULL)
+		*ielen += 2+se->se_rsn_ie[1];
+	if (se->se_wme_ie != NULL)
+		*ielen += 2+se->se_wme_ie[1];
+	if (se->se_ath_ie != NULL)
+		*ielen += 2+se->se_ath_ie[1];
 	/*
-	 * The value sr->isr_ie_len is defined as a uint8_t, so we
-	 * need to be careful to avoid an integer overflow.  If the
-	 * value would overflow, we will set isr_ie_len to zero, and
-	 * ieee80211_ioctl_getscanresults (below) will avoid copying
-	 * the (overflowing) data.
+	 * NB: ie's can be no more than 255 bytes and the max 802.11
+	 * packet is <3Kbytes so we are sure this doesn't overflow
+	 * 16-bits; if this is a concern we can drop the ie's.
 	 */
-	if (ielen > 255)
-		ielen = 0;
+	len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen;
+	return roundup(len, sizeof(uint32_t));
+}
+
+static void
+get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
+{
+	struct scanreq *req = arg;
+	int ielen;
+
+	req->space += scan_space(se, &ielen);
+}
+
+static void
+get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
+{
+	struct scanreq *req = arg;
+	struct ieee80211req_scan_result *sr;
+	int ielen, len, nr, nxr;
+	uint8_t *cp;
+
+	len = scan_space(se, &ielen);
+	if (len > req->space)
+		return;
+
+	sr = req->sr;
+	KASSERT(len <= 65535 && ielen <= 65535,
+	    ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
+	sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
 	sr->isr_ie_len = ielen;
-	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);
+	sr->isr_len = len;
+	sr->isr_freq = se->se_chan->ic_freq;
+	sr->isr_flags = se->se_chan->ic_flags;
+	sr->isr_rssi = se->se_rssi;
+	sr->isr_noise = se->se_noise;
+	sr->isr_intval = se->se_intval;
+	sr->isr_capinfo = se->se_capinfo;
+	sr->isr_erp = se->se_erp;
+	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
+	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
+	memcpy(sr->isr_rates, se->se_rates+2, nr);
+	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
+	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
+	sr->isr_nrates = nr + nxr;
+
+	sr->isr_ssid_len = se->se_ssid[1];
+	cp = ((uint8_t *)sr) + sr->isr_ie_off;
+	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
+
+	if (ielen) {
+		cp += sr->isr_ssid_len;
+		cp = copyie(cp, se->se_wpa_ie);
+		cp = copyie(cp, se->se_rsn_ie);
+		cp = copyie(cp, se->se_wme_ie);
+		cp = copyie(cp, se->se_ath_ie);
+		cp = copyie(cp, se->se_htcap_ie);
+	}
+
+	req->space -= len;
+	req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
 }
 
 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;
+	struct scanreq req;
+	int error;
+
+	if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
+		return EFAULT;
 
-	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 (sr->isr_ie_len > 0 && 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 (sr->isr_ie_len > 0 && 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;
+	req.space = 0;
+	ieee80211_scan_iterate(ic, get_scan_space, &req);
+	if (req.space > ireq->i_len)
+		req.space = ireq->i_len;
+	if (req.space > 0) {
+		size_t space;
+		void *p;
+
+		space = req.space;
+		/* XXX M_WAITOK after driver lock released */
+		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
+		if (p == NULL)
+			return ENOMEM;
+		req.sr = p;
+		ieee80211_scan_iterate(ic, get_scan_result, &req);
+		ireq->i_len = space - req.space;
+		error = copyout(p, ireq->i_data, ireq->i_len);
+		FREE(p, M_TEMP);
+	} else
+		ireq->i_len = 0;
+
 	return error;
 }
 
@@ -1072,10 +510,14 @@
 	*ielen = 0;
 	if (ni->ni_wpa_ie != NULL)
 		*ielen += 2+ni->ni_wpa_ie[1];
+	if (ni->ni_rsn_ie != NULL)
+		*ielen += 2+ni->ni_rsn_ie[1];
 	if (ni->ni_wme_ie != NULL)
 		*ielen += 2+ni->ni_wme_ie[1];
+	if (ni->ni_ath_ie != NULL)
+		*ielen += 2+ni->ni_ath_ie[1];
 	return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
-		      sizeof(u_int32_t));
+		      sizeof(uint32_t));
 }
 
 static void
@@ -1098,7 +540,7 @@
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211req_sta_info *si;
 	size_t ielen, len;
-	u_int8_t *cp;
+	uint8_t *cp;
 
 	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
 	    ni->ni_associd == 0)	/* only associated stations */
@@ -1110,12 +552,14 @@
 		return;
 	si = req->si;
 	si->isi_len = len;
+	si->isi_ie_off = sizeof(struct ieee80211req_sta_info);
 	si->isi_ie_len = ielen;
 	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);
+	ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
+	si->isi_noise = 0;		/* XXX */
 	si->isi_capinfo = ni->ni_capinfo;
 	si->isi_erp = ni->ni_erp;
 	IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
@@ -1124,6 +568,7 @@
 		si->isi_nrates = 15;
 	memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
 	si->isi_txrate = ni->ni_txrate;
+	si->isi_ie_len = ielen;
 	si->isi_associd = ni->ni_associd;
 	si->isi_txpower = ni->ni_txpower;
 	si->isi_vlan = ni->ni_vlan;
@@ -1131,8 +576,8 @@
 		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];
+		si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID];
+		si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID];
 	}
 	/* NB: leave all cases in case we relax ni_associd == 0 check */
 	if (ieee80211_node_is_authorized(ni))
@@ -1143,55 +588,95 @@
 		si->isi_inact = ic->ic_inact_init;
 	si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
 
-	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];
+	if (ielen) {
+		cp = ((uint8_t *)si) + si->isi_ie_off;
+		cp = copyie(cp, ni->ni_wpa_ie);
+		cp = copyie(cp, ni->ni_rsn_ie);
+		cp = copyie(cp, ni->ni_wme_ie);
+		cp = copyie(cp, ni->ni_ath_ie);
 	}
 
-	req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len);
+	req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
 	req->space -= len;
 }
 
 static int
-ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
+	struct ieee80211_node *ni, int off)
 {
 	struct stainforeq req;
+	size_t space;
+	void *p;
 	int error;
 
-	if (ireq->i_len < sizeof(struct stainforeq))
-		return EFAULT;
-
 	error = 0;
 	req.space = 0;
-	ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
+	if (ni == NULL)
+		ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
+	else
+		get_sta_space(&req, ni);
 	if (req.space > ireq->i_len)
 		req.space = ireq->i_len;
 	if (req.space > 0) {
-		size_t space;
-		void *p;
-
 		space = req.space;
 		/* XXX M_WAITOK after driver lock released */
 		MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
-		if (p == NULL)
-			return ENOMEM;
+		if (p == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
 		req.si = p;
-		ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
+		if (ni == NULL)
+			ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
+		else
+			get_sta_info(&req, ni);
 		ireq->i_len = space - req.space;
-		error = copyout(p, ireq->i_data, ireq->i_len);
+		error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len);
 		FREE(p, M_TEMP);
 	} else
 		ireq->i_len = 0;
-
+bad:
+	if (ni != NULL)
+		ieee80211_free_node(ni);
 	return error;
 }
 
 static int
+ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	uint8_t macaddr[IEEE80211_ADDR_LEN];
+	const int off = __offsetof(struct ieee80211req_sta_req, info);
+	struct ieee80211_node *ni;
+	int error;
+
+	if (ireq->i_len < sizeof(struct ieee80211req_sta_req))
+		return EFAULT;
+	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
+	if (error != 0)
+		return error;
+	if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
+		ni = NULL;
+	} else {
+		ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+		if (ni == NULL)
+			return EINVAL;
+	}
+	return getstainfo_common(ic, ireq, ni, off);
+}
+
+#ifdef COMPAT_FREEBSD6
+#define	IEEE80211_IOC_STA_INFO_OLD	45
+
+static int
+old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
+		return EFAULT;
+	return getstainfo_common(ic, ireq, NULL, 0);
+}
+#endif /* COMPAT_FREEBSD6 */
+
+static int
 ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
 {
 	struct ieee80211_node *ni;
@@ -1263,6 +748,28 @@
 }
 
 /*
+ * Return the current ``state'' of an Atheros capbility.
+ * If associated in station mode report the negotiated
+ * setting. Otherwise report the current setting.
+ */
+static int
+getathcap(struct ieee80211com *ic, int cap)
+{
+	if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
+		return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
+	else
+		return (ic->ic_flags & cap) != 0;
+}
+
+static int
+ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	if (ireq->i_len != sizeof(struct ieee80211_channel))
+		return EINVAL;
+	return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
+}
+
+/*
  * 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,
@@ -1287,7 +794,7 @@
 	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
 	int error = 0;
 	u_int kid, len, m;
-	u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
+	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
 	char tmpssid[IEEE80211_NWID_LEN];
 
 	switch (ireq->i_type) {
@@ -1295,8 +802,8 @@
 		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);
+			ireq->i_len = ic->ic_des_ssid[0].len;
+			memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
 			break;
 		default:
 			ireq->i_len = ic->ic_bss->ni_esslen;
@@ -1323,7 +830,7 @@
 			return EINVAL;
 		len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
 		/* NB: only root can read WEP keys */
-		if (suser(curthread) == 0) {
+		if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
 			bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
 		} else {
 			bzero(tmpkey, len);
@@ -1457,8 +964,16 @@
 				ireq->i_data, ireq->i_len);
 		break;
 	case IEEE80211_IOC_WPAIE:
-		error = ieee80211_ioctl_getwpaie(ic, ireq);
+		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
+		break;
+	case IEEE80211_IOC_WPAIE2:
+		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
 		break;
+#ifdef COMPAT_FREEBSD6
+	case IEEE80211_IOC_SCAN_RESULTS_OLD:
+		error = old_getscanresults(ic, ireq);
+		break;
+#endif
 	case IEEE80211_IOC_SCAN_RESULTS:
 		error = ieee80211_ioctl_getscanresults(ic, ireq);
 		break;
@@ -1471,6 +986,11 @@
 	case IEEE80211_IOC_STA_TXPOW:
 		error = ieee80211_ioctl_getstatxpow(ic, ireq);
 		break;
+#ifdef COMPAT_FREEBSD6
+	case IEEE80211_IOC_STA_INFO_OLD:
+		error = old_getstainfo(ic, ireq);
+		break;
+#endif
 	case IEEE80211_IOC_STA_INFO:
 		error = ieee80211_ioctl_getstainfo(ic, ireq);
 		break;
@@ -1482,15 +1002,51 @@
 	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;
+	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;
+	case IEEE80211_IOC_PUREG:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+		break;
+	case IEEE80211_IOC_FF:
+		ireq->i_val = getathcap(ic, IEEE80211_F_FF);
+		break;
+	case IEEE80211_IOC_TURBOP:
+		ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
+		break;
+	case IEEE80211_IOC_BGSCAN:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
+		break;
+	case IEEE80211_IOC_BGSCAN_IDLE:
+		ireq->i_val = ic->ic_bgscanidle*hz/1000;	/* ms */
+		break;
+	case IEEE80211_IOC_BGSCAN_INTERVAL:
+		ireq->i_val = ic->ic_bgscanintvl/hz;		/* seconds */
+		break;
+	case IEEE80211_IOC_SCANVALID:
+		ireq->i_val = ic->ic_scanvalid/hz;		/* seconds */
 		break;
-	case IEEE80211_IOC_BEACON_INTERVAL:
-		/* NB: get from ic_bss for station mode */
-		ireq->i_val = ic->ic_bss->ni_intval;
+	case IEEE80211_IOC_ROAM_RSSI_11A:
+		ireq->i_val = ic->ic_roam.rssi11a;
 		break;
-	case IEEE80211_IOC_PUREG:
-		ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+	case IEEE80211_IOC_ROAM_RSSI_11B:
+		ireq->i_val = ic->ic_roam.rssi11bOnly;
+		break;
+	case IEEE80211_IOC_ROAM_RSSI_11G:
+		ireq->i_val = ic->ic_roam.rssi11b;
+		break;
+	case IEEE80211_IOC_ROAM_RATE_11A:
+		ireq->i_val = ic->ic_roam.rate11a;
+		break;
+	case IEEE80211_IOC_ROAM_RATE_11B:
+		ireq->i_val = ic->ic_roam.rate11bOnly;
+		break;
+	case IEEE80211_IOC_ROAM_RATE_11G:
+		ireq->i_val = ic->ic_roam.rate11b;
 		break;
 	case IEEE80211_IOC_MCAST_RATE:
 		ireq->i_val = ic->ic_mcast_rate;
@@ -1504,6 +1060,65 @@
 	case IEEE80211_IOC_BURST:
 		ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
 		break;
+	case IEEE80211_IOC_BMISSTHRESHOLD:
+		ireq->i_val = ic->ic_bmissthreshold;
+		break;
+	case IEEE80211_IOC_CURCHAN:
+		error = ieee80211_ioctl_getcurchan(ic, ireq);
+		break;
+	case IEEE80211_IOC_SHORTGI:
+		ireq->i_val = 0;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+			ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+			ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
+		break;
+	case IEEE80211_IOC_AMPDU:
+		ireq->i_val = 0;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
+			ireq->i_val |= 1;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
+			ireq->i_val |= 2;
+		break;
+	case IEEE80211_IOC_AMPDU_LIMIT:
+		ireq->i_val = ic->ic_ampdu_limit;	/* XXX truncation? */
+		break;
+	case IEEE80211_IOC_AMPDU_DENSITY:
+		ireq->i_val = ic->ic_ampdu_density;
+		break;
+	case IEEE80211_IOC_AMSDU:
+		ireq->i_val = 0;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
+			ireq->i_val |= 1;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
+			ireq->i_val |= 2;
+		break;
+	case IEEE80211_IOC_AMSDU_LIMIT:
+		ireq->i_val = ic->ic_amsdu_limit;	/* XXX truncation? */
+		break;
+	case IEEE80211_IOC_PUREN:
+		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
+		break;
+	case IEEE80211_IOC_DOTH:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
+		break;
+	case IEEE80211_IOC_HTCOMPAT:
+		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+		break;
+	case IEEE80211_IOC_INACTIVITY:
+		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
+		break;
+	case IEEE80211_IOC_HTPROTMODE:
+		ireq->i_val = ic->ic_htprotmode;
+		break;
+	case IEEE80211_IOC_HTCONF:
+		if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
+			ireq->i_val = 1;
+			if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+				ireq->i_val |= 2;
+		} else
+			ireq->i_val = 0;
+		break;
 	default:
 		error = EINVAL;
 		break;
@@ -1527,6 +1142,8 @@
 		return EINVAL;
 	if (ireq->i_len > IEEE80211_MAX_OPT_IE)
 		return EINVAL;
+	/* NB: data.length is validated by the wireless extensions code */
+	/* XXX M_WAITOK after driver lock released */
 	if (ireq->i_len > 0) {
 		MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
 		if (ie == NULL)
@@ -1555,7 +1172,7 @@
 	struct ieee80211req_key ik;
 	struct ieee80211_node *ni;
 	struct ieee80211_key *wk;
-	u_int16_t kid;
+	uint16_t kid;
 	int error;
 
 	if (ireq->i_len != sizeof(ik))
@@ -1632,8 +1249,8 @@
 	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) {
+	/* XXX uint8_t -> uint16_t */
+	if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
 		struct ieee80211_node *ni;
 
 		if (ic->ic_opmode == IEEE80211_M_STA) {
@@ -1675,6 +1292,32 @@
 	ieee80211_node_leave(ic, ni);
 }
 
+struct scanlookup {
+	const uint8_t *mac;
+	int esslen;
+	const uint8_t *essid;
+	const struct ieee80211_scan_entry *se;
+};
+
+/*
+ * Match mac address and any ssid.
+ */
+static void
+mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
+{
+	struct scanlookup *look = arg;
+
+	if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr))
+		return;
+	if (look->esslen != 0) {
+		if (se->se_ssid[1] != look->esslen)
+			return;
+		if (memcmp(look->essid, se->se_ssid+2, look->esslen))
+			return;
+	}
+	look->se = se;
+}
+
 static int
 ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
 {
@@ -1689,31 +1332,21 @@
 		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? */
+		/* XXX ibss/ahdemo */
+		if (ic->ic_opmode == IEEE80211_M_STA) {
+			struct scanlookup lookup;
 
-		if (mlme.im_ssid_len != 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,
-				mlme.im_ssid_len, mlme.im_ssid);
-		} 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;
+			lookup.se = NULL;
+			lookup.mac = mlme.im_macaddr;
+			/* XXX use revised api w/ explicit ssid */
+			lookup.esslen = ic->ic_des_ssid[0].len;
+			lookup.essid = ic->ic_des_ssid[0].ssid;
+			ieee80211_scan_iterate(ic, mlmelookup, &lookup);
+			if (lookup.se != NULL &&
+			    ieee80211_sta_join(ic, lookup.se))
+				return 0;
 		}
-		break;
+		return EINVAL;
 	case IEEE80211_MLME_DISASSOC:
 	case IEEE80211_MLME_DEAUTH:
 		switch (ic->ic_opmode) {
@@ -1761,7 +1394,7 @@
 static int
 ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
 {
-	u_int8_t mac[IEEE80211_ADDR_LEN];
+	uint8_t mac[IEEE80211_ADDR_LEN];
 	const struct ieee80211_aclator *acl = ic->ic_acl;
 	int error;
 
@@ -1825,7 +1458,7 @@
 {
 	struct ieee80211req_chanlist list;
 	u_char chanlist[IEEE80211_CHAN_BYTES];
-	int i, j, error;
+	int i, j, nchan, error;
 
 	if (ireq->i_len != sizeof(list))
 		return EINVAL;
@@ -1841,34 +1474,31 @@
 		i = 1;
 	else
 		i = 0;
+	nchan = 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))
+		if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) {
 			setbit(chanlist, i);
+			nchan++;
+		}
 	}
-	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:
-		;
-	}
+	if (nchan == 0)
+		return EINVAL;
+	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&	/* XXX */
+	    isclr(chanlist, ic->ic_bsschan->ic_ieee))
+		ic->ic_bsschan = IEEE80211_CHAN_ANYC;
 	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
-	return IS_UP_AUTO(ic) ? ENETRESET : 0;
+	return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
 }
 
 static int
 ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
 {
 	struct ieee80211_node *ni;
-	u_int8_t macaddr[IEEE80211_ADDR_LEN];
+	uint8_t macaddr[IEEE80211_ADDR_LEN];
 	int error;
 
 	/*
@@ -1998,15 +1628,273 @@
 }
 
 static int
+find11gchannel(struct ieee80211com *ic, int start, int freq)
+{
+	const struct ieee80211_channel *c;
+	int i;
+
+	for (i = start+1; i < ic->ic_nchans; i++) {
+		c = &ic->ic_channels[i];
+		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+			return 1;
+	}
+	/* NB: should not be needed but in case things are mis-sorted */
+	for (i = 0; i < start; i++) {
+		c = &ic->ic_channels[i];
+		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+			return 1;
+	}
+	return 0;
+}
+
+static struct ieee80211_channel *
+findchannel(struct ieee80211com *ic, int ieee, int mode)
+{
+	static const u_int chanflags[IEEE80211_MODE_MAX] = {
+		0,			/* IEEE80211_MODE_AUTO */
+		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
+		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
+		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11G */
+		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
+		IEEE80211_CHAN_108A,	/* IEEE80211_MODE_TURBO_A */
+		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
+		IEEE80211_CHAN_STURBO,	/* IEEE80211_MODE_STURBO_A */
+		/* NB: handled specially below */
+		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA */
+		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG */
+	};
+	u_int modeflags;
+	int i;
+
+	KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
+	modeflags = chanflags[mode];
+	KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
+	    ("no chanflags for mode %u", mode));
+	for (i = 0; i < ic->ic_nchans; i++) {
+		struct ieee80211_channel *c = &ic->ic_channels[i];
+
+		if (c->ic_ieee != ieee)
+			continue;
+		if (mode == IEEE80211_MODE_AUTO) {
+			/* ignore turbo channels for autoselect */
+			if (IEEE80211_IS_CHAN_TURBO(c))
+				continue;
+			/*
+			 * XXX special-case 11b/g channels so we
+			 *     always select the g channel if both
+			 *     are present.
+			 * XXX prefer HT to non-HT?
+			 */
+			if (!IEEE80211_IS_CHAN_B(c) ||
+			    !find11gchannel(ic, i, c->ic_freq))
+				return c;
+		} else {
+			/* must check HT specially */
+			if ((mode == IEEE80211_MODE_11NA ||
+			    mode == IEEE80211_MODE_11NG) &&
+			    !IEEE80211_IS_CHAN_HT(c))
+				continue;
+			if ((c->ic_flags & modeflags) == modeflags)
+				return c;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Check the specified against any desired mode (aka netband).
+ * This is only used (presently) when operating in hostap mode
+ * to enforce consistency.
+ */
+static int
+check_mode_consistency(const struct ieee80211_channel *c, int mode)
+{
+	KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel"));
+
+	switch (mode) {
+	case IEEE80211_MODE_11B:
+		return (IEEE80211_IS_CHAN_B(c));
+	case IEEE80211_MODE_11G:
+		return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
+	case IEEE80211_MODE_11A:
+		return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
+	case IEEE80211_MODE_STURBO_A:
+		return (IEEE80211_IS_CHAN_STURBO(c));
+	case IEEE80211_MODE_11NA:
+		return (IEEE80211_IS_CHAN_HTA(c));
+	case IEEE80211_MODE_11NG:
+		return (IEEE80211_IS_CHAN_HTG(c));
+	}
+	return 1;
+
+}
+
+/*
+ * Common code to set the current channel.  If the device
+ * is up and running this may result in an immediate channel
+ * change or a kick of the state machine.
+ */
+static int
+setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+	int error;
+
+	if (c != IEEE80211_CHAN_ANYC) {
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		    !check_mode_consistency(c, ic->ic_des_mode))
+			return EINVAL;
+		if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
+			return 0;	/* NB: nothing to do */
+	}
+	ic->ic_des_chan = c;
+
+	error = 0;
+	if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
+	    ic->ic_opmode == IEEE80211_M_WDS) &&
+	    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+		/*
+		 * Monitor and wds modes can switch directly.
+		 */
+		ic->ic_curchan = ic->ic_des_chan;
+		if (ic->ic_state == IEEE80211_S_RUN)
+			ic->ic_set_channel(ic);
+	} else {
+		/*
+		 * Need to go through the state machine in case we
+		 * need to reassociate or the like.  The state machine
+		 * will pickup the desired channel and avoid scanning.
+		 */
+		if (IS_UP_AUTO(ic))
+			error = ieee80211_init(ic, RESCAN);
+		else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+			/*
+			 * When not up+running and a real channel has
+			 * been specified fix the current channel so
+			 * there is immediate feedback; e.g. via ifconfig.
+			 */
+			ic->ic_curchan = ic->ic_des_chan;
+		}
+	}
+	return error;
+}
+
+/*
+ * Old api for setting the current channel; this is
+ * deprecated because channel numbers are ambiguous.
+ */
+static int
+ieee80211_ioctl_setchannel(struct ieee80211com *ic,
+	const struct ieee80211req *ireq)
+{
+	struct ieee80211_channel *c;
+
+	/* XXX 0xffff overflows 16-bit signed */
+	if (ireq->i_val == 0 ||
+	    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) {
+		c = IEEE80211_CHAN_ANYC;
+	} else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) {
+		return EINVAL;
+	} else {
+		struct ieee80211_channel *c2;
+
+		c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
+		if (c == NULL) {
+			c = findchannel(ic, ireq->i_val,
+				IEEE80211_MODE_AUTO);
+			if (c == NULL)
+				return EINVAL;
+		}
+		/*
+		 * Fine tune channel selection based on desired mode:
+		 *   if 11b is requested, find the 11b version of any
+		 *      11g channel returned,
+		 *   if static turbo, find the turbo version of any
+		 *	11a channel return,
+		 *   if 11na is requested, find the ht version of any
+		 *      11a channel returned,
+		 *   if 11ng is requested, find the ht version of any
+		 *      11g channel returned,
+		 *   otherwise we should be ok with what we've got.
+		 */
+		switch (ic->ic_des_mode) {
+		case IEEE80211_MODE_11B:
+			if (IEEE80211_IS_CHAN_ANYG(c)) {
+				c2 = findchannel(ic, ireq->i_val,
+					IEEE80211_MODE_11B);
+				/* NB: should not happen, =>'s 11g w/o 11b */
+				if (c2 != NULL)
+					c = c2;
+			}
+			break;
+		case IEEE80211_MODE_TURBO_A:
+			if (IEEE80211_IS_CHAN_A(c)) {
+				c2 = findchannel(ic, ireq->i_val,
+					IEEE80211_MODE_TURBO_A);
+				if (c2 != NULL)
+					c = c2;
+			}
+			break;
+		case IEEE80211_MODE_11NA:
+			if (IEEE80211_IS_CHAN_A(c)) {
+				c2 = findchannel(ic, ireq->i_val,
+					IEEE80211_MODE_11NA);
+				if (c2 != NULL)
+					c = c2;
+			}
+			break;
+		case IEEE80211_MODE_11NG:
+			if (IEEE80211_IS_CHAN_ANYG(c)) {
+				c2 = findchannel(ic, ireq->i_val,
+					IEEE80211_MODE_11NG);
+				if (c2 != NULL)
+					c = c2;
+			}
+			break;
+		default:		/* NB: no static turboG */
+			break;
+		}
+	}
+	return setcurchan(ic, c);
+}
+
+/*
+ * New/current api for setting the current channel; a complete
+ * channel description is provide so there is no ambiguity in
+ * identifying the channel.
+ */
+static int
+ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
+	const struct ieee80211req *ireq)
+{
+	struct ieee80211_channel chan, *c;
+	int error;
+
+	if (ireq->i_len != sizeof(chan))
+		return EINVAL;
+	error = copyin(ireq->i_data, &chan, sizeof(chan));
+	if (error != 0)
+		return error;
+	/* XXX 0xffff overflows 16-bit signed */
+	if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) {
+		c = IEEE80211_CHAN_ANYC;
+	} else {
+		c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags);
+		if (c == NULL)
+			return EINVAL;
+	}
+	return setcurchan(ic, c);
+}
+
+static int
 ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
 {
-	static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+	static const uint8_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];
+	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
 	char tmpssid[IEEE80211_NWID_LEN];
-	u_int8_t tmpbssid[IEEE80211_ADDR_LEN];
+	uint8_t tmpbssid[IEEE80211_ADDR_LEN];
 	struct ieee80211_key *k;
 	int j, caps;
 	u_int kid;
@@ -2020,10 +1908,12 @@
 		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);
-		error = ENETRESET;
+		memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
+		ic->ic_des_ssid[0].len = ireq->i_len;
+		memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
+		ic->ic_des_nssid = (ireq->i_len > 0);
+		if (IS_UP_AUTO(ic))
+			error = ieee80211_init(ic, RESCAN);
 		break;
 	case IEEE80211_IOC_WEP:
 		switch (ireq->i_val) {
@@ -2040,7 +1930,8 @@
 			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
 			break;
 		}
-		error = ENETRESET;
+		if (IS_UP_AUTO(ic))
+			error = ieee80211_init(ic, RESCAN);
 		break;
 	case IEEE80211_IOC_WEPKEY:
 		kid = (u_int) ireq->i_val;
@@ -2069,16 +1960,13 @@
 		} else
 			error = EINVAL;
 		ieee80211_key_update_end(ic);
-		if (!error)			/* NB: for compatibility */
-			error = ENETRESET;
 		break;
 	case IEEE80211_IOC_WEPTXKEY:
 		kid = (u_int) ireq->i_val;
 		if (kid >= IEEE80211_WEP_NKID &&
-		    (u_int16_t) kid != IEEE80211_KEYIX_NONE)
+		    (uint16_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) {
@@ -2118,48 +2006,11 @@
 		ic->ic_bss->ni_authmode = ireq->i_val;
 		/* XXX mixed/mode/usage? */
 		ic->ic_auth = auth;
-		error = ENETRESET;
+		if (IS_UP_AUTO(ic))
+			error = ieee80211_init(ic, RESCAN);
 		break;
 	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;
-		default:
-			/*
-			 * If the desired channel has changed (to something
-			 * other than any) and we're not already scanning,
-			 * then kick the state machine.
-			 */
-			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;
-		}
-		if (error == ENETRESET &&
-			ic->ic_opmode == IEEE80211_M_MONITOR) {
-			if (IS_UP(ic)) {
-				/*
-				 * Monitor mode can switch directly.
-				 */
-				if (ic->ic_des_chan != IEEE80211_CHAN_ANYC)
-					ic->ic_curchan = ic->ic_des_chan;
-				error = ic->ic_reset(ic->ic_ifp);
-			} else
-				error = 0;
-		}
+		error = ieee80211_ioctl_setchannel(ic, ireq);
 		break;
 	case IEEE80211_IOC_POWERSAVE:
 		switch (ireq->i_val) {
@@ -2181,6 +2032,13 @@
 			error = EINVAL;
 			break;
 		}
+		if (error == ENETRESET) {
+			/*
+			 * Switching in+out of power save mode
+			 * should not require a state change.
+			 */
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		}
 		break;
 	case IEEE80211_IOC_POWERSAVESLEEP:
 		if (ireq->i_val < 0)
@@ -2200,14 +2058,15 @@
 			return EINVAL;
 		ic->ic_protmode = ireq->i_val;
 		/* NB: if not operating in 11g this can wait */
-		if (ic->ic_curmode == IEEE80211_MODE_11G)
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
 			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))
+		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;
@@ -2268,7 +2127,7 @@
 			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
 			break;
 		}
-		error = ENETRESET;		/* XXX? */
+		error = ENETRESET;
 		break;
 	case IEEE80211_IOC_WME:
 		if (ireq->i_val) {
@@ -2277,7 +2136,8 @@
 			ic->ic_flags |= IEEE80211_F_WME;
 		} else
 			ic->ic_flags &= ~IEEE80211_F_WME;
-		error = ENETRESET;		/* XXX maybe not for station? */
+		if (IS_UP_AUTO(ic))
+			error = ieee80211_init(ic, 0);
 		break;
 	case IEEE80211_IOC_HIDESSID:
 		if (ireq->i_val)
@@ -2340,8 +2200,8 @@
 		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);
+		ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
+			       ((uint16_t) ireq->i_len);
 		break;
 	case IEEE80211_IOC_KEYMGTALGS:
 		/* XXX check */
@@ -2364,17 +2224,21 @@
 			ic->ic_flags &= ~IEEE80211_F_DESBSSID;
 		else
 			ic->ic_flags |= IEEE80211_F_DESBSSID;
-		error = ENETRESET;
+		if (IS_UP_AUTO(ic))
+			error = ieee80211_init(ic, RESCAN);
 		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);
+		if (!IS_UP(ic))
+			return EINVAL;
+		(void) ieee80211_start_scan(ic,
+			IEEE80211_SCAN_ACTIVE |
+			IEEE80211_SCAN_NOPICK |
+			IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
+			/* XXX use ioctl params */
+			ic->ic_des_nssid, ic->ic_des_ssid);
 		break;
 	case IEEE80211_IOC_ADDMAC:
 	case IEEE80211_IOC_DELMAC:
@@ -2425,9 +2289,72 @@
 		else
 			ic->ic_flags &= ~IEEE80211_F_PUREG;
 		/* NB: reset only if we're operating on an 11g channel */
-		if (ic->ic_curmode == IEEE80211_MODE_11G)
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
 			error = ENETRESET;
 		break;
+	case IEEE80211_IOC_FF:
+		if (ireq->i_val) {
+			if ((ic->ic_caps & IEEE80211_C_FF) == 0)
+				return EINVAL;
+			ic->ic_flags |= IEEE80211_F_FF;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_FF;
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_TURBOP:
+		if (ireq->i_val) {
+			if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
+				return EINVAL;
+			ic->ic_flags |= IEEE80211_F_TURBOP;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_TURBOP;
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_BGSCAN:
+		if (ireq->i_val) {
+			if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
+				return EINVAL;
+			ic->ic_flags |= IEEE80211_F_BGSCAN;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+		break;
+	case IEEE80211_IOC_BGSCAN_IDLE:
+		if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
+			ic->ic_bgscanidle = ireq->i_val*hz/1000;
+		else
+			error = EINVAL;
+		break;
+	case IEEE80211_IOC_BGSCAN_INTERVAL:
+		if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
+			ic->ic_bgscanintvl = ireq->i_val*hz;
+		else
+			error = EINVAL;
+		break;
+	case IEEE80211_IOC_SCANVALID:
+		if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
+			ic->ic_scanvalid = ireq->i_val*hz;
+		else
+			error = EINVAL;
+		break;
+	case IEEE80211_IOC_ROAM_RSSI_11A:
+		ic->ic_roam.rssi11a = ireq->i_val;
+		break;
+	case IEEE80211_IOC_ROAM_RSSI_11B:
+		ic->ic_roam.rssi11bOnly = ireq->i_val;
+		break;
+	case IEEE80211_IOC_ROAM_RSSI_11G:
+		ic->ic_roam.rssi11b = ireq->i_val;
+		break;
+	case IEEE80211_IOC_ROAM_RATE_11A:
+		ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
+		break;
+	case IEEE80211_IOC_ROAM_RATE_11B:
+		ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
+		break;
+	case IEEE80211_IOC_ROAM_RATE_11G:
+		ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
+		break;
 	case IEEE80211_IOC_MCAST_RATE:
 		ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
 		break;
@@ -2450,12 +2377,146 @@
 			ic->ic_flags &= ~IEEE80211_F_BURST;
 		error = ENETRESET;		/* XXX maybe not for station? */
 		break;
+	case IEEE80211_IOC_BMISSTHRESHOLD:
+		if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
+		      ireq->i_val <= IEEE80211_HWBMISS_MAX))
+			return EINVAL;
+		ic->ic_bmissthreshold = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_CURCHAN:
+		error = ieee80211_ioctl_setcurchan(ic, ireq);
+		break;
+	case IEEE80211_IOC_SHORTGI:
+		if (ireq->i_val) {
+#define	IEEE80211_HTCAP_SHORTGI \
+	(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
+			if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
+				return EINVAL;
+			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
+				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
+				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+#undef IEEE80211_HTCAP_SHORTGI
+		} else
+			ic->ic_flags_ext &=
+			    ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
+		/* XXX kick state machine? */
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_AMPDU:
+		if (ireq->i_val) {
+			if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
+				return EINVAL;
+			if (ireq->i_val & 1)
+				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+			if (ireq->i_val & 2)
+				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+		} else
+			ic->ic_flags_ext &=
+			    ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
+		/* NB: reset only if we're operating on an 11n channel */
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+			error = ENETRESET;
+		break;
+	case IEEE80211_IOC_AMPDU_LIMIT:
+		/* XXX validate */
+		ic->ic_ampdu_limit = ireq->i_val;
+		break;
+	case IEEE80211_IOC_AMPDU_DENSITY:
+		/* XXX validate */
+		ic->ic_ampdu_density = ireq->i_val;
+		break;
+	case IEEE80211_IOC_AMSDU:
+		if (ireq->i_val) {
+			if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
+				return EINVAL;
+			if (ireq->i_val & 1)
+				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+			if (ireq->i_val & 2)
+				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+		} else
+			ic->ic_flags_ext &=
+			    ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
+		/* NB: reset only if we're operating on an 11n channel */
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+			error = ENETRESET;
+		break;
+	case IEEE80211_IOC_AMSDU_LIMIT:
+		/* XXX validate */
+		ic->ic_amsdu_limit = ireq->i_val;	/* XXX truncation? */
+		break;
+	case IEEE80211_IOC_PUREN:
+		if (ireq->i_val) {
+			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
+				return EINVAL;
+			ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
+		} else
+			ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
+		/* NB: reset only if we're operating on an 11n channel */
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+			error = ENETRESET;
+		break;
+	case IEEE80211_IOC_DOTH:
+		if (ireq->i_val) {
+#if 0
+			/* XXX no capability */
+			if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
+				return EINVAL;
+#endif
+			ic->ic_flags |= IEEE80211_F_DOTH;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_DOTH;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_HTCOMPAT:
+		if (ireq->i_val) {
+			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
+				return EINVAL;
+			ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
+		} else
+			ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
+		/* NB: reset only if we're operating on an 11n channel */
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+			error = ENETRESET;
+		break;
+	case IEEE80211_IOC_INACTIVITY:
+		if (ireq->i_val)
+			ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
+		else
+			ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
+		break;
+	case IEEE80211_IOC_HTPROTMODE:
+		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
+			return EINVAL;
+		ic->ic_htprotmode = ireq->i_val ?
+		    IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
+		/* NB: if not operating in 11n this can wait */
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+			error = ERESTART;
+		break;
+	case IEEE80211_IOC_HTCONF:
+		if (ireq->i_val & 1)
+			ic->ic_flags_ext |= IEEE80211_FEXT_HT;
+		else
+			ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
+		if (ireq->i_val & 2)
+			ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
+		else
+			ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
+		error = ENETRESET;
+		break;
 	default:
 		error = EINVAL;
 		break;
 	}
-	if (error == ENETRESET && !IS_UP_AUTO(ic))
-		error = 0;
+	if (error == ENETRESET)
+		error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
 	return error;
 }
 
@@ -2478,20 +2539,11 @@
 				(struct ieee80211req *) data);
 		break;
 	case SIOCS80211:
-		error = suser(curthread);
+		error = priv_check(curthread, PRIV_NET80211_MANAGE);
 		if (error == 0)
 			error = ieee80211_ioctl_set80211(ic, cmd,
 					(struct ieee80211req *) data);
 		break;
-	case SIOCGIFGENERIC:
-		error = ieee80211_cfgget(ic, cmd, data);
-		break;
-	case SIOCSIFGENERIC:
-		error = suser(curthread);
-		if (error)
-			break;
-		error = ieee80211_cfgset(ic, cmd, data);
-		break;
 	case SIOCG80211STATS:
 		ifr = (struct ifreq *)data;
 		copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
@@ -2533,10 +2585,10 @@
 
 			if (ipx_nullhost(*ina))
 				ina->x_host = *(union ipx_host *)
-				    IFP2ENADDR(ifp);
+				    IF_LLADDR(ifp);
 			else
 				bcopy((caddr_t) ina->x_host.c_host,
-				      (caddr_t) IFP2ENADDR(ifp),
+				      (caddr_t) IF_LLADDR(ifp),
 				      ETHER_ADDR_LEN);
 			/* fall thru... */
 		}
Index: ieee80211_freebsd.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_freebsd.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_freebsd.h -L sys/net80211/ieee80211_freebsd.h -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_freebsd.h
+++ sys/net80211/ieee80211_freebsd.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -10,8 +10,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -24,11 +22,24 @@
  * (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_freebsd.h,v 1.5.2.1 2005/09/03 22:40:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_freebsd.h,v 1.15.2.1 2007/11/11 17:44:35 sam Exp $
  */
 #ifndef _NET80211_IEEE80211_FREEBSD_H_
 #define _NET80211_IEEE80211_FREEBSD_H_
 
+#ifdef _KERNEL
+/*
+ * Common state locking definitions.
+ */
+typedef struct mtx ieee80211_com_lock_t;
+#define	IEEE80211_LOCK_INIT(_ic, _name) \
+	mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", MTX_DEF)
+#define	IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_comlock)
+#define	IEEE80211_LOCK(_ic)	   mtx_lock(&(_ic)->ic_comlock)
+#define	IEEE80211_UNLOCK(_ic)	   mtx_unlock(&(_ic)->ic_comlock)
+#define	IEEE80211_LOCK_ASSERT(_ic) \
+	mtx_assert(&(_ic)->ic_comlock, MA_OWNED)
+
 /*
  * Beacon locking definitions.
  */
@@ -61,7 +72,7 @@
  */
 typedef struct mtx ieee80211_scan_lock_t;
 #define	IEEE80211_SCAN_LOCK_INIT(_nt, _name) \
-	mtx_init(&(_nt)->nt_scanlock, _name, "802.11 scangen", MTX_DEF)
+	mtx_init(&(_nt)->nt_scanlock, _name, "802.11 node scangen", MTX_DEF)
 #define	IEEE80211_SCAN_LOCK_DESTROY(_nt)	mtx_destroy(&(_nt)->nt_scanlock)
 #define	IEEE80211_SCAN_LOCK(_nt)		mtx_lock(&(_nt)->nt_scanlock)
 #define	IEEE80211_SCAN_UNLOCK(_nt)		mtx_unlock(&(_nt)->nt_scanlock)
@@ -114,6 +125,28 @@
 	(_qlen) = ++(_ni)->ni_savedq.ifq_len; 			\
 } while (0)
 
+#define	IEEE80211_TAPQ_INIT(_tap) do {				\
+	mtx_init(&(tap)->txa_q.ifq_mtx, "ampdu tx queue", NULL, MTX_DEF); \
+	(_tap)->txa_q.ifq_maxlen = IEEE80211_AGGR_BAWMAX;	\
+} while (0)
+#define	IEEE80211_TAPQ_DESTROY(_tap) \
+	mtx_destroy(&(_tap)->txa_q.ifq_mtx)
+
+#ifndef IF_PREPEND_LIST
+#define _IF_PREPEND_LIST(ifq, mhead, mtail, mcount) do {	\
+	(mtail)->m_nextpkt = (ifq)->ifq_head;			\
+	if ((ifq)->ifq_tail == NULL)				\
+		(ifq)->ifq_tail = (mtail);			\
+	(ifq)->ifq_head = (mhead);				\
+	(ifq)->ifq_len += (mcount);				\
+} while (0)
+#define IF_PREPEND_LIST(ifq, mhead, mtail, mcount) do {		\
+	IF_LOCK(ifq);						\
+	_IF_PREPEND_LIST(ifq, mhead, mtail, mcount);		\
+	IF_UNLOCK(ifq);						\
+} while (0)
+#endif /* IF_PREPEND_LIST */
+
 /*
  * 802.1x MAC ACL database locking definitions.
  */
@@ -148,10 +181,30 @@
 int	ieee80211_node_dectestref(struct ieee80211_node *ni);
 #define	ieee80211_node_refcnt(_ni)	(_ni)->ni_refcnt
 
-struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen);
+struct ifqueue;
+void	ieee80211_drain_ifq(struct ifqueue *);
+
+#define	msecs_to_ticks(ms)	(((ms)*hz)/1000)
+#define	ticks_to_msecs(t)	((t) / hz)
+#define time_after(a,b) 	((long)(b) - (long)(a) < 0)
+#define time_before(a,b)	time_after(b,a)
+#define time_after_eq(a,b)	((long)(a) - (long)(b) >= 0)
+#define time_before_eq(a,b)	time_after_eq(b,a)
+
+struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
+
+/* tx path usage */
 #define	M_LINK0		M_PROTO1		/* WEP requested */
 #define	M_PWR_SAV	M_PROTO4		/* bypass PS handling */
 #define	M_MORE_DATA	M_PROTO5		/* more data frames to follow */
+#define	M_FF		0x20000			/* fast frame */
+#define	M_TXCB		0x40000			/* do tx complete callback */
+#define	M_80211_TX	(0x60000|M_PROTO1|M_WME_AC_MASK|M_PROTO4|M_PROTO5)
+
+/* rx path usage */
+#define	M_AMPDU		M_PROTO1		/* A-MPDU processing done */
+#define	M_WEP		M_PROTO2		/* WEP done by hardware */
+#define	M_80211_RX	(M_AMPDU|M_WEP)
 /*
  * Encode WME access control bits in the PROTO flags.
  * This is safe since it's passed directly in to the
@@ -176,6 +229,17 @@
 #define	M_AGE_GET(m)		(m->m_pkthdr.csum_data)
 #define	M_AGE_SUB(m,adj)	(m->m_pkthdr.csum_data -= adj)
 
+#define	MTAG_ABI_NET80211	1132948340	/* net80211 ABI */
+
+struct ieee80211_cb {
+	void	(*func)(struct ieee80211_node *, void *, int status);
+	void	*arg;
+};
+#define	NET80211_TAG_CALLBACK	0	/* xmit complete callback */
+int	ieee80211_add_callback(struct mbuf *m,
+		void (*func)(struct ieee80211_node *, void *, int), void *arg);
+void	ieee80211_process_callback(struct ieee80211_node *, struct mbuf *, int);
+
 void	get_random_bytes(void *, size_t);
 
 struct ieee80211com;
@@ -185,6 +249,37 @@
 
 void	ieee80211_load_module(const char *);
 
+#define	IEEE80211_CRYPTO_MODULE(name, version) \
+static int								\
+name##_modevent(module_t mod, int type, void *unused)			\
+{									\
+	switch (type) {							\
+	case MOD_LOAD:							\
+		ieee80211_crypto_register(&name);			\
+		return 0;						\
+	case MOD_UNLOAD:						\
+	case MOD_QUIESCE:						\
+		if (nrefs) {						\
+			printf("wlan_##name: still in use (%u dynamic refs)\n",\
+				nrefs);					\
+			return EBUSY;					\
+		}							\
+		if (type == MOD_UNLOAD)					\
+			ieee80211_crypto_unregister(&name);		\
+		return 0;						\
+	}								\
+	return EINVAL;							\
+}									\
+static moduledata_t name##_mod = {					\
+	"wlan_" #name,							\
+	name##_modevent,						\
+	0								\
+};									\
+DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\
+MODULE_VERSION(wlan_##name, version);					\
+MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1)
+#endif /* _KERNEL */
+
 /* XXX this stuff belongs elsewhere */
 /*
  * Message formats for messages from the net80211 layer to user
@@ -225,4 +320,37 @@
 #define	RTM_IEEE80211_MICHAEL	107	/* Michael MIC failure detected */
 #define	RTM_IEEE80211_REJOIN	108	/* station re-associate (ap mode) */
 
+/*
+ * Structure prepended to raw packets sent through the bpf
+ * interface when set to DLT_IEEE802_11_RADIO.  This allows
+ * user applications to specify pretty much everything in
+ * an Atheros tx descriptor.  XXX need to generalize.
+ *
+ * XXX cannot be more than 14 bytes as it is copied to a sockaddr's
+ * XXX sa_data area.
+ */
+struct ieee80211_bpf_params {
+	uint8_t		ibp_vers;	/* version */
+#define	IEEE80211_BPF_VERSION	0
+	uint8_t		ibp_len;	/* header length in bytes */
+	uint8_t		ibp_flags;
+#define	IEEE80211_BPF_SHORTPRE	0x01	/* tx with short preamble */
+#define	IEEE80211_BPF_NOACK	0x02	/* tx with no ack */
+#define	IEEE80211_BPF_CRYPTO	0x04	/* tx with h/w encryption */
+#define	IEEE80211_BPF_FCS	0x10	/* frame incldues FCS */
+#define	IEEE80211_BPF_DATAPAD	0x20	/* frame includes data padding */
+#define	IEEE80211_BPF_RTS	0x40	/* tx with RTS/CTS */
+#define	IEEE80211_BPF_CTS	0x80	/* tx with CTS only */
+	uint8_t		ibp_pri;	/* WME/WMM AC+tx antenna */
+	uint8_t		ibp_try0;	/* series 1 try count */
+	uint8_t		ibp_rate0;	/* series 1 IEEE tx rate */
+	uint8_t		ibp_power;	/* tx power (device units) */
+	uint8_t		ibp_ctsrate;	/* IEEE tx rate for CTS */
+	uint8_t		ibp_try1;	/* series 2 try count */
+	uint8_t		ibp_rate1;	/* series 2 IEEE tx rate */
+	uint8_t		ibp_try2;	/* series 3 try count */
+	uint8_t		ibp_rate2;	/* series 3 IEEE tx rate */
+	uint8_t		ibp_try3;	/* series 4 try count */
+	uint8_t		ibp_rate3;	/* series 4 IEEE tx rate */
+};
 #endif /* _NET80211_IEEE80211_FREEBSD_H_ */
Index: ieee80211_input.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_input.c -L sys/net80211/ieee80211_input.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_input.c
+++ sys/net80211/ieee80211_input.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.62.2.12 2006/03/14 23:24:02 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.113.2.1.2.1 2008/02/05 18:29:03 sam Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -73,48 +67,17 @@
 	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 *,
+static const uint8_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 *, int);
 static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int);
 static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
-		const u_int8_t *mac, int subtype, int arg);
-static void ieee80211_deliver_data(struct ieee80211com *,
+		const uint8_t *mac, int subtype, int arg);
+static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *,
 	struct ieee80211_node *, struct mbuf *);
-static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
 static void ieee80211_recv_pspoll(struct ieee80211com *,
 	struct ieee80211_node *, struct mbuf *);
 
@@ -130,7 +93,7 @@
  */
 int
 ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
-	struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+	struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp)
 {
 #define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
 #define	HAS_SEQ(type)	((type & 0x4) == 0)
@@ -138,19 +101,33 @@
 	struct ieee80211_frame *wh;
 	struct ieee80211_key *key;
 	struct ether_header *eh;
-	int hdrspace;
-	u_int8_t dir, type, subtype;
-	u_int8_t *bssid;
-	u_int16_t rxseq;
+	int hdrspace, need_tap;
+	uint8_t dir, type, subtype, qos;
+	uint8_t *bssid;
+	uint16_t rxseq;
+
+	if (m->m_flags & M_AMPDU) {
+		/*
+		 * Fastpath for A-MPDU reorder q resubmission.  Frames
+		 * w/ M_AMPDU marked have already passed through here
+		 * but were received out of order and been held on the
+		 * reorder queue.  When resubmitted they are marked
+		 * with the M_AMPDU flag and we can bypass most of the
+		 * normal processing.
+		 */
+		wh = mtod(m, struct ieee80211_frame *);
+		type = IEEE80211_FC0_TYPE_DATA;
+		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+		subtype = IEEE80211_FC0_SUBTYPE_QOS;
+		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
+		need_tap = 0;
+		goto resubmit_ampdu;
+	}
 
 	KASSERT(ni != NULL, ("null node"));
 	ni->ni_inact = ni->ni_inact_reload;
 
-	/* 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);
-		m->m_flags &= ~M_HASFCS;
-	}
+	need_tap = 1;			/* mbuf need to be tapped. */
 	type = -1;			/* undefined */
 	/*
 	 * In monitor mode, send everything directly to bpf.
@@ -251,9 +228,10 @@
 			goto out;
 		}
 		ni->ni_rssi = rssi;
+		ni->ni_noise = noise;
 		ni->ni_rstamp = rstamp;
 		if (HAS_SEQ(type)) {
-			u_int8_t tid;
+			uint8_t tid;
 			if (IEEE80211_QOS_HAS_SEQ(wh)) {
 				tid = ((struct ieee80211_qosframe *)wh)->
 					i_qos[0] & IEEE80211_QOS_TID;
@@ -261,9 +239,10 @@
 					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) &&
+				tid = IEEE80211_NONQOS_TID;
+			rxseq = le16toh(*(uint16_t *)wh->i_seq);
+			if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+			    (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
 			    SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
 				/* duplicate, discard */
 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
@@ -299,7 +278,7 @@
 		case IEEE80211_M_STA:
 			if (dir != IEEE80211_FC1_DIR_FROMDS) {
 				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
-				    wh, "data", "%s", "unknown dir 0x%x", dir);
+				    wh, "data", "unknown dir 0x%x", dir);
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
@@ -322,7 +301,7 @@
 		case IEEE80211_M_AHDEMO:
 			if (dir != IEEE80211_FC1_DIR_NODS) {
 				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
-				    wh, "data", "%s", "unknown dir 0x%x", dir);
+				    wh, "data", "unknown dir 0x%x", dir);
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
@@ -331,7 +310,7 @@
 		case IEEE80211_M_HOSTAP:
 			if (dir != IEEE80211_FC1_DIR_TODS) {
 				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
-				    wh, "data", "%s", "unknown dir 0x%x", dir);
+				    wh, "data", "unknown dir 0x%x", dir);
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
@@ -357,6 +336,7 @@
 
 			/*
 			 * Check for power save state change.
+			 * XXX out-of-order A-MPDU frames?
 			 */
 			if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
 			    (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
@@ -369,6 +349,23 @@
 		}
 
 		/*
+		 * Handle A-MPDU re-ordering.  The station must be
+		 * associated and negotiated HT.  The frame must be
+		 * a QoS frame (not QoS null data) and not previously
+		 * processed for A-MPDU re-ordering.  If the frame is
+		 * to be processed directly then ieee80211_ampdu_reorder
+		 * will return 0; otherwise it has consumed the mbuf
+		 * and we should do nothing more with it.
+		 */
+		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+		    subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+		    ieee80211_ampdu_reorder(ni, m) != 0) {
+			m = NULL;
+			goto out;
+		}
+	resubmit_ampdu:
+
+		/*
 		 * Handle privacy requirements.  Note that we
 		 * must not be preempted from here until after
 		 * we (potentially) call ieee80211_crypto_demic;
@@ -396,10 +393,21 @@
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
 		} else {
+			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
 		}
 
 		/*
+		 * Save QoS bits for use below--before we strip the header.
+		 */
+		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
+		} else
+			qos = 0;
+
+		/*
 		 * Next up, any fragmentation.
 		 */
 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
@@ -417,13 +425,15 @@
 		if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
 			    ni->ni_macaddr, "data", "%s", "demic error");
+			ic->ic_stats.is_rx_demicfail++;
 			IEEE80211_NODE_STAT(ni, rx_demicfail);
 			goto out;
 		}
 
 		/* copy to listener after decrypt */
-		if (ic->ic_rawbpf)
+		if (bpf_peers_present(ic->ic_rawbpf))
 			bpf_mtap(ic->ic_rawbpf, m);
+		need_tap = 0;
 
 		/*
 		 * Finally, strip the 802.11 header.
@@ -431,7 +441,8 @@
 		m = ieee80211_decap(ic, m, hdrspace);
 		if (m == NULL) {
 			/* don't count Null data frames as errors */
-			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
+			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
 				goto out;
 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
 			    ni->ni_macaddr, "data", "%s", "decap error");
@@ -464,7 +475,7 @@
 			 * any non-PAE frames received without encryption.
 			 */
 			if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
-			    key == NULL &&
+			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
 			    eh->ether_type != htons(ETHERTYPE_PAE)) {
 				/*
 				 * Drop unencrypted frames.
@@ -474,18 +485,46 @@
 				goto out;
 			}
 		}
-		ifp->if_ipackets++;
-		IEEE80211_NODE_STAT(ni, rx_data);
-		IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+		/* XXX require HT? */
+		if (qos & IEEE80211_QOS_AMSDU) {
+			m = ieee80211_decap_amsdu(ni, m);
+			if (m == NULL)
+				return IEEE80211_FC0_TYPE_DATA;
+		} else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define	FF_LLC_SIZE	(sizeof(struct ether_header) + sizeof(struct llc))
+		    m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+			struct llc *llc;
 
+			/*
+			 * Check for fast-frame tunnel encapsulation.
+			 */
+			if (m->m_len < FF_LLC_SIZE &&
+			    (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+				    ni->ni_macaddr, "fast-frame",
+				    "%s", "m_pullup(llc) failed");
+				ic->ic_stats.is_rx_tooshort++;
+				return IEEE80211_FC0_TYPE_DATA;
+			}
+			llc = (struct llc *)(mtod(m, uint8_t *) + 
+				sizeof(struct ether_header));
+			if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+				m_adj(m, FF_LLC_SIZE);
+				m = ieee80211_decap_fastframe(ic, ni, m);
+				if (m == NULL)
+					return IEEE80211_FC0_TYPE_DATA;
+			}
+		}
+#undef FF_LLC_SIZE
 		ieee80211_deliver_data(ic, ni, m);
 		return IEEE80211_FC0_TYPE_DATA;
 
 	case IEEE80211_FC0_TYPE_MGT:
+		ic->ic_stats.is_rx_mgmt++;
 		IEEE80211_NODE_STAT(ni, rx_mgmt);
 		if (dir != IEEE80211_FC1_DIR_NODS) {
 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
-			    wh, "data", "%s", "unknown dir 0x%x", dir);
+			    wh, "data", "unknown dir 0x%x", dir);
 			ic->ic_stats.is_rx_wrongdir++;
 			goto err;
 		}
@@ -536,20 +575,23 @@
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
 		}
-		if (ic->ic_rawbpf)
+		if (bpf_peers_present(ic->ic_rawbpf))
 			bpf_mtap(ic->ic_rawbpf, m);
-		(*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
+		(*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp);
 		m_freem(m);
-		return type;
+		return IEEE80211_FC0_TYPE_MGT;
 
 	case IEEE80211_FC0_TYPE_CTL:
-		IEEE80211_NODE_STAT(ni, rx_ctrl);
 		ic->ic_stats.is_rx_ctl++;
+		IEEE80211_NODE_STAT(ni, rx_ctrl);
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
 			switch (subtype) {
 			case IEEE80211_FC0_SUBTYPE_PS_POLL:
 				ieee80211_recv_pspoll(ic, ni, m);
 				break;
+			case IEEE80211_FC0_SUBTYPE_BAR:
+				ieee80211_recv_bar(ni, m);
+				break;
 			}
 		}
 		goto out;
@@ -563,7 +605,7 @@
 	ifp->if_ierrors++;
 out:
 	if (m != NULL) {
-		if (ic->ic_rawbpf)
+		if (bpf_peers_present(ic->ic_rawbpf) && need_tap)
 			bpf_mtap(ic->ic_rawbpf, m);
 		m_freem(m);
 	}
@@ -580,14 +622,14 @@
 {
 	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;
+	uint16_t rxseq;
+	uint8_t fragno;
+	uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
 	struct mbuf *mfrag;
 
 	KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
 
-	rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+	rxseq = le16toh(*(uint16_t *)wh->i_seq);
 	fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
 
 	/* Quick way out, if there's nothing to defragment */
@@ -620,10 +662,10 @@
 	 * related to the previous ones.
 	 */
 	if (mfrag != NULL) {
-		u_int16_t last_rxseq;
+		uint16_t last_rxseq;
 
 		lwh = mtod(mfrag, struct ieee80211_frame *);
-		last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq);
+		last_rxseq = le16toh(*(uint16_t *)lwh->i_seq);
 		/* NB: check seq # and frag together */
 		if (rxseq != last_rxseq+1 ||
 		    !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
@@ -639,6 +681,7 @@
 
  	if (mfrag == NULL) {
 		if (fragno != 0) {		/* !first fragment, discard */
+			ic->ic_stats.is_rx_defrag++;
 			IEEE80211_NODE_STAT(ni, rx_defrag);
 			m_freem(m);
 			return NULL;
@@ -651,7 +694,7 @@
 		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;
+		*(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq;
 	}
 	if (more_frag) {			/* more to come, save */
 		ni->ni_rxfragstamp = ticks;
@@ -661,19 +704,34 @@
 	return mfrag;
 }
 
-static void
+void
 ieee80211_deliver_data(struct ieee80211com *ic,
 	struct ieee80211_node *ni, struct mbuf *m)
 {
 	struct ether_header *eh = mtod(m, struct ether_header *);
 	struct ifnet *ifp = ic->ic_ifp;
 
+	/*
+	 * Do accounting.
+	 */
+	ifp->if_ipackets++;
+	IEEE80211_NODE_STAT(ni, rx_data);
+	IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+	if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+		m->m_flags |= M_MCAST;		/* XXX M_BCAST? */
+		IEEE80211_NODE_STAT(ni, rx_mcast);
+	} else
+		IEEE80211_NODE_STAT(ni, rx_ucast);
+
+	/* clear driver/net80211 flags before passing up */
+	m->m_flags &= ~M_80211_RX;
+
 	/* perform as a bridge within the AP */
 	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)) {
+		if (m->m_flags & M_MCAST) {
 			m1 = m_dup(m, M_DONTWAIT);
 			if (m1 == NULL)
 				ifp->if_oerrors++;
@@ -704,25 +762,22 @@
 				ieee80211_free_node(sta);
 			}
 		}
-		if (m1 != NULL)
-			IF_HANDOFF(&ifp->if_snd, m1, ifp);
+		if (m1 != NULL) {
+			int error;
+
+			/* XXX does not work well with WME */
+			IFQ_HANDOFF(ifp, m1, error);
+		}
 	}
 	if (m != NULL) {
+		m->m_pkthdr.rcvif = ifp;
 		if (ni->ni_vlan != 0) {
 			/* attach vlan tag */
-			VLAN_INPUT_TAG_NEW(ifp, m, ni->ni_vlan);
-			if (m == NULL)
-				goto out;	/* XXX goto err? */
+			m->m_pkthdr.ether_vtag = ni->ni_vlan;
+			m->m_flags |= M_VLANTAG;
 		}
 		(*ifp->if_input)(ifp, m);
 	}
-	return;
-  out:
-	if (m != NULL) {
-		if (ic->ic_rawbpf)
-			bpf_mtap(ic->ic_rawbpf, m);
-		m_freem(m);
-	}
 }
 
 static struct mbuf *
@@ -767,7 +822,7 @@
 		break;
 	}
 #ifdef ALIGNED_POINTER
-	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) {
+	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
 		struct mbuf *n, *n0, **np;
 		caddr_t newdata;
 		int off, pktlen;
@@ -825,11 +880,109 @@
 }
 
 /*
+ * Decap a frame encapsulated in a fast-frame/A-MSDU.
+ */
+struct mbuf *
+ieee80211_decap1(struct mbuf *m, int *framelen)
+{
+#define	FF_LLC_SIZE	(sizeof(struct ether_header) + sizeof(struct llc))
+	struct ether_header *eh;
+	struct llc *llc;
+
+	/*
+	 * The frame has an 802.3 header followed by an 802.2
+	 * LLC header.  The encapsulated frame length is in the
+	 * first header type field; save that and overwrite it 
+	 * with the true type field found in the second.  Then
+	 * copy the 802.3 header up to where it belongs and
+	 * adjust the mbuf contents to remove the void.
+	 */
+	if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL)
+		return NULL;
+	eh = mtod(m, struct ether_header *);	/* 802.3 header is first */
+	llc = (struct llc *)&eh[1];		/* 802.2 header follows */
+	*framelen = ntohs(eh->ether_type)	/* encap'd frame size */
+		  + sizeof(struct ether_header) - sizeof(struct llc);
+	eh->ether_type = llc->llc_un.type_snap.ether_type;
+	ovbcopy(eh, mtod(m, uint8_t *) + sizeof(struct llc),
+		sizeof(struct ether_header));
+	m_adj(m, sizeof(struct llc));
+	return m;
+#undef FF_LLC_SIZE
+}
+
+/*
+ * Decap the encapsulated frame pair and dispatch the first
+ * for delivery.  The second frame is returned for delivery
+ * via the normal path.
+ */
+static struct mbuf *
+ieee80211_decap_fastframe(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m)
+{
+#define	MS(x,f)	(((x) & f) >> f##_S)
+	uint32_t ath;
+	struct mbuf *n;
+	int framelen;
+
+	m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath);
+	if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, "fast-frame",
+		    "unsupport tunnel protocol, header 0x%x", ath);
+		ic->ic_stats.is_ff_badhdr++;
+		m_freem(m);
+		return NULL;
+	}
+	/* NB: skip header and alignment padding */
+	m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2);
+
+	ic->ic_stats.is_ff_decap++;
+
+	/*
+	 * Decap the first frame, bust it apart from the
+	 * second and deliver; then decap the second frame
+	 * and return it to the caller for normal delivery.
+	 */
+	m = ieee80211_decap1(m, &framelen);
+	if (m == NULL) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, "fast-frame", "%s", "first decap failed");
+		ic->ic_stats.is_ff_tooshort++;
+		return NULL;
+	}
+	n = m_split(m, framelen, M_NOWAIT);
+	if (n == NULL) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, "fast-frame",
+		    "%s", "unable to split encapsulated frames");
+		ic->ic_stats.is_ff_split++;
+		m_freem(m);			/* NB: must reclaim */
+		return NULL;
+	}
+	ieee80211_deliver_data(ic, ni, m);	/* 1st of pair */
+
+	/*
+	 * Decap second frame.
+	 */
+	m_adj(n, roundup2(framelen, 4) - framelen);	/* padding */
+	n = ieee80211_decap1(n, &framelen);
+	if (n == NULL) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, "fast-frame", "%s", "second decap failed");
+		ic->ic_stats.is_ff_tooshort++;
+	}
+	/* XXX verify framelen against mbuf contents */
+	return n;				/* 2nd delivered by caller */
+#undef MS
+}
+
+/*
  * Install received rate set information in the node's state block.
  */
 int
 ieee80211_setup_rates(struct ieee80211_node *ni,
-	const u_int8_t *rates, const u_int8_t *xrates, int flags)
+	const uint8_t *rates, const uint8_t *xrates, int flags)
 {
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_rateset *rs = &ni->ni_rates;
@@ -838,7 +991,7 @@
 	rs->rs_nrates = rates[1];
 	memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
 	if (xrates != NULL) {
-		u_int8_t nxrates;
+		uint8_t nxrates;
 		/*
 		 * Tack on 11g extended supported rate element.
 		 */
@@ -854,13 +1007,13 @@
 		memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
 		rs->rs_nrates += nxrates;
 	}
-	return ieee80211_fix_rate(ni, flags);
+	return ieee80211_fix_rate(ni, rs, 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)
+    struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp,
+    uint16_t seq, uint16_t status)
 {
 
 	if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
@@ -869,6 +1022,15 @@
 		    "bad sta auth mode %u", ni->ni_authmode);
 		ic->ic_stats.is_rx_bad_auth++;	/* XXX */
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			/*
+			 * Clear any challenge text that may be there if
+			 * a previous shared key auth failed and then an
+			 * open auth is attempted.
+			 */
+			if (ni->ni_challenge != NULL) {
+				FREE(ni->ni_challenge, M_80211_NODE);
+				ni->ni_challenge = NULL;
+			}
 			/* XXX hack to workaround calling convention */
 			ieee80211_send_error(ic, ni, wh->i_addr2, 
 			    IEEE80211_FC0_SUBTYPE_AUTH,
@@ -880,6 +1042,7 @@
 	case IEEE80211_M_IBSS:
 	case IEEE80211_M_AHDEMO:
 	case IEEE80211_M_MONITOR:
+	case IEEE80211_M_WDS:
 		/* should not come here */
 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
 		    ni->ni_macaddr, "open auth",
@@ -934,10 +1097,10 @@
 			if (ni != ic->ic_bss)
 				ni->ni_fails++;
 			ic->ic_stats.is_rx_auth_fail++;
-			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+			ieee80211_new_state(ic, IEEE80211_S_SCAN,
+			    IEEE80211_SCAN_FAIL_STATUS);
 		} else
-			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
 		break;
 	}
 }
@@ -951,7 +1114,7 @@
  */
 static void
 ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
-	const u_int8_t *mac, int subtype, int arg)
+	const uint8_t *mac, int subtype, int arg)
 {
 	int istmp;
 
@@ -973,8 +1136,8 @@
 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);
+		MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN,
+		    M_80211_NODE, M_NOWAIT);
 	if (ni->ni_challenge == NULL) {
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
 		    "[%s] shared key challenge alloc failed\n",
@@ -987,10 +1150,10 @@
 /* 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)
+    uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni,
+    int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
 {
-	u_int8_t *challenge;
+	uint8_t *challenge;
 	int allocbs, estatus;
 
 	/*
@@ -1064,6 +1227,7 @@
 	case IEEE80211_M_MONITOR:
 	case IEEE80211_M_AHDEMO:
 	case IEEE80211_M_IBSS:
+	case IEEE80211_M_WDS:
 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
 		    ni->ni_macaddr, "shared key auth",
 		    "bad operating mode %u", ic->ic_opmode);
@@ -1097,6 +1261,7 @@
 			 */
 			ni->ni_flags |= IEEE80211_NODE_AREF;
 			ni->ni_rssi = rssi;
+			ni->ni_noise = noise;
 			ni->ni_rstamp = rstamp;
 			if (!alloc_challenge(ic, ni)) {
 				/* NB: don't return error so they rexmit */
@@ -1159,7 +1324,7 @@
 		switch (seq) {
 		case IEEE80211_AUTH_SHARED_PASS:
 			if (ni->ni_challenge != NULL) {
-				FREE(ni->ni_challenge, M_DEVBUF);
+				FREE(ni->ni_challenge, M_80211_NODE);
 				ni->ni_challenge = NULL;
 			}
 			if (status != 0) {
@@ -1174,8 +1339,7 @@
 				ic->ic_stats.is_rx_auth_fail++;
 				return;
 			}
-			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
 			break;
 		case IEEE80211_AUTH_SHARED_CHALLENGE:
 			if (!alloc_challenge(ic, ni))
@@ -1210,7 +1374,8 @@
 		 * state transition.
 		 */
 		if (ic->ic_state == IEEE80211_S_AUTH)
-			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+			ieee80211_new_state(ic, IEEE80211_S_SCAN,
+			    IEEE80211_SCAN_FAIL_STATUS);
 	}
 }
 
@@ -1234,21 +1399,22 @@
 	}								\
 } while (0)
 
-#define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
+#define	IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do {		\
 	if ((_len) < (_minlen)) {					\
 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
 		    wh, ieee80211_mgt_subtype_name[subtype >>		\
 			IEEE80211_FC0_SUBTYPE_SHIFT],			\
-		    "%s", "ie too short");				\
+		    "ie too short, got %d, expected %d",		\
+		    (_len), (_minlen));					\
 		ic->ic_stats.is_rx_elem_toosmall++;			\
-		return;							\
+		_action;						\
 	}								\
 } 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)
+	uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
 {
 	printf("[%s] discard %s frame, ssid mismatch: ",
 		ether_sprintf(mac), tag);
@@ -1282,58 +1448,70 @@
 
 /* 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)))
+	((uint16_t)					\
+	 ((((const uint8_t *)(p))[0]      ) |		\
+	  (((const uint8_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)))
+	((uint32_t)					\
+	 ((((const uint8_t *)(p))[0]      ) |		\
+	  (((const uint8_t *)(p))[1] <<  8) |		\
+	  (((const uint8_t *)(p))[2] << 16) |		\
+	  (((const uint8_t *)(p))[3] << 24)))
 
-static int __inline
-iswpaoui(const u_int8_t *frm)
+static __inline int
+iswpaoui(const uint8_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)
+static __inline int
+iswmeoui(const uint8_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)
+static __inline int
+iswmeparam(const uint8_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)
+static __inline int
+iswmeinfo(const uint8_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)
+static __inline int
+isatherosoui(const uint8_t *frm)
 {
 	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
 }
 
+static __inline int
+ishtcapoui(const uint8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
+}
+
+static __inline int
+ishtinfooui(const uint8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_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)
+wpa_cipher(uint8_t *sel, uint8_t *keylen)
 {
 #define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
-	u_int32_t w = LE_READ_4(sel);
+	uint32_t w = LE_READ_4(sel);
 
 	switch (w) {
 	case WPA_SEL(WPA_CSE_NULL):
@@ -1360,10 +1538,10 @@
  * to an internal code.
  */
 static int
-wpa_keymgmt(u_int8_t *sel)
+wpa_keymgmt(uint8_t *sel)
 {
 #define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
-	u_int32_t w = LE_READ_4(sel);
+	uint32_t w = LE_READ_4(sel);
 
 	switch (w) {
 	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
@@ -1383,11 +1561,11 @@
  * configured for the system.
  */
 static int
-ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm,
+ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm,
 	struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
 {
-	u_int8_t len = frm[1];
-	u_int32_t w;
+	uint8_t len = frm[1];
+	uint32_t w;
 	int n;
 
 	/*
@@ -1495,10 +1673,10 @@
  * record any key length.
  */
 static int
-rsn_cipher(u_int8_t *sel, u_int8_t *keylen)
+rsn_cipher(uint8_t *sel, uint8_t *keylen)
 {
 #define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
-	u_int32_t w = LE_READ_4(sel);
+	uint32_t w = LE_READ_4(sel);
 
 	switch (w) {
 	case RSN_SEL(RSN_CSE_NULL):
@@ -1527,10 +1705,10 @@
  * to an internal code.
  */
 static int
-rsn_keymgmt(u_int8_t *sel)
+rsn_keymgmt(uint8_t *sel)
 {
 #define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
-	u_int32_t w = LE_READ_4(sel);
+	uint32_t w = LE_READ_4(sel);
 
 	switch (w) {
 	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
@@ -1550,11 +1728,11 @@
  * configured for the system.
  */
 static int
-ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm,
+ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm,
 	struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
 {
-	u_int8_t len = frm[1];
-	u_int32_t w;
+	uint8_t len = frm[1];
+	uint32_t w;
 	int n;
 
 	/*
@@ -1658,7 +1836,7 @@
 }
 
 static int
-ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm,
+ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm,
 	const struct ieee80211_frame *wh)
 {
 #define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
@@ -1694,8 +1872,65 @@
 #undef MS
 }
 
+static int
+ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
+	const struct ieee80211_frame *wh)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	const struct ieee80211_ath_ie *ath;
+	u_int len = frm[1];
+	int capschanged;
+	uint16_t defkeyix;
+
+	if (len < sizeof(struct ieee80211_ath_ie)-2) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
+		    wh, "Atheros", "too short, len %u", len);
+		return -1;
+	}
+	ath = (const struct ieee80211_ath_ie *)frm;
+	capschanged = (ni->ni_ath_flags != ath->ath_capability);
+	defkeyix = LE_READ_2(ath->ath_defkeyix);
+	if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
+		ni->ni_ath_flags = ath->ath_capability;
+		ni->ni_ath_defkeyix = defkeyix;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+		    "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n",
+		    ether_sprintf(ni->ni_macaddr),
+		    ni->ni_ath_flags, ni->ni_ath_defkeyix);
+	}
+	if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) {
+		uint16_t curflags, newflags;
+
+		/*
+		 * Check for turbo mode switch.  Calculate flags
+		 * for the new mode and effect the switch.
+		 */
+		newflags = curflags = ic->ic_bsschan->ic_flags;
+		/* NB: BOOST is not in ic_flags, so get it from the ie */
+		if (ath->ath_capability & ATHEROS_CAP_BOOST) 
+			newflags |= IEEE80211_CHAN_TURBO;
+		else
+			newflags &= ~IEEE80211_CHAN_TURBO;
+		if (newflags != curflags)
+			ieee80211_dturbo_switch(ic, newflags);
+	}
+	return capschanged;
+}
+
+void
+ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie)
+{
+	const struct ieee80211_ath_ie *ath =
+		(const struct ieee80211_ath_ie *) ie;
+
+	ni->ni_ath_flags = ath->ath_capability;
+	ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix);
+	ieee80211_saveie(&ni->ni_ath_ie, ie);
+}
+
 void
-ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie)
+ieee80211_saveie(uint8_t **iep, const uint8_t *ie)
 {
 	u_int ielen = ie[1]+2;
 	/*
@@ -1703,8 +1938,8 @@
 	 */
 	if (*iep == NULL || (*iep)[1] != ie[1]) {
 		if (*iep != NULL)
-			FREE(*iep, M_DEVBUF);
-		MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT);
+			FREE(*iep, M_80211_NODE);
+		MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT);
 	}
 	if (*iep != NULL)
 		memcpy(*iep, ie, ielen);
@@ -1714,10 +1949,10 @@
 /* XXX find a better place for definition */
 struct l2_update_frame {
 	struct ether_header eh;
-	u_int8_t dsap;
-	u_int8_t ssap;
-	u_int8_t control;
-	u_int8_t xid[3];
+	uint8_t dsap;
+	uint8_t ssap;
+	uint8_t control;
+	uint8_t xid[3];
 }  __packed;
 
 /*
@@ -1757,26 +1992,84 @@
 	l2uf->xid[2] = 0x00;
 	
 	m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
-	m->m_pkthdr.rcvif = ifp;
 	ieee80211_deliver_data(ic, ni, m);
 }
 
+static __inline int
+contbgscan(struct ieee80211com *ic)
+{
+	return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
+	    time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
+}
+
+static __inline int
+startbgscan(struct ieee80211com *ic)
+{
+	return ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
+	    !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
+	    time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) &&
+	    time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
+}
+
+static void
+ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+	int reassoc, int resp, const char *tag, int rate)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+	    "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n",
+	    ether_sprintf(wh->i_addr2),
+	    reassoc ? "reassoc" : "assoc", tag, rate);
+	IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
+	ieee80211_node_leave(ic, ni);
+	ic->ic_stats.is_rx_assoc_norate++;
+}
+
+static void
+capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+	int reassoc, int resp, const char *tag, int capinfo)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+	    "[%s] deny %s request, %s mismatch 0x%x\n",
+	    ether_sprintf(wh->i_addr2),
+	    reassoc ? "reassoc" : "assoc", tag, capinfo);
+	IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
+	ieee80211_node_leave(ic, ni);
+	ic->ic_stats.is_rx_assoc_capmismatch++;
+}
+
+static void
+htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+	int reassoc, int resp)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2,
+	    "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
+	/* XXX no better code */
+	IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER);
+	ieee80211_node_leave(ic, ni);
+}
+
 void
 ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
 	struct ieee80211_node *ni,
-	int subtype, int rssi, u_int32_t rstamp)
+	int subtype, int rssi, int noise, uint32_t rstamp)
 {
 #define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
 #define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
 	struct ieee80211_frame *wh;
-	u_int8_t *frm, *efrm;
-	u_int8_t *ssid, *rates, *xrates, *wpa, *wme;
+	uint8_t *frm, *efrm;
+	uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap, *htinfo;
 	int reassoc, resp, allocbs;
-	u_int8_t rate;
+	uint8_t rate;
 
 	wh = mtod(m0, struct ieee80211_frame *);
-	frm = (u_int8_t *)&wh[1];
-	efrm = mtod(m0, u_int8_t *) + m0->m_len;
+	frm = (uint8_t *)&wh[1];
+	efrm = mtod(m0, uint8_t *) + m0->m_len;
 	switch (subtype) {
 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
 	case IEEE80211_FC0_SUBTYPE_BEACON: {
@@ -1809,17 +2102,20 @@
 		 *	[tlv] extended supported rates
 		 *	[tlv] WME
 		 *	[tlv] WPA or RSN
+		 *	[tlv] HT capabilities
+		 *	[tlv] HT information
+		 *	[tlv] Atheros capabilities
 		 */
-		IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return);
 		memset(&scan, 0, sizeof(scan));
 		scan.tstamp  = frm;				frm += 8;
-		scan.bintval = le16toh(*(u_int16_t *)frm);	frm += 2;
-		scan.capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
-		scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
-		scan.chan = scan.bchan;
+		scan.bintval = le16toh(*(uint16_t *)frm);	frm += 2;
+		scan.capinfo = le16toh(*(uint16_t *)frm);	frm += 2;
+		scan.bchan = IEEE80211_CHAN2IEEE(ic->ic_curchan);
+		scan.curchan = ic->ic_curchan;
 
-		while (frm < efrm) {
-			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+		while (efrm - frm > 1) {
+			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
 			switch (*frm) {
 			case IEEE80211_ELEMID_SSID:
 				scan.ssid = frm;
@@ -1833,7 +2129,7 @@
 			case IEEE80211_ELEMID_FHPARMS:
 				if (ic->ic_phytype == IEEE80211_T_FH) {
 					scan.fhdwell = LE_READ_2(&frm[2]);
-					scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+					scan.bchan = IEEE80211_FH_CHAN(frm[4], frm[5]);
 					scan.fhindex = frm[6];
 				}
 				break;
@@ -1843,12 +2139,12 @@
 				 * is problematic for multi-mode devices.
 				 */
 				if (ic->ic_phytype != IEEE80211_T_FH)
-					scan.chan = frm[2];
+					scan.bchan = frm[2];
 				break;
 			case IEEE80211_ELEMID_TIM:
 				/* XXX ATIM? */
 				scan.tim = frm;
-				scan.timoff = frm - mtod(m0, u_int8_t *);
+				scan.timoff = frm - mtod(m0, uint8_t *);
 				break;
 			case IEEE80211_ELEMID_IBSSPARMS:
 				break;
@@ -1865,15 +2161,35 @@
 				}
 				scan.erp = frm[2];
 				break;
+			case IEEE80211_ELEMID_HTCAP:
+				scan.htcap = frm;
+				break;
 			case IEEE80211_ELEMID_RSN:
-				scan.wpa = frm;
+				scan.rsn = frm;
+				break;
+			case IEEE80211_ELEMID_HTINFO:
+				scan.htinfo = frm;
 				break;
 			case IEEE80211_ELEMID_VENDOR:
 				if (iswpaoui(frm))
 					scan.wpa = frm;
 				else if (iswmeparam(frm) || iswmeinfo(frm))
 					scan.wme = frm;
-				/* XXX Atheros OUI support */
+				else if (isatherosoui(frm))
+					scan.ath = frm;
+				else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+					/*
+					 * Accept pre-draft HT ie's if the
+					 * standard ones have not been seen.
+					 */
+					if (ishtcapoui(frm)) {
+						if (scan.htcap == NULL)
+							scan.htcap = frm;
+					} else if (ishtinfooui(frm)) {
+						if (scan.htinfo == NULL)
+							scan.htcap = frm;
+					}
+				}
 				break;
 			default:
 				IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
@@ -1885,21 +2201,21 @@
 			frm += frm[1] + 2;
 		}
 		IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE);
+		if (scan.xrates != NULL)
+			IEEE80211_VERIFY_ELEMENT(scan.xrates,
+				IEEE80211_RATE_MAXSIZE - scan.rates[1]);
 		IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN);
-		if (
 #if IEEE80211_CHAN_MAX < 255
-		    scan.chan > IEEE80211_CHAN_MAX ||
-#endif
-		    isclr(ic->ic_chan_active, scan.chan)) {
-			IEEE80211_DISCARD(ic,
-			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+		if (scan.chan > IEEE80211_CHAN_MAX) {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
 			    wh, ieee80211_mgt_subtype_name[subtype >>
 				IEEE80211_FC0_SUBTYPE_SHIFT],
 			    "invalid channel %u", scan.chan);
 			ic->ic_stats.is_rx_badchan++;
 			return;
 		}
-		if (scan.chan != scan.bchan &&
+#endif
+		if (IEEE80211_CHAN2IEEE(scan.curchan) != scan.bchan &&
 		    ic->ic_phytype != IEEE80211_T_FH) {
 			/*
 			 * Frame was received on a channel different from the
@@ -1915,7 +2231,8 @@
 			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
 			    wh, ieee80211_mgt_subtype_name[subtype >>
 				IEEE80211_FC0_SUBTYPE_SHIFT],
-			    "for off-channel %u", scan.chan);
+			    "for off-channel %u",
+			    IEEE80211_CHAN2IEEE(scan.curchan));
 			ic->ic_stats.is_rx_chanmismatch++;
 			return;
 		}
@@ -1929,6 +2246,25 @@
 			ic->ic_stats.is_rx_badbintval++;
 			return;
 		}
+		/*
+		 * Process HT ie's.  This is complicated by our
+		 * accepting both the standard ie's and the pre-draft
+		 * vendor OUI ie's that some vendors still use/require.
+		 */
+		if (scan.htcap != NULL) {
+			IEEE80211_VERIFY_LENGTH(scan.htcap[1],
+			     scan.htcap[0] == IEEE80211_ELEMID_VENDOR ?
+			         4 + sizeof(struct ieee80211_ie_htcap)-2 :
+			         sizeof(struct ieee80211_ie_htcap)-2,
+			     scan.htcap = NULL);
+		}
+		if (scan.htinfo != NULL) {
+			IEEE80211_VERIFY_LENGTH(scan.htinfo[1],
+			     scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ?
+			         4 + sizeof(struct ieee80211_ie_htinfo)-2 :
+			         sizeof(struct ieee80211_ie_htinfo)-2,
+			     scan.htinfo = NULL);
+		}
 
 		/*
 		 * Count frame now that we know it's to be processed.
@@ -1959,7 +2295,7 @@
 				    "[%s] erp change: was 0x%x, now 0x%x\n",
 				    ether_sprintf(wh->i_addr2),
 				    ni->ni_erp, scan.erp);
-				if (ic->ic_curmode == IEEE80211_MODE_11G &&
+				if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
 				    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
 					ic->ic_flags |= IEEE80211_F_USEPROT;
 				else
@@ -1978,25 +2314,79 @@
 				 *     change dynamically
 				 */
 				ieee80211_set_shortslottime(ic,
-					ic->ic_curmode == IEEE80211_MODE_11A ||
+					IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
 					(scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
-				ni->ni_capinfo = scan.capinfo;
+				ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
+					       | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
 				/* XXX statistic */
 			}
 			if (scan.wme != NULL &&
 			    (ni->ni_flags & IEEE80211_NODE_QOS) &&
 			    ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0)
 				ieee80211_wme_updateparams(ic);
+			if (scan.ath != NULL)
+				ieee80211_parse_athparams(ni, scan.ath, wh);
+			if (scan.htcap != NULL)
+				ieee80211_parse_htcap(ni, scan.htcap);
+			if (scan.htinfo != NULL) {
+				ieee80211_parse_htinfo(ni, scan.htinfo);
+				if (ni->ni_chan != ic->ic_bsschan) {
+					/*
+					 * Channel has been adjusted based on
+					 * negotiated HT parameters; force the
+					 * channel state to follow.
+					 */
+					ieee80211_setbsschan(ic, ni->ni_chan);
+				}
+			}
 			if (scan.tim != NULL) {
-				struct ieee80211_tim_ie *ie =
+				struct ieee80211_tim_ie *tim =
 				    (struct ieee80211_tim_ie *) scan.tim;
-
-				ni->ni_dtim_count = ie->tim_count;
-				ni->ni_dtim_period = ie->tim_period;
+#if 0
+				int aid = IEEE80211_AID(ni->ni_associd);
+				int ix = aid / NBBY;
+				int min = tim->tim_bitctl &~ 1;
+				int max = tim->tim_len + min - 4;
+				if ((tim->tim_bitctl&1) ||
+				    (min <= ix && ix <= max &&
+				     isset(tim->tim_bitmap - min, aid))) {
+					/* 
+					 * XXX Do not let bg scan kick off
+					 * we are expecting data.
+					 */
+					ic->ic_lastdata = ticks;
+					ieee80211_sta_pwrsave(ic, 0);
+				}
+#endif
+				ni->ni_dtim_count = tim->tim_count;
+				ni->ni_dtim_period = tim->tim_period;
 			}
-			if (ic->ic_flags & IEEE80211_F_SCAN)
+			/*
+			 * If scanning, pass the info to the scan module.
+			 * Otherwise, check if it's the right time to do
+			 * a background scan.  Background scanning must
+			 * be enabled and we must not be operating in the
+			 * turbo phase of dynamic turbo mode.  Then,
+			 * it's been a while since the last background
+			 * scan and if no data frames have come through
+			 * recently, kick off a scan.  Note that this
+			 * is the mechanism by which a background scan
+			 * is started _and_ continued each time we
+			 * return on-channel to receive a beacon from
+			 * our ap.
+			 */
+			if (ic->ic_flags & IEEE80211_F_SCAN) {
 				ieee80211_add_scan(ic, &scan, wh,
-					subtype, rssi, rstamp);
+					subtype, rssi, noise, rstamp);
+			} else if (contbgscan(ic)) {
+				ieee80211_bg_scan(ic);
+			} else if (startbgscan(ic)) {
+#if 0
+				/* wakeup if we are sleeing */
+				ieee80211_set_pwrsave(ic, 0);
+#endif
+				ieee80211_bg_scan(ic);
+			}
 			return;
 		}
 		/*
@@ -2016,7 +2406,7 @@
 				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
 			}
 			ieee80211_add_scan(ic, &scan, wh,
-				subtype, rssi, rstamp);
+				subtype, rssi, noise, rstamp);
 			return;
 		}
 		if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
@@ -2040,6 +2430,7 @@
 			}
 			if (ni != NULL) {
 				ni->ni_rssi = rssi;
+				ni->ni_noise = noise;
 				ni->ni_rstamp = rstamp;
 			}
 		}
@@ -2063,10 +2454,11 @@
 		 *	[tlv] ssid
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] Atheros capabilities
 		 */
-		ssid = rates = xrates = NULL;
-		while (frm < efrm) {
-			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+		ssid = rates = xrates = ath = NULL;
+		while (efrm - frm > 1) {
+			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
 			switch (*frm) {
 			case IEEE80211_ELEMID_SSID:
 				ssid = frm;
@@ -2077,10 +2469,17 @@
 			case IEEE80211_ELEMID_XRATES:
 				xrates = frm;
 				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (isatherosoui(frm))
+					ath = frm;
+				break;
 			}
 			frm += frm[1] + 2;
 		}
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+		if (xrates != NULL)
+			IEEE80211_VERIFY_ELEMENT(xrates,
+				IEEE80211_RATE_MAXSIZE - rates[1]);
 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
 		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
 		if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
@@ -2132,11 +2531,12 @@
 			 * response, reclaim immediately.
 			 */
 			ieee80211_free_node(ni);
-		}
+		} else if (ath != NULL)
+			ieee80211_saveath(ni, ath);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_AUTH: {
-		u_int16_t algo, seq, status;
+		uint16_t algo, seq, status;
 		/*
 		 * auth frame format
 		 *	[2] algorithm
@@ -2144,10 +2544,10 @@
 		 *	[2] status
 		 *	[tlv*] challenge
 		 */
-		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
-		algo   = le16toh(*(u_int16_t *)frm);
-		seq    = le16toh(*(u_int16_t *)(frm + 2));
-		status = le16toh(*(u_int16_t *)(frm + 4));
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+		algo   = le16toh(*(uint16_t *)frm);
+		seq    = le16toh(*(uint16_t *)(frm + 2));
+		status = le16toh(*(uint16_t *)(frm + 4));
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
 		    "[%s] recv auth frame with algorithm %d seq %d\n",
 		    ether_sprintf(wh->i_addr2), algo, seq);
@@ -2180,10 +2580,10 @@
 		}
 		if (algo == IEEE80211_AUTH_ALG_SHARED)
 			ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
-			    rstamp, seq, status);
+			    noise, rstamp, seq, status);
 		else if (algo == IEEE80211_AUTH_ALG_OPEN)
-			ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq,
-			    status);
+			ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp,
+			    seq, status);
 		else {
 			IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
 			    wh, "auth", "unsupported alg %d", algo);
@@ -2201,9 +2601,10 @@
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
-		u_int16_t capinfo, lintval;
-		struct ieee80211_rsnparms rsn;
-		u_int8_t reason;
+		uint16_t capinfo, lintval;
+		struct ieee80211_rsnparms rsnparms;
+		uint8_t reason;
+		int badwparsn;
 
 		if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
 		    ic->ic_state != IEEE80211_S_RUN) {
@@ -2227,8 +2628,10 @@
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
 		 *	[tlv] WPA or RSN
+		 *	[tlv] HT capabilities
+		 *	[tlv] Atheros capabilities
 		 */
-		IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
+		IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
 		if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
 			IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
 			    wh, ieee80211_mgt_subtype_name[subtype >>
@@ -2237,13 +2640,13 @@
 			ic->ic_stats.is_rx_assoc_bss++;
 			return;
 		}
-		capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
-		lintval = le16toh(*(u_int16_t *)frm);	frm += 2;
+		capinfo = le16toh(*(uint16_t *)frm);	frm += 2;
+		lintval = le16toh(*(uint16_t *)frm);	frm += 2;
 		if (reassoc)
 			frm += 6;	/* ignore current AP info */
-		ssid = rates = xrates = wpa = wme = NULL;
-		while (frm < efrm) {
-			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+		ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
+		while (efrm - frm > 1) {
+			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
 			switch (*frm) {
 			case IEEE80211_ELEMID_SSID:
 				ssid = frm;
@@ -2256,21 +2659,39 @@
 				break;
 			/* XXX verify only one of RSN and WPA ie's? */
 			case IEEE80211_ELEMID_RSN:
-				wpa = frm;
+				rsn = frm;
+				break;
+			case IEEE80211_ELEMID_HTCAP:
+				htcap = frm;
 				break;
 			case IEEE80211_ELEMID_VENDOR:
 				if (iswpaoui(frm))
 					wpa = frm;
 				else if (iswmeinfo(frm))
 					wme = frm;
-				/* XXX Atheros OUI support */
+				else if (isatherosoui(frm))
+					ath = frm;
+				else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+					if (ishtcapoui(frm) && htcap == NULL)
+						htcap = frm;
+				}
 				break;
 			}
 			frm += frm[1] + 2;
 		}
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+		if (xrates != NULL)
+			IEEE80211_VERIFY_ELEMENT(xrates,
+				IEEE80211_RATE_MAXSIZE - rates[1]);
 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
 		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+		if (htcap != NULL) {
+			IEEE80211_VERIFY_LENGTH(htcap[1],
+			     htcap[0] == IEEE80211_ELEMID_VENDOR ?
+			         4 + sizeof(struct ieee80211_ie_htcap)-2 :
+			         sizeof(struct ieee80211_ie_htcap)-2,
+			     return);		/* XXX just NULL out? */
+		}
 
 		if (ni == ic->ic_bss) {
 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
@@ -2283,33 +2704,47 @@
 			ic->ic_stats.is_rx_assoc_notauth++;
 			return;
 		}
-		/* assert right associstion security credentials */
-		if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
+		/* assert right association security credentials */
+		badwparsn = 0;
+		switch (ic->ic_flags & IEEE80211_F_WPA) {
+		case IEEE80211_F_WPA1:
+			if (wpa == NULL)
+				badwparsn = 1;
+			break;
+		case IEEE80211_F_WPA2:
+			if (rsn == NULL)
+				badwparsn = 1;
+			break;
+		case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
+			if (wpa == NULL && rsn == NULL)
+				badwparsn = 1;
+			break;
+		}
+		if (badwparsn) {
 			IEEE80211_DPRINTF(ic,
 			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
 			    "[%s] no WPA/RSN IE in association request\n",
 			    ether_sprintf(wh->i_addr2));
 			IEEE80211_SEND_MGMT(ic, ni,
 			    IEEE80211_FC0_SUBTYPE_DEAUTH,
-			    IEEE80211_REASON_RSN_REQUIRED);
+			    IEEE80211_REASON_IE_INVALID);
 			ieee80211_node_leave(ic, ni);
-			/* XXX distinguish WPA/RSN? */
 			ic->ic_stats.is_rx_assoc_badwpaie++;
-			return;	
+			return;
 		}
-		if (wpa != NULL) {
+		if (wpa != NULL || rsn != NULL) {
 			/*
-			 * Parse WPA information element.  Note that
+			 * Parse WPA/RSN 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);
+			rsnparms = ni->ni_rsn;
+			if (wpa != NULL)
+				reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh);
 			else
-				reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh);
+				reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh);
 			if (reason != 0) {
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
@@ -2322,71 +2757,133 @@
 			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
 			    "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
 			    ether_sprintf(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);
+			    wpa != NULL ? "WPA" : "RSN",
+			    rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen,
+			    rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen,
+			    rsnparms.rsn_keymgmt, rsnparms.rsn_caps);
 		}
 		/* discard challenge after association */
 		if (ni->ni_challenge != NULL) {
-			FREE(ni->ni_challenge, M_DEVBUF);
+			FREE(ni->ni_challenge, M_80211_NODE);
 			ni->ni_challenge = NULL;
 		}
 		/* NB: 802.11 spec says to ignore station's privacy bit */
 		if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
-			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
-			    "[%s] deny %s request, capability mismatch 0x%x\n",
-			    ether_sprintf(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++;
+			capinfomismatch(ni, wh, reassoc, resp,
+			    "capability", capinfo);
+			return;
+		}
+		/*
+		 * Disallow re-associate w/ invalid slot time setting.
+		 */
+		if (ni->ni_associd != 0 &&
+		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+		    ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
+			capinfomismatch(ni, wh, reassoc, resp,
+			    "slot time", capinfo);
 			return;
 		}
 		rate = ieee80211_setup_rates(ni, rates, xrates,
 				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
 				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+		if (rate & IEEE80211_RATE_BASIC) {
+			ratesetmismatch(ni, wh, reassoc, resp, "basic", rate);
+			return;
+		}
 		/*
 		 * If constrained to 11g-only stations reject an
 		 * 11b-only station.  We cheat a bit here by looking
 		 * at the max negotiated xmit rate and assuming anyone
 		 * with a best rate <24Mb/s is an 11b station.
 		 */
-		if ((rate & IEEE80211_RATE_BASIC) ||
-		    ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48)) {
-			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
-			    "[%s] deny %s request, rate set mismatch\n",
-			    ether_sprintf(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++;
+		if ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48) {
+			ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
+			return;
+		}
+		/* XXX enforce PUREN */
+		/* 802.11n-specific rateset handling */
+		if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) {
+			rate = ieee80211_setup_htrates(ni, htcap,
+				IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
+				IEEE80211_F_DOBRS);
+			if (rate & IEEE80211_RATE_BASIC) {
+				/* XXX 11n-specific stat */
+				ratesetmismatch(ni, wh, reassoc, resp,
+				    "HT", rate);
+				return;
+			}
+			ieee80211_ht_node_init(ni, htcap);
+		} else if (ni->ni_flags & IEEE80211_NODE_HT)
+			ieee80211_ht_node_cleanup(ni);
+		/*
+		 * Allow AMPDU operation only with unencrypted traffic
+		 * or AES-CCM; the 11n spec only specifies these ciphers
+		 * so permitting any others is undefined and can lead
+		 * to interoperability problems.
+		 *
+		 * NB: We check for AES by looking at the GTK cipher
+		 *     since the WPA/11i specs say the PTK cipher has
+		 *     to be "as good or better".
+		 */
+		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+		    (((ic->ic_flags & IEEE80211_F_WPA) &&
+		      rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) ||
+		     (ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+			    "disallow HT use because WEP or TKIP requested, "
+			    "capinfo 0x%x mcastcipher %d", capinfo,
+			    rsnparms.rsn_mcastcipher);
+			ieee80211_ht_node_cleanup(ni);
+			ic->ic_stats.is_ht_assoc_downgrade++;
+		}
+		/*
+		 * If constrained to 11n-only stations reject legacy stations.
+		 */
+		if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) &&
+		    (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
+			htcapmismatch(ni, wh, reassoc, resp);
+			ic->ic_stats.is_ht_assoc_nohtcap++;
 			return;
 		}
 		ni->ni_rssi = rssi;
+		ni->ni_noise = noise;
 		ni->ni_rstamp = rstamp;
 		ni->ni_intval = lintval;
 		ni->ni_capinfo = capinfo;
-		ni->ni_chan = ic->ic_bss->ni_chan;
+		ni->ni_chan = ic->ic_bsschan;
 		ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
 		ni->ni_fhindex = ic->ic_bss->ni_fhindex;
 		if (wpa != NULL) {
 			/*
-			 * Record WPA/RSN parameters for station, mark
+			 * Record WPA parameters for station, mark
 			 * node as using WPA and record information element
 			 * for applications that require it.
 			 */
-			ni->ni_rsn = rsn;
+			ni->ni_rsn = rsnparms;
 			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);
+			FREE(ni->ni_wpa_ie, M_80211_NODE);
 			ni->ni_wpa_ie = NULL;
 		}
+		if (rsn != NULL) {
+			/*
+			 * Record RSN parameters for station, mark
+			 * node as using WPA and record information element
+			 * for applications that require it.
+			 */
+			ni->ni_rsn = rsnparms;
+			ieee80211_saveie(&ni->ni_rsn_ie, rsn);
+		} else if (ni->ni_rsn_ie != NULL) {
+			/*
+			 * Flush any state from a previous association.
+			 */
+			FREE(ni->ni_rsn_ie, M_80211_NODE);
+			ni->ni_rsn_ie = NULL;
+		}
 		if (wme != NULL) {
 			/*
 			 * Record WME parameters for station, mark node
@@ -2399,19 +2896,35 @@
 			/*
 			 * Flush any state from a previous association.
 			 */
-			FREE(ni->ni_wme_ie, M_DEVBUF);
+			FREE(ni->ni_wme_ie, M_80211_NODE);
 			ni->ni_wme_ie = NULL;
 			ni->ni_flags &= ~IEEE80211_NODE_QOS;
 		}
-		ieee80211_deliver_l2uf(ni);
+		if (ath != NULL) {
+			/* 
+			 * Record ATH parameters for station, mark
+			 * node with appropriate capabilities, and
+			 * record the information element for
+			 * applications that require it.
+			 */
+			ieee80211_saveath(ni, ath);
+		} else if (ni->ni_ath_ie != NULL) {
+			/*
+			 * Flush any state from a previous association.
+			 */
+			FREE(ni->ni_ath_ie, M_80211_NODE);
+			ni->ni_ath_ie = NULL;
+			ni->ni_ath_flags = 0;
+		}
 		ieee80211_node_join(ic, ni, resp);
+		ieee80211_deliver_l2uf(ni);
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
-		u_int16_t capinfo, associd;
-		u_int16_t status;
+		uint16_t capinfo, associd;
+		uint16_t status;
 
 		if (ic->ic_opmode != IEEE80211_M_STA ||
 		    ic->ic_state != IEEE80211_S_ASSOC) {
@@ -2427,12 +2940,14 @@
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
 		 *	[tlv] WME
+		 *	[tlv] HT capabilities
+		 *	[tlv] HT info
 		 */
-		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
 		ni = ic->ic_bss;
-		capinfo = le16toh(*(u_int16_t *)frm);
+		capinfo = le16toh(*(uint16_t *)frm);
 		frm += 2;
-		status = le16toh(*(u_int16_t *)frm);
+		status = le16toh(*(uint16_t *)frm);
 		frm += 2;
 		if (status != 0) {
 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
@@ -2444,12 +2959,12 @@
 			ic->ic_stats.is_rx_auth_fail++;	/* XXX */
 			return;
 		}
-		associd = le16toh(*(u_int16_t *)frm);
+		associd = le16toh(*(uint16_t *)frm);
 		frm += 2;
 
-		rates = xrates = wpa = wme = NULL;
-		while (frm < efrm) {
-			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+		rates = xrates = wme = htcap = htinfo = NULL;
+		while (efrm - frm > 1) {
+			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
 			switch (*frm) {
 			case IEEE80211_ELEMID_RATES:
 				rates = frm;
@@ -2457,9 +2972,28 @@
 			case IEEE80211_ELEMID_XRATES:
 				xrates = frm;
 				break;
+			case IEEE80211_ELEMID_HTCAP:
+				htcap = frm;
+				break;
+			case IEEE80211_ELEMID_HTINFO:
+				htinfo = frm;
+				break;
 			case IEEE80211_ELEMID_VENDOR:
 				if (iswmeoui(frm))
 					wme = frm;
+				else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+					/*
+					 * Accept pre-draft HT ie's if the
+					 * standard ones have not been seen.
+					 */
+					if (ishtcapoui(frm)) {
+						if (htcap == NULL)
+							htcap = frm;
+					} else if (ishtinfooui(frm)) {
+						if (htinfo == NULL)
+							htcap = frm;
+					}
+				}
 				/* XXX Atheros OUI support */
 				break;
 			}
@@ -2467,7 +3001,11 @@
 		}
 
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+		if (xrates != NULL)
+			IEEE80211_VERIFY_ELEMENT(xrates,
+				IEEE80211_RATE_MAXSIZE - rates[1]);
 		rate = ieee80211_setup_rates(ni, rates, xrates,
+				IEEE80211_F_JOIN |
 				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
 				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
 		if (rate & IEEE80211_RATE_BASIC) {
@@ -2478,7 +3016,8 @@
 			if (ni != ic->ic_bss)	/* XXX never true? */
 				ni->ni_fails++;
 			ic->ic_stats.is_rx_assoc_norate++;
-			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+			ieee80211_new_state(ic, IEEE80211_S_SCAN,
+			    IEEE80211_SCAN_FAIL_STATUS);
 			return;
 		}
 
@@ -2491,11 +3030,30 @@
 		} else
 			ni->ni_flags &= ~IEEE80211_NODE_QOS;
 		/*
+		 * Setup HT state according to the negotiation.
+		 */
+		if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
+		    htcap != NULL && htinfo != NULL) {
+			ieee80211_ht_node_init(ni, htcap);
+			ieee80211_parse_htinfo(ni, htinfo);
+			ieee80211_setup_htrates(ni,
+			    htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+			ieee80211_setup_basic_htrates(ni, htinfo);
+			if (ni->ni_chan != ic->ic_bsschan) {
+				/*
+				 * Channel has been adjusted based on
+				 * negotiated HT parameters; force the
+				 * channel state to follow.
+				 */
+				ieee80211_setbsschan(ic, ni->ni_chan);
+			}
+		}
+		/*
 		 * Configure state now that we are associated.
 		 *
 		 * XXX may need different/additional driver callbacks?
 		 */
-		if (ic->ic_curmode == IEEE80211_MODE_11A ||
+		if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
 		    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
 			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
 			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
@@ -2504,34 +3062,40 @@
 			ic->ic_flags |= IEEE80211_F_USEBARKER;
 		}
 		ieee80211_set_shortslottime(ic,
-			ic->ic_curmode == IEEE80211_MODE_11A ||
+			IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
 			(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 (ic->ic_curmode == IEEE80211_MODE_11G &&
+		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
 		    (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,
-		    "[%s] %sassoc success: %s preamble, %s slot time%s%s\n",
+		    "[%s] %sassoc success: %s preamble, %s slot time%s%s%s%s\n",
 		    ether_sprintf(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" : ""
+		    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+		    ni->ni_flags & IEEE80211_NODE_HT ?
+			(ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
+		    ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
+		    IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
+			", fast-frames" : "",
+		    IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
+			", turbo" : ""
 		);
 		ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_DEAUTH: {
-		u_int16_t reason;
+		uint16_t reason;
 
 		if (ic->ic_state == IEEE80211_S_SCAN) {
 			ic->ic_stats.is_rx_mgtdiscard++;
@@ -2541,18 +3105,23 @@
 		 * deauth frame format
 		 *	[2] reason
 		 */
-		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
-		reason = le16toh(*(u_int16_t *)frm);
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+		reason = le16toh(*(uint16_t *)frm);
 		ic->ic_stats.is_rx_deauth++;
 		IEEE80211_NODE_STAT(ni, rx_deauth);
 
+		if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
+			/* NB: can happen when in promiscuous mode */
+			ic->ic_stats.is_rx_mgtdiscard++;
+			break;
+		}
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
 		    "[%s] recv deauthenticate (reason %d)\n",
 		    ether_sprintf(ni->ni_macaddr), reason);
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
 			ieee80211_new_state(ic, IEEE80211_S_AUTH,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			    (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (ni != ic->ic_bss)
@@ -2566,7 +3135,7 @@
 	}
 
 	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
-		u_int16_t reason;
+		uint16_t reason;
 
 		if (ic->ic_state != IEEE80211_S_RUN &&
 		    ic->ic_state != IEEE80211_S_ASSOC &&
@@ -2578,18 +3147,22 @@
 		 * disassoc frame format
 		 *	[2] reason
 		 */
-		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
-		reason = le16toh(*(u_int16_t *)frm);
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+		reason = le16toh(*(uint16_t *)frm);
 		ic->ic_stats.is_rx_disassoc++;
 		IEEE80211_NODE_STAT(ni, rx_disassoc);
 
+		if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
+			/* NB: can happen when in promiscuous mode */
+			ic->ic_stats.is_rx_mgtdiscard++;
+			break;
+		}
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
 		    "[%s] recv disassociate (reason %d)\n",
 		    ether_sprintf(ni->ni_macaddr), reason);
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
-			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (ni != ic->ic_bss)
@@ -2601,6 +3174,65 @@
 		}
 		break;
 	}
+
+	case IEEE80211_FC0_SUBTYPE_ACTION: {
+		const struct ieee80211_action *ia;
+
+		if (ic->ic_state != IEEE80211_S_RUN &&
+		    ic->ic_state != IEEE80211_S_ASSOC &&
+		    ic->ic_state != IEEE80211_S_AUTH) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+		/*
+		 * action frame format:
+		 *	[1] category
+		 *	[1] action
+		 *	[tlv] parameters
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm,
+			sizeof(struct ieee80211_action), return);
+		ia = (const struct ieee80211_action *) frm;
+
+		ic->ic_stats.is_rx_action++;
+		IEEE80211_NODE_STAT(ni, rx_action);
+
+		/* verify frame payloads but defer processing */
+		/* XXX maybe push this to method */
+		switch (ia->ia_category) {
+		case IEEE80211_ACTION_CAT_BA:
+			switch (ia->ia_action) {
+			case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+				IEEE80211_VERIFY_LENGTH(efrm - frm,
+				    sizeof(struct ieee80211_action_ba_addbarequest),
+				    return);
+				break;
+			case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+				IEEE80211_VERIFY_LENGTH(efrm - frm,
+				    sizeof(struct ieee80211_action_ba_addbaresponse),
+				    return);
+				break;
+			case IEEE80211_ACTION_BA_DELBA:
+				IEEE80211_VERIFY_LENGTH(efrm - frm,
+				    sizeof(struct ieee80211_action_ba_delba),
+				    return);
+				break;
+			}
+			break;
+		case IEEE80211_ACTION_CAT_HT:
+			switch (ia->ia_action) {
+			case IEEE80211_ACTION_HT_TXCHWIDTH:
+				IEEE80211_VERIFY_LENGTH(efrm - frm,
+				    sizeof(struct ieee80211_action_ht_txchwidth),
+				    return);
+				break;
+			}
+			break;
+		}
+		ic->ic_recv_action(ni, frm, efrm);
+		break;
+	}
+
 	default:
 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
 		     wh, "mgt", "subtype 0x%x not handled", subtype);
@@ -2614,66 +3246,6 @@
 #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,
-		    "[%s] power save mode on, %u sta's in ps mode\n",
-		    ether_sprintf(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,
-	    "[%s] power save mode off, %u sta's in ps mode\n",
-	    ether_sprintf(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) {
-		if (ic->ic_set_tim != NULL)
-			ic->ic_set_tim(ni, 0);		/* just in case */
-		return;
-	}
-	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
-	    "[%s] flush ps queue, %u packets queued\n",
-	    ether_sprintf(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 mbuf so ieee80211_encap will mark the 802.11
-		 * head to indicate more data frames will follow.
-		 */
-		if (qlen != 0)
-			m->m_flags |= M_MORE_DATA;
-		/* XXX need different driver interface */
-		/* XXX bypasses q max */
-		IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
-	}
-	if (ic->ic_set_tim != NULL)
-		ic->ic_set_tim(ni, 0);
-}
-
-/*
  * Process a received ps-poll frame.
  */
 static void
@@ -2682,7 +3254,7 @@
 {
 	struct ieee80211_frame_min *wh;
 	struct mbuf *m;
-	u_int16_t aid;
+	uint16_t aid;
 	int qlen;
 
 	wh = mtod(m0, struct ieee80211_frame_min *);
@@ -2696,7 +3268,7 @@
 		return;
 	}
 
-	aid = le16toh(*(u_int16_t *)wh->i_dur);
+	aid = le16toh(*(uint16_t *)wh->i_dur);
 	if (aid != ni->ni_associd) {
 		IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
 		    (struct ieee80211_frame *) wh, "ps-poll",
@@ -2749,7 +3321,7 @@
 /*
  * Return the bssid of a frame.
  */
-static const u_int8_t *
+static const uint8_t *
 ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
 {
 	if (ic->ic_opmode == IEEE80211_M_STA)
@@ -2791,7 +3363,7 @@
 
 void
 ieee80211_note_mac(struct ieee80211com *ic,
-	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	const uint8_t mac[IEEE80211_ADDR_LEN],
 	const char *fmt, ...)
 {
 	char buf[128];		/* XXX */
@@ -2803,7 +3375,7 @@
 	if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
 }
 
-static void
+void
 ieee80211_discard_frame(struct ieee80211com *ic,
 	const struct ieee80211_frame *wh,
 	const char *type, const char *fmt, ...)
@@ -2822,7 +3394,7 @@
 	printf("\n");
 }
 
-static void
+void
 ieee80211_discard_ie(struct ieee80211com *ic,
 	const struct ieee80211_frame *wh,
 	const char *type, const char *fmt, ...)
@@ -2841,9 +3413,9 @@
 	printf("\n");
 }
 
-static void
+void
 ieee80211_discard_mac(struct ieee80211com *ic,
-	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	const uint8_t mac[IEEE80211_ADDR_LEN],
 	const char *type, const char *fmt, ...)
 {
 	va_list ap;
Index: ieee80211_crypto_none.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_none.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_none.c -L sys/net80211/ieee80211_crypto_none.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_crypto_none.c
+++ sys/net80211/ieee80211_crypto_none.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -30,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_none.c,v 1.5 2005/06/10 16:11:24 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_none.c,v 1.7 2007/06/11 03:36:54 sam Exp $");
 
 /*
  * IEEE 802.11 NULL crypto support.
@@ -51,7 +45,7 @@
 static	void *none_attach(struct ieee80211com *, struct ieee80211_key *);
 static	void none_detach(struct ieee80211_key *);
 static	int none_setkey(struct ieee80211_key *);
-static	int none_encap(struct ieee80211_key *, struct mbuf *, u_int8_t);
+static	int none_encap(struct ieee80211_key *, struct mbuf *, uint8_t);
 static	int none_decap(struct ieee80211_key *, struct mbuf *, int);
 static	int none_enmic(struct ieee80211_key *, struct mbuf *, int);
 static	int none_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -91,7 +85,7 @@
 }
 
 static int
-none_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
 {
 	struct ieee80211com *ic = k->wk_private;
 #ifdef IEEE80211_DEBUG
@@ -115,7 +109,7 @@
 	struct ieee80211com *ic = k->wk_private;
 #ifdef IEEE80211_DEBUG
 	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
-	const u_int8_t *ivp = (const u_int8_t *)&wh[1];
+	const uint8_t *ivp = (const uint8_t *)&wh[1];
 #endif
 
 	/*
Index: _ieee80211.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/_ieee80211.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/_ieee80211.h -L sys/net80211/_ieee80211.h -u -r1.1.1.1 -r1.2
--- sys/net80211/_ieee80211.h
+++ sys/net80211/_ieee80211.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,7 +23,7 @@
  * (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.2.2.1 2005/09/03 22:40:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/_ieee80211.h,v 1.14 2007/09/18 20:46:36 sam Exp $
  */
 #ifndef _NET80211__IEEE80211_H_
 #define _NET80211__IEEE80211_H_
@@ -39,6 +33,7 @@
 	IEEE80211_T_FH,			/* frequency hopping */
 	IEEE80211_T_OFDM,		/* frequency division multiplexing */
 	IEEE80211_T_TURBO,		/* high rate OFDM, aka turbo mode */
+	IEEE80211_T_HT,			/* high throughput, full GI */
 };
 #define	IEEE80211_T_CCK	IEEE80211_T_DS	/* more common nomenclature */
 
@@ -51,19 +46,24 @@
 	IEEE80211_MODE_FH	= 4,	/* 2GHz, GFSK */
 	IEEE80211_MODE_TURBO_A	= 5,	/* 5GHz, OFDM, 2x clock */
 	IEEE80211_MODE_TURBO_G	= 6,	/* 2GHz, OFDM, 2x clock */
+	IEEE80211_MODE_STURBO_A	= 7,	/* 5GHz, OFDM, 2x clock, static */
+	IEEE80211_MODE_11NA	= 8,	/* 5GHz, w/ HT */
+	IEEE80211_MODE_11NG	= 9,	/* 2GHz, w/ HT */
 };
-#define	IEEE80211_MODE_MAX	(IEEE80211_MODE_TURBO_G+1)
+#define	IEEE80211_MODE_MAX	(IEEE80211_MODE_11NG+1)
 
 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 */
+	IEEE80211_M_MONITOR	= 8,	/* Monitor mode */
+	IEEE80211_M_WDS		= 2	/* WDS link */
 };
+#define	IEEE80211_OPMODE_MAX	(IEEE80211_M_MONITOR+1)
 
 /*
- * 802.11g protection mode.
+ * 802.11g/802.11n protection mode.
  */
 enum ieee80211_protmode {
 	IEEE80211_PROT_NONE	= 0,	/* no protection */
@@ -103,8 +103,14 @@
  * Channels are specified by frequency and attributes.
  */
 struct ieee80211_channel {
-	u_int16_t	ic_freq;	/* setting in Mhz */
-	u_int16_t	ic_flags;	/* see below */
+	uint32_t	ic_flags;	/* see below */
+	uint16_t	ic_freq;	/* setting in Mhz */
+	uint8_t		ic_ieee;	/* IEEE channel number */
+	int8_t		ic_maxregpower;	/* maximum regulatory tx power in dBm */
+	int8_t		ic_maxpower;	/* maximum tx power in .5 dBm */
+	int8_t		ic_minpower;	/* minimum tx power in .5 dBm */
+	uint8_t		ic_state;	/* dynamic state */
+	uint8_t		ic_extieee;	/* HT40 extension channel number */
 };
 
 #define	IEEE80211_CHAN_MAX	255
@@ -115,14 +121,29 @@
 
 /* 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_CHAN_TURBO	0x00000010 /* Turbo channel */
+#define	IEEE80211_CHAN_CCK	0x00000020 /* CCK channel */
+#define	IEEE80211_CHAN_OFDM	0x00000040 /* OFDM channel */
+#define	IEEE80211_CHAN_2GHZ	0x00000080 /* 2 GHz spectrum channel. */
+#define	IEEE80211_CHAN_5GHZ	0x00000100 /* 5 GHz spectrum channel */
+#define	IEEE80211_CHAN_PASSIVE	0x00000200 /* Only passive scan allowed */
+#define	IEEE80211_CHAN_DYN	0x00000400 /* Dynamic CCK-OFDM channel */
+#define	IEEE80211_CHAN_GFSK	0x00000800 /* GFSK channel (FHSS PHY) */
+#define	IEEE80211_CHAN_GSM	0x00001000 /* 900 MHz spectrum channel */
+#define	IEEE80211_CHAN_STURBO	0x00002000 /* 11a static turbo channel only */
+#define	IEEE80211_CHAN_HALF	0x00004000 /* Half rate channel */
+#define	IEEE80211_CHAN_QUARTER	0x00008000 /* Quarter rate channel */
+#define	IEEE80211_CHAN_HT20	0x00010000 /* HT 20 channel */
+#define	IEEE80211_CHAN_HT40U	0x00020000 /* HT 40 channel w/ ext above */
+#define	IEEE80211_CHAN_HT40D	0x00040000 /* HT 40 channel w/ ext below */
+#define	IEEE80211_CHAN_DFS	0x00080000 /* DFS required */
+#define	IEEE80211_CHAN_4MSXMIT	0x00100000 /* 4ms limit on frame length */
+#define	IEEE80211_CHAN_NOADHOC	0x00200000 /* adhoc mode not allowed */
+#define	IEEE80211_CHAN_NOHOSTAP	0x00400000 /* hostap mode not allowed */
+#define	IEEE80211_CHAN_11D	0x00800000 /* 802.11d required */
+
+#define	IEEE80211_CHAN_HT40	(IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
+#define	IEEE80211_CHAN_HT	(IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
 
 /*
  * Useful combinations of channel characteristics.
@@ -137,10 +158,19 @@
 	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
 #define	IEEE80211_CHAN_G \
 	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
-#define	IEEE80211_CHAN_T \
+#define IEEE80211_CHAN_108A \
 	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
 #define	IEEE80211_CHAN_108G \
 	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+#define	IEEE80211_CHAN_ST \
+	(IEEE80211_CHAN_108A | IEEE80211_CHAN_STURBO)
+
+#define	IEEE80211_CHAN_ALL \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_GFSK | \
+	 IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | \
+	 IEEE80211_CHAN_HT)
+#define	IEEE80211_CHAN_ALLTURBO \
+	(IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
 
 #define	IEEE80211_IS_CHAN_FHSS(_c) \
 	(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
@@ -154,8 +184,10 @@
 	(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
 #define	IEEE80211_IS_CHAN_ANYG(_c) \
 	(IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c))
-#define	IEEE80211_IS_CHAN_T(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+#define	IEEE80211_IS_CHAN_ST(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)
+#define	IEEE80211_IS_CHAN_108A(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)
 #define	IEEE80211_IS_CHAN_108G(_c) \
 	(((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
 
@@ -163,12 +195,65 @@
 	(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
 #define	IEEE80211_IS_CHAN_5GHZ(_c) \
 	(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
+#define	IEEE80211_IS_CHAN_PASSIVE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_PASSIVE) != 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)
+#define	IEEE80211_IS_CHAN_TURBO(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_TURBO) != 0)
+#define	IEEE80211_IS_CHAN_STURBO(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_STURBO) != 0)
+#define	IEEE80211_IS_CHAN_DTURBO(_c) \
+	(((_c)->ic_flags & \
+	(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) == IEEE80211_CHAN_TURBO)
+#define	IEEE80211_IS_CHAN_HALF(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HALF) != 0)
+#define	IEEE80211_IS_CHAN_QUARTER(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_QUARTER) != 0)
+#define	IEEE80211_IS_CHAN_FULL(_c) \
+	(((_c)->ic_flags & (IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HALF)) == 0)
+#define	IEEE80211_IS_CHAN_GSM(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_GSM) != 0)
+#define	IEEE80211_IS_CHAN_HT(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define	IEEE80211_IS_CHAN_HT20(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT20) != 0)
+#define	IEEE80211_IS_CHAN_HT40(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40) != 0)
+#define	IEEE80211_IS_CHAN_HT40U(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40U) != 0)
+#define	IEEE80211_IS_CHAN_HT40D(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40D) != 0)
+#define	IEEE80211_IS_CHAN_HTA(_c) \
+	(IEEE80211_IS_CHAN_5GHZ(_c) && \
+	 ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define	IEEE80211_IS_CHAN_HTG(_c) \
+	(IEEE80211_IS_CHAN_2GHZ(_c) && \
+	 ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define	IEEE80211_IS_CHAN_DFS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_DFS) != 0)
+#define	IEEE80211_IS_CHAN_NOADHOC(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_NOADHOC) != 0)
+#define	IEEE80211_IS_CHAN_NOHOSTAP(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_NOHOSTAP) != 0)
+#define	IEEE80211_IS_CHAN_11D(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11D) != 0)
+
+#define	IEEE80211_CHAN2IEEE(_c)		(_c)->ic_ieee
+
+/* dynamic state */
+#define	IEEE80211_CHANSTATE_RADAR	0x01	/* radar detected */
+#define	IEEE80211_CHANSTATE_CACDONE	0x02	/* CAC completed */
+#define	IEEE80211_CHANSTATE_NORADAR	0x10	/* post notify on radar clear */
+
+#define	IEEE80211_IS_CHAN_RADAR(_c) \
+	(((_c)->ic_state & IEEE80211_CHANSTATE_RADAR) != 0)
+#define	IEEE80211_IS_CHAN_CACDONE(_c) \
+	(((_c)->ic_state & IEEE80211_CHANSTATE_CACDONE) != 0)
 
 /* ni_chan encoding for FH phy */
 #define	IEEE80211_FH_CHANMOD	80
@@ -176,6 +261,9 @@
 #define	IEEE80211_FH_CHANSET(chan)	((chan)/IEEE80211_FH_CHANMOD+1)
 #define	IEEE80211_FH_CHANPAT(chan)	((chan)%IEEE80211_FH_CHANMOD)
 
+#define	IEEE80211_TID_SIZE	(WME_NUM_TID+1)	/* WME TID's +1 for non-QoS */
+#define	IEEE80211_NONQOS_TID	WME_NUM_TID	/* index for non-QoS sta */
+
 /*
  * 802.11 rate set.
  */
@@ -183,8 +271,40 @@
 #define	IEEE80211_RATE_MAXSIZE	15		/* max rates we'll handle */
 
 struct ieee80211_rateset {
-	u_int8_t		rs_nrates;
-	u_int8_t		rs_rates[IEEE80211_RATE_MAXSIZE];
+	uint8_t			rs_nrates;
+	uint8_t			rs_rates[IEEE80211_RATE_MAXSIZE];
 };
 
+/*
+ * 802.11n variant of ieee80211_rateset.  Instead
+ * legacy rates the entries are MCS rates.  We define
+ * the structure such that it can be used interchangeably
+ * with an ieee80211_rateset (modulo structure size).
+ */
+#define	IEEE80211_HTRATE_MAXSIZE 127
+
+struct ieee80211_htrateset {
+	uint8_t			rs_nrates;
+	uint8_t			rs_rates[IEEE80211_HTRATE_MAXSIZE];
+};
+
+#define	IEEE80211_RATE_MCS	0x80
+
+/*
+ * Roaming state visible to user space.  There are two
+ * thresholds that control whether roaming is considered;
+ * when either is exceeded the 802.11 layer will check
+ * the scan cache for another AP.  If the cache is stale
+ * then a scan may be triggered.
+ */
+struct ieee80211_roam {
+	int8_t			rssi11a;	/* rssi thresh for 11a bss */
+	int8_t			rssi11b;	/* for 11g sta in 11b bss */
+	int8_t			rssi11bOnly;	/* for 11b sta */
+	uint8_t			pad1;
+	uint8_t			rate11a;	/* rate thresh for 11a bss */
+	uint8_t			rate11b;	/* for 11g sta in 11b bss */
+	uint8_t			rate11bOnly;	/* for 11b sta */
+	uint8_t			pad2;
+};
 #endif /* _NET80211__IEEE80211_H_ */
Index: ieee80211_xauth.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_xauth.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_xauth.c -L sys/net80211/ieee80211_xauth.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_xauth.c
+++ sys/net80211/ieee80211_xauth.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_xauth.c,v 1.2 2004/12/31 22:42:38 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_xauth.c,v 1.3 2007/06/06 04:56:04 sam Exp $");
 
 /*
  * External authenticator placeholder module.
Index: ieee80211_radiotap.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_radiotap.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_radiotap.h -L sys/net80211/ieee80211_radiotap.h -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_radiotap.h
+++ sys/net80211/ieee80211_radiotap.h
@@ -1,5 +1,5 @@
-/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5.2.1 2006/01/29 07:13:58 sam Exp $ */
-/* $NetBSD: ieee80211_radiotap.h,v 1.10 2005/01/04 00:34:58 dyoung Exp $ */
+/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.10 2007/07/01 06:59:30 thompsa Exp $ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.16 2007/01/06 05:51:15 dyoung Exp $ */
 
 /*-
  * Copyright (c) 2003, 2004 David Young.  All rights reserved.
@@ -29,14 +29,14 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  */
-#ifndef _NET_IF_IEEE80211RADIOTAP_H_
-#define _NET_IF_IEEE80211RADIOTAP_H_
+#ifndef _NET80211_IEEE80211_RADIOTAP_H_
+#define _NET80211_IEEE80211_RADIOTAP_H_
 
-/* A generic radio capture format is desirable. There is one for
- * Linux, but it is neither rigidly defined (there were not even
- * units given for some fields) nor easily extensible.
+/* A generic radio capture format is desirable. It must be
+ * rigidly defined (e.g., units for fields should be given),
+ * and easily extensible.
  *
- * I suggest the following extensible radio capture format. It is
+ * The following is an extensible radio capture format. It is
  * based on a bitmap indicating which fields are present.
  *
  * I am trying to describe precisely what the application programmer
@@ -52,54 +52,57 @@
 #endif
 #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
+#define	IEEE80211_RADIOTAP_HDRLEN	64	/* XXX deprecated */
 
-/* The radio capture header precedes the 802.11 header. */
+/*
+ * The radio capture header precedes the 802.11 header.
+ *
+ * Note well: all radiotap fields are little-endian.
+ */
 struct ieee80211_radiotap_header {
-	u_int8_t	it_version;	/* Version 0. Only increases
+	uint8_t		it_version;	/* Version 0. Only increases
 					 * for drastic changes,
 					 * introduction of compatible
 					 * new fields does not count.
 					 */
-	u_int8_t	it_pad;
-	u_int16_t       it_len;         /* length of the whole
+	uint8_t		it_pad;
+	uint16_t	it_len;		/* length of the whole
 					 * header in bytes, including
 					 * it_version, it_pad,
 					 * it_len, and data fields.
 					 */
-	u_int32_t       it_present;     /* A bitmap telling which
+	uint32_t	it_present;	/* A bitmap telling which
 					 * fields are present. Set bit 31
 					 * (0x80000000) to extend the
 					 * bitmap by another 32 bits.
 					 * Additional extensions are made
 					 * by setting bit 31.
 					 */
-} __attribute__((__packed__));
+} __packed;
 
-/* Name                                 Data type       Units
+/*
+ * Name                                 Data type       Units
  * ----                                 ---------       -----
  *
- * IEEE80211_RADIOTAP_TSFT              u_int64_t       microseconds
+ * IEEE80211_RADIOTAP_TSFT              uint64_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 u_int16_t   MHz, bitmap
+ * IEEE80211_RADIOTAP_CHANNEL           2 x uint16_t    MHz, bitmap
  *
  *      Tx/Rx frequency in MHz, followed by flags (see below).
  *
- * IEEE80211_RADIOTAP_FHSS              u_int16_t       see below
+ * IEEE80211_RADIOTAP_FHSS              uint16_t        see below
  *
  *      For frequency-hopping radios, the hop set (first byte)
  *      and pattern (second byte).
  *
- * IEEE80211_RADIOTAP_RATE              u_int8_t        500kb/s
+ * IEEE80211_RADIOTAP_RATE              uint8_t         500kb/s or index
  *
- *      Tx/Rx data rate
+ *      Tx/Rx data rate.  If bit 0x80 is set then it represents an
+ *	an MCS index and not an IEEE rate.
  *
  * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     int8_t          decibels from
  *                                                      one milliwatt (dBm)
@@ -113,30 +116,30 @@
  *      RF noise power at the antenna, decibel difference from one
  *      milliwatt.
  *
- * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u_int8_t        decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL      uint8_t         decibel (dB)
  *
  *      RF signal power at the antenna, decibel difference from an
  *      arbitrary, fixed reference.
  *
- * IEEE80211_RADIOTAP_DB_ANTNOISE       u_int8_t        decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTNOISE       uint8_t         decibel (dB)
  *
  *      RF noise power at the antenna, decibel difference from an
  *      arbitrary, fixed reference point.
  *
- * IEEE80211_RADIOTAP_LOCK_QUALITY      u_int16_t       unitless
+ * IEEE80211_RADIOTAP_LOCK_QUALITY      uint16_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    u_int16_t       unitless
+ * IEEE80211_RADIOTAP_TX_ATTENUATION    uint16_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 u_int16_t       decibels (dB)
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t        decibels (dB)
  *
  *      Transmit power expressed as decibel distance from max power
  *      set at factory calibration.  0 is max power.  Monotonically
@@ -149,19 +152,26 @@
  *      reference). This is the absolute power level measured at
  *      the antenna port.
  *
- * IEEE80211_RADIOTAP_FLAGS             u_int8_t        bitmap
+ * IEEE80211_RADIOTAP_FLAGS             uint8_t         bitmap
  *
  *      Properties of transmitted and received frames. See flags
  *      defined below.
  *
- * IEEE80211_RADIOTAP_ANTENNA           u_int8_t        antenna index
+ * IEEE80211_RADIOTAP_ANTENNA           uint8_t         antenna index
  *
  *      Unitless indication of the Rx/Tx antenna for this packet.
  *      The first antenna is antenna 0.
  *
- * IEEE80211_RADIOTAP_FCS           	u_int32_t       data
- *
- *	FCS from frame in network byte order.
+ * IEEE80211_RADIOTAP_XCHANNEL          uint32_t        bitmap
+ *                                      uint16_t        MHz
+ *                                      uint8_t         channel number
+ *                                      int8_t          .5 dBm
+ *
+ *      Extended channel specification: flags (see below) followed by
+ *      frequency in MHz, the corresponding IEEE channel number, and
+ *      finally the maximum regulatory transmit power cap in .5 dBm
+ *      units.  This property supersedes IEEE80211_RADIOTAP_CHANNEL
+ *      and only one of the two should be present.
  */
 enum ieee80211_radiotap_type {
 	IEEE80211_RADIOTAP_TSFT = 0,
@@ -178,19 +188,28 @@
 	IEEE80211_RADIOTAP_ANTENNA = 11,
 	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
 	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+	/* NB: gap for netbsd definitions */
+	IEEE80211_RADIOTAP_XCHANNEL = 18,
 	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_DYN	0x0400	/* Dynamic CCK-OFDM channel */
-#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+#define	IEEE80211_CHAN_TURBO	0x00010	/* Turbo channel */
+#define	IEEE80211_CHAN_CCK	0x00020	/* CCK channel */
+#define	IEEE80211_CHAN_OFDM	0x00040	/* OFDM channel */
+#define	IEEE80211_CHAN_2GHZ	0x00080	/* 2 GHz spectrum channel. */
+#define	IEEE80211_CHAN_5GHZ	0x00100	/* 5 GHz spectrum channel */
+#define	IEEE80211_CHAN_PASSIVE	0x00200	/* Only passive scan allowed */
+#define	IEEE80211_CHAN_DYN	0x00400	/* Dynamic CCK-OFDM channel */
+#define	IEEE80211_CHAN_GFSK	0x00800	/* GFSK channel (FHSS PHY) */
+#define	IEEE80211_CHAN_GSM	0x01000	/* 900 MHz spectrum channel */
+#define	IEEE80211_CHAN_STURBO	0x02000	/* 11a static turbo channel only */
+#define	IEEE80211_CHAN_HALF	0x04000	/* Half rate channel */
+#define	IEEE80211_CHAN_QUARTER	0x08000	/* Quarter rate channel */
+#define	IEEE80211_CHAN_HT20	0x10000	/* HT 20 channel */
+#define	IEEE80211_CHAN_HT40U	0x20000	/* HT 40 channel w/ ext above */
+#define	IEEE80211_CHAN_HT40D	0x40000	/* HT 40 channel w/ ext below */
 #endif /* !_KERNEL */
 
 /* For IEEE80211_RADIOTAP_FLAGS */
@@ -213,5 +232,6 @@
 						 * (to 32-bit boundary)
 						 */
 #define	IEEE80211_RADIOTAP_F_BADFCS	0x40	/* does not pass FCS check */
+#define	IEEE80211_RADIOTAP_F_SHORTGI	0x80	/* HT short GI */
 
-#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */
+#endif /* !_NET80211_IEEE80211_RADIOTAP_H_ */
Index: ieee80211_ioctl.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_ioctl.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_ioctl.h -L sys/net80211/ieee80211_ioctl.h -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_ioctl.h
+++ sys/net80211/ieee80211_ioctl.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,7 +23,7 @@
  * (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.10.2.4 2005/12/22 19:18:23 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.24.2.2 2007/11/11 17:44:36 sam Exp $
  */
 #ifndef _NET80211_IEEE80211_IOCTL_H_
 #define _NET80211_IEEE80211_IOCTL_H_
@@ -42,146 +36,179 @@
 #include <net80211/ieee80211_crypto.h>
 
 /*
- * Per/node (station) statistics available when operating as an AP.
+ * Per/node (station) statistics.
  */
 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 */
+	uint32_t	ns_rx_data;		/* rx data frames */
+	uint32_t	ns_rx_mgmt;		/* rx management frames */
+	uint32_t	ns_rx_ctrl;		/* rx control frames */
+	uint32_t	ns_rx_ucast;		/* rx unicast frames */
+	uint32_t	ns_rx_mcast;		/* rx multi/broadcast frames */
+	uint64_t	ns_rx_bytes;		/* rx data count (bytes) */
+	uint64_t	ns_rx_beacons;		/* rx beacon frames */
+	uint32_t	ns_rx_proberesp;	/* rx probe response frames */
+
+	uint32_t	ns_rx_dup;		/* rx discard 'cuz dup */
+	uint32_t	ns_rx_noprivacy;	/* rx w/ wep but privacy off */
+	uint32_t	ns_rx_wepfail;		/* rx wep processing failed */
+	uint32_t	ns_rx_demicfail;	/* rx demic failed */
+	uint32_t	ns_rx_decap;		/* rx decapsulation failed */
+	uint32_t	ns_rx_defrag;		/* rx defragmentation failed */
+	uint32_t	ns_rx_disassoc;		/* rx disassociation */
+	uint32_t	ns_rx_deauth;		/* rx deauthentication */
+	uint32_t	ns_rx_action;		/* rx action */
+	uint32_t	ns_rx_decryptcrc;	/* rx decrypt failed on crc */
+	uint32_t	ns_rx_unauth;		/* rx on unauthorized port */
+	uint32_t	ns_rx_unencrypted;	/* rx unecrypted w/ privacy */
+	uint32_t	ns_rx_drop;		/* rx discard other reason */
+
+	uint32_t	ns_tx_data;		/* tx data frames */
+	uint32_t	ns_tx_mgmt;		/* tx management frames */
+	uint32_t	ns_tx_ucast;		/* tx unicast frames */
+	uint32_t	ns_tx_mcast;		/* tx multi/broadcast frames */
+	uint64_t	ns_tx_bytes;		/* tx data count (bytes) */
+	uint32_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 */
+	uint32_t	ns_tx_novlantag;	/* tx discard 'cuz no tag */
+	uint32_t	ns_tx_vlanmismatch;	/* tx discard 'cuz bad tag */
 
-	u_int32_t	ns_ps_discard;		/* ps discard 'cuz of age */
+	uint32_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 */
+	uint32_t	ns_tx_assoc;		/* [re]associations */
+	uint32_t	ns_tx_assoc_fail;	/* [re]association failures */
+	uint32_t	ns_tx_auth;		/* [re]authentications */
+	uint32_t	ns_tx_auth_fail;	/* [re]authentication failures*/
+	uint32_t	ns_tx_deauth;		/* deauthentications */
+	uint32_t	ns_tx_deauth_code;	/* last deauth reason */
+	uint32_t	ns_tx_disassoc;		/* disassociations */
+	uint32_t	ns_tx_disassoc_code;	/* last disassociation reason */
 };
 
 /*
  * Summary statistics.
  */
 struct ieee80211_stats {
-	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_tx_fragframes;	/* tx frames fragmented */
-	u_int32_t	is_tx_frags;		/* tx fragments created */
-	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 */
-	u_int32_t	is_ff_badhdr;		/* fast frame rx'd w/ bad hdr */
-	u_int32_t	is_ff_tooshort;		/* fast frame rx decap error */
-	u_int32_t	is_ff_split;		/* fast frame rx split error */
-	u_int32_t	is_ff_decap;		/* fast frames decap'd */
-	u_int32_t	is_ff_encap;		/* fast frames encap'd for tx */
-	u_int32_t	is_rx_badbintval;	/* rx frame w/ bogus bintval */
-	u_int32_t	is_spare[9];
+	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_noprivacy;	/* rx w/ wep but privacy off */
+	uint32_t	is_rx_unencrypted;	/* rx w/o wep and privacy on */
+	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_beacon;		/* rx beacon 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_auth_countermeasures;/* rx auth discard 'cuz CM */
+	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_assoc_badwpaie;	/* rx assoc w/ bad WPA IE */
+	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_nobuf;		/* rx failed for lack of buf */
+	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_rx_unauth;		/* rx on unauthorized port */
+	uint32_t	is_rx_badkeyid;		/* rx w/ incorrect keyid */
+	uint32_t	is_rx_ccmpreplay;	/* rx seq# violation (CCMP) */
+	uint32_t	is_rx_ccmpformat;	/* rx format bad (CCMP) */
+	uint32_t	is_rx_ccmpmic;		/* rx MIC check failed (CCMP) */
+	uint32_t	is_rx_tkipreplay;	/* rx seq# violation (TKIP) */
+	uint32_t	is_rx_tkipformat;	/* rx format bad (TKIP) */
+	uint32_t	is_rx_tkipmic;		/* rx MIC check failed (TKIP) */
+	uint32_t	is_rx_tkipicv;		/* rx ICV check failed (TKIP) */
+	uint32_t	is_rx_badcipher;	/* rx failed 'cuz key type */
+	uint32_t	is_rx_nocipherctx;	/* rx failed 'cuz key !setup */
+	uint32_t	is_rx_acl;		/* rx discard 'cuz acl policy */
+	uint32_t	is_tx_nobuf;		/* tx failed for lack of buf */
+	uint32_t	is_tx_nonode;		/* tx failed for no node */
+	uint32_t	is_tx_unknownmgt;	/* tx of unknown mgt frame */
+	uint32_t	is_tx_badcipher;	/* tx failed 'cuz key type */
+	uint32_t	is_tx_nodefkey;		/* tx failed 'cuz no defkey */
+	uint32_t	is_tx_noheadroom;	/* tx failed 'cuz no space */
+	uint32_t	is_tx_fragframes;	/* tx frames fragmented */
+	uint32_t	is_tx_frags;		/* tx fragments created */
+	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 */
+	uint32_t	is_crypto_tkip;		/* tkip crypto done in s/w */
+	uint32_t	is_crypto_tkipenmic;	/* tkip en-MIC done in s/w */
+	uint32_t	is_crypto_tkipdemic;	/* tkip de-MIC done in s/w */
+	uint32_t	is_crypto_tkipcm;	/* tkip counter measures */
+	uint32_t	is_crypto_ccmp;		/* ccmp crypto done in s/w */
+	uint32_t	is_crypto_wep;		/* wep crypto done in s/w */
+	uint32_t	is_crypto_setkey_cipher;/* cipher rejected key */
+	uint32_t	is_crypto_setkey_nokey;	/* no key index for setkey */
+	uint32_t	is_crypto_delkey;	/* driver key delete failed */
+	uint32_t	is_crypto_badcipher;	/* unknown cipher */
+	uint32_t	is_crypto_nocipher;	/* cipher not available */
+	uint32_t	is_crypto_attachfail;	/* cipher attach failed */
+	uint32_t	is_crypto_swfallback;	/* cipher fallback to s/w */
+	uint32_t	is_crypto_keyfail;	/* driver key alloc failed */
+	uint32_t	is_crypto_enmicfail;	/* en-MIC failed */
+	uint32_t	is_ibss_capmismatch;	/* merge failed-cap mismatch */
+	uint32_t	is_ibss_norate;		/* merge failed-rate mismatch */
+	uint32_t	is_ps_unassoc;		/* ps-poll for unassoc. sta */
+	uint32_t	is_ps_badaid;		/* ps-poll w/ incorrect aid */
+	uint32_t	is_ps_qempty;		/* ps-poll w/ nothing to send */
+	uint32_t	is_ff_badhdr;		/* fast frame rx'd w/ bad hdr */
+	uint32_t	is_ff_tooshort;		/* fast frame rx decap error */
+	uint32_t	is_ff_split;		/* fast frame rx split error */
+	uint32_t	is_ff_decap;		/* fast frames decap'd */
+	uint32_t	is_ff_encap;		/* fast frames encap'd for tx */
+	uint32_t	is_rx_badbintval;	/* rx frame w/ bogus bintval */
+	uint32_t	is_rx_demicfail;	/* rx demic failed */
+	uint32_t	is_rx_defrag;		/* rx defragmentation failed */
+	uint32_t	is_rx_mgmt;		/* rx management frames */
+	uint32_t	is_rx_action;		/* rx action mgt frames */
+	uint32_t	is_amsdu_tooshort;	/* A-MSDU rx decap error */
+	uint32_t	is_amsdu_split;		/* A-MSDU rx split error */
+	uint32_t	is_amsdu_decap;		/* A-MSDU decap'd */
+	uint32_t	is_amsdu_encap;		/* A-MSDU encap'd for tx */
+	uint32_t	is_ampdu_bar_bad;	/* A-MPDU BAR out of window */
+	uint32_t	is_ampdu_bar_oow;	/* A-MPDU BAR before ADDBA */
+	uint32_t	is_ampdu_bar_move;	/* A-MPDU BAR moved window */
+	uint32_t	is_ampdu_bar_rx;	/* A-MPDU BAR frames handled */
+	uint32_t	is_ampdu_rx_flush;	/* A-MPDU frames flushed */
+	uint32_t	is_ampdu_rx_oor;	/* A-MPDU frames out-of-order */
+	uint32_t	is_ampdu_rx_copy;	/* A-MPDU frames copied down */
+	uint32_t	is_ampdu_rx_drop;	/* A-MPDU frames dropped */
+	uint32_t	is_tx_badstate;		/* tx discard state != RUN */
+	uint32_t	is_tx_notassoc;		/* tx failed, sta not assoc */
+	uint32_t	is_tx_classify;		/* tx classification failed */
+	uint32_t	is_ht_assoc_nohtcap;	/* non-HT sta rejected */
+	uint32_t	is_ht_assoc_downgrade;	/* HT sta forced to legacy */
+	uint32_t	is_ht_assoc_norate;	/* HT assoc w/ rate mismatch */
+	uint32_t	is_ampdu_rx_age;	/* A-MPDU sent up 'cuz of age */
+	uint32_t	is_ampdu_rx_move;	/* A-MPDU MSDU moved window */
+	uint32_t	is_addba_reject;	/* ADDBA reject 'cuz disabled */
+	uint32_t	is_addba_norequest;	/* ADDBA response w/o ADDBA */
+	uint32_t	is_addba_badtoken;	/* ADDBA response w/ wrong
+						   dialogtoken */
+	uint32_t	is_ampdu_stop;		/* A-MPDU stream stopped */
+	uint32_t	is_ampdu_stop_failed;	/* A-MPDU stream not running */
+	uint32_t	is_ampdu_rx_reorder;	/* A-MPDU held for rx reorder */
+	uint32_t	is_spare[16];
 };
 
 /*
@@ -199,20 +226,20 @@
  * 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.
+ * 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;
+	uint8_t		ik_type;	/* key/cipher type */
+	uint8_t		ik_pad;
+	uint16_t	ik_keyix;	/* key index */
+	uint8_t		ik_keylen;	/* key length in bytes */
+	uint8_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];
+	uint8_t		ik_macaddr[IEEE80211_ADDR_LEN];
+	uint64_t	ik_keyrsc;	/* key receive sequence counter */
+	uint64_t	ik_keytsc;	/* key transmit sequence counter */
+	uint8_t		ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
 };
 
 /*
@@ -220,8 +247,8 @@
  * 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];
+	uint8_t		idk_keyix;	/* key index */
+	uint8_t		idk_macaddr[IEEE80211_ADDR_LEN];
 };
 
 /*
@@ -231,16 +258,16 @@
  * ap (to effect a station).
  */
 struct ieee80211req_mlme {
-	u_int8_t	im_op;		/* operation to perform */
+	uint8_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_int8_t	im_ssid_len;	/* length of optional ssid */
-	u_int16_t	im_reason;	/* 802.11 reason code */
-	u_int8_t	im_macaddr[IEEE80211_ADDR_LEN];
-	u_int8_t	im_ssid[IEEE80211_NWID_LEN];
+	uint8_t		im_ssid_len;	/* length of optional ssid */
+	uint16_t	im_reason;	/* 802.11 reason code */
+	uint8_t		im_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t		im_ssid[IEEE80211_NWID_LEN];
 };
 
 /* 
@@ -257,7 +284,7 @@
 };
 
 struct ieee80211req_maclist {
-	u_int8_t	ml_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t		ml_macaddr[IEEE80211_ADDR_LEN];
 };
 
 /*
@@ -267,7 +294,7 @@
  * scanning.
  */
 struct ieee80211req_chanlist {
-	u_int8_t	ic_channels[IEEE80211_CHAN_BYTES];
+	uint8_t		ic_channels[IEEE80211_CHAN_BYTES];
 };
 
 /*
@@ -281,9 +308,14 @@
 /*
  * 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];
+struct ieee80211req_wpaie {	/* old version w/ only one ie */
+	uint8_t		wpa_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t		wpa_ie[IEEE80211_MAX_OPT_IE];
+};
+struct ieee80211req_wpaie2 {
+	uint8_t		wpa_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t		wpa_ie[IEEE80211_MAX_OPT_IE];
+	uint8_t		rsn_ie[IEEE80211_MAX_OPT_IE];
 };
 
 /*
@@ -292,8 +324,8 @@
 struct ieee80211req_sta_stats {
 	union {
 		/* NB: explicitly force 64-bit alignment */
-		u_int8_t	macaddr[IEEE80211_ADDR_LEN];
-		u_int64_t	pad;
+		uint8_t		macaddr[IEEE80211_ADDR_LEN];
+		uint64_t	pad;
 	} is_u;
 	struct ieee80211_nodestats is_stats;
 };
@@ -303,26 +335,29 @@
  * 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;
+	uint16_t	isi_len;		/* length (mult of 4) */
+	uint16_t	isi_ie_off;		/* offset to IE data */
+	uint16_t	isi_ie_len;		/* IE length */
+	uint16_t	isi_freq;		/* MHz */
+	uint32_t	isi_flags;		/* channel flags */
+	uint16_t	isi_state;		/* state flags */
+	uint8_t		isi_authmode;		/* authentication algorithm */
+	int8_t		isi_rssi;		/* receive signal strength */
+	int8_t		isi_noise;		/* noise floor */
+	uint8_t		isi_capinfo;		/* capabilities */
+	uint8_t		isi_erp;		/* ERP element */
+	uint8_t		isi_macaddr[IEEE80211_ADDR_LEN];
+	uint8_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 */
+	uint8_t		isi_rates[IEEE80211_RATE_MAXSIZE];
+	uint8_t		isi_txrate;		/* index to isi_rates[] */
+	uint16_t	isi_associd;		/* assoc response */
+	uint16_t	isi_txpower;		/* current tx power */
+	uint16_t	isi_vlan;		/* vlan tag */
+	/* NB: [IEEE80211_NONQOS_TID] holds seq#'s for non-QoS stations */
+	uint16_t	isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */
+	uint16_t	isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */
+	uint16_t	isi_inact;		/* inactivity timer */
 	/* XXX frag state? */
 	/* variable length IE data */
 };
@@ -334,8 +369,8 @@
 struct ieee80211req_sta_req {
 	union {
 		/* NB: explicitly force 64-bit alignment */
-		u_int8_t	macaddr[IEEE80211_ADDR_LEN];
-		u_int64_t	pad;
+		uint8_t		macaddr[IEEE80211_ADDR_LEN];
+		uint64_t	pad;
 	} is_u;
 	struct ieee80211req_sta_info info[1];	/* variable length */
 };
@@ -344,8 +379,8 @@
  * Get/set per-station tx power cap.
  */
 struct ieee80211req_sta_txpow {
-	u_int8_t	it_macaddr[IEEE80211_ADDR_LEN];
-	u_int8_t	it_txpow;
+	uint8_t		it_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t		it_txpow;
 };
 
 /*
@@ -365,13 +400,14 @@
 /* the first member must be matched with struct ifreq */
 struct ieee80211req {
 	char		i_name[IFNAMSIZ];	/* if_name, e.g. "wi0" */
-	u_int16_t	i_type;			/* req type */
+	uint16_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 */
 };
 #define	SIOCS80211		 _IOW('i', 234, struct ieee80211req)
 #define	SIOCG80211		_IOWR('i', 235, struct ieee80211req)
+#define	SIOCG80211STATS		_IOWR('i', 236, struct ifreq)
 
 #define IEEE80211_IOC_SSID		1
 #define IEEE80211_IOC_NUMSSIDS		2
@@ -409,7 +445,7 @@
 #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
+/* 24 was IEEE80211_IOC_SCAN_RESULTS */
 #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 */
@@ -430,7 +466,7 @@
 #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 */
+/* 45 was IEEE80211_IOC_STA_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 */
@@ -442,32 +478,72 @@
 #define	IEEE80211_IOC_ADDMAC		54	/* add sta to MAC ACL table */
 #define	IEEE80211_IOC_DELMAC		55	/* del sta from MAC ACL table */
 #define	IEEE80211_IOC_PUREG		56	/* pure 11g (no 11b stations) */
+#define	IEEE80211_IOC_FF		57	/* ATH fast frames (on, off) */
+#define	IEEE80211_IOC_TURBOP		58	/* ATH turbo' (on, off) */
+#define	IEEE80211_IOC_BGSCAN		59	/* bg scanning (on, off) */
+#define	IEEE80211_IOC_BGSCAN_IDLE	60	/* bg scan idle threshold */
+#define	IEEE80211_IOC_BGSCAN_INTERVAL	61	/* bg scan interval */
+#define	IEEE80211_IOC_SCANVALID		65	/* scan cache valid threshold */
+#define	IEEE80211_IOC_ROAM_RSSI_11A	66	/* rssi threshold in 11a */
+#define	IEEE80211_IOC_ROAM_RSSI_11B	67	/* rssi threshold in 11b */
+#define	IEEE80211_IOC_ROAM_RSSI_11G	68	/* rssi threshold in 11g */
+#define	IEEE80211_IOC_ROAM_RATE_11A	69	/* tx rate threshold in 11a */
+#define	IEEE80211_IOC_ROAM_RATE_11B	70	/* tx rate threshold in 11b */
+#define	IEEE80211_IOC_ROAM_RATE_11G	71	/* tx rate threshold in 11g */
 #define	IEEE80211_IOC_MCAST_RATE	72	/* tx rate for mcast frames */
 #define	IEEE80211_IOC_FRAGTHRESHOLD	73	/* tx fragmentation threshold */
 #define	IEEE80211_IOC_BURST		75	/* packet bursting */
+#define	IEEE80211_IOC_SCAN_RESULTS	76	/* get scan results */
+#define	IEEE80211_IOC_BMISSTHRESHOLD	77	/* beacon miss threshold */
+#define	IEEE80211_IOC_STA_INFO		78	/* station/neighbor info */
+#define	IEEE80211_IOC_WPAIE2		79	/* WPA+RSN info elements */
+#define	IEEE80211_IOC_CURCHAN		80	/* current channel */
+#define	IEEE80211_IOC_SHORTGI		81	/* 802.11n half GI */
+#define	IEEE80211_IOC_AMPDU		82	/* 802.11n A-MPDU (on, off) */
+#define	IEEE80211_IOC_AMPDU_LIMIT	83	/* A-MPDU length limit */
+#define	IEEE80211_IOC_AMPDU_DENSITY	84	/* A-MPDU density */
+#define	IEEE80211_IOC_AMSDU		85	/* 802.11n A-MSDU (on, off) */
+#define	IEEE80211_IOC_AMSDU_LIMIT	86	/* A-MSDU length limit */
+#define	IEEE80211_IOC_PUREN		87	/* pure 11n (no legacy sta's) */
+#define	IEEE80211_IOC_DOTH		88	/* 802.11h (on, off) */
+#define	IEEE80211_IOC_REGDOMAIN		89	/* regulatory domain */
+#define	IEEE80211_IOC_COUNTRYCODE	90	/* ISO country code */
+#define	IEEE80211_IOC_LOCATION		91	/* indoor/outdoor/anywhere */
+#define	IEEE80211_IOC_HTCOMPAT		92	/* support pre-D1.10 HT ie's */
+#define	IEEE80211_IOC_INACTIVITY	94	/* sta inactivity handling */
+#define IEEE80211_IOC_HTPROTMODE	102	/* HT protection (off, rts) */
+#define	IEEE80211_IOC_HTCONF		105	/* HT config (off, HT20, HT40)*/
 
 /*
  * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
+ * Each result is a fixed size structure followed by a variable
+ * length SSID and one or more variable length information elements.
+ * The size of each variable length item is found in the fixed
+ * size structure and the entire length of the record is specified
+ * in isr_len.  Result records are rounded to a multiple of 4 bytes.
  */
 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];
+	uint16_t	isr_len;		/* length (mult of 4) */
+	uint16_t	isr_ie_off;		/* offset to IE data */
+	uint16_t	isr_ie_len;		/* IE length */
+	uint16_t	isr_freq;		/* MHz */
+	uint16_t	isr_flags;		/* channel flags */
+	int8_t		isr_noise;
+	int8_t		isr_rssi;
+	uint8_t		isr_intval;		/* beacon interval */
+	uint8_t		isr_capinfo;		/* capabilities */
+	uint8_t		isr_erp;		/* ERP element */
+	uint8_t		isr_bssid[IEEE80211_ADDR_LEN];
+	uint8_t		isr_nrates;
+	uint8_t		isr_rates[IEEE80211_RATE_MAXSIZE];
+	uint8_t		isr_ssid_len;		/* SSID length */
 	/* variable length SSID followed by IE data */
 };
 
-#define	SIOCG80211STATS		_IOWR('i', 236, struct ifreq)
+struct ieee80211_clone_params {
+	char	icp_parent[IFNAMSIZ];		/* parent device */
+	int	icp_opmode;			/* operating mode */
+};
 #endif /* __FreeBSD__ */
 
 #endif /* _NET80211_IEEE80211_IOCTL_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_scan.c
@@ -0,0 +1,994 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_scan.c,v 1.3.2.1 2007/10/31 16:53:44 sam Exp $");
+
+/*
+ * IEEE 802.11 scanning support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/kernel.h>
+ 
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+struct scan_state {
+	struct ieee80211_scan_state base;	/* public state */
+
+	u_int		ss_iflags;		/* flags used internally */
+#define	ISCAN_MINDWELL 	0x0001		/* min dwell time reached */
+#define	ISCAN_DISCARD	0x0002		/* discard rx'd frames */
+#define	ISCAN_CANCEL	0x0004		/* cancel current scan */
+#define	ISCAN_START	0x0008		/* 1st time through next_scan */
+	unsigned long	ss_chanmindwell;	/* min dwell on curchan */
+	unsigned long	ss_scanend;		/* time scan must stop */
+	u_int		ss_duration;		/* duration for next scan */
+	struct callout	ss_scan_timer;		/* scan timer */
+};
+#define	SCAN_PRIVATE(ss)	((struct scan_state *) ss)
+
+/*
+ * Amount of time to go off-channel during a background
+ * scan.  This value should be large enough to catch most
+ * ap's but short enough that we can return on-channel
+ * before our listen interval expires.
+ *
+ * XXX tunable
+ * XXX check against configured listen interval
+ */
+#define	IEEE80211_SCAN_OFFCHANNEL	msecs_to_ticks(150)
+
+/*
+ * Roaming-related defaults.  RSSI thresholds are as returned by the
+ * driver (dBm).  Transmit rate thresholds are IEEE rate codes (i.e
+ * .5M units).
+ */
+#define	ROAM_RSSI_11A_DEFAULT		14	/* rssi threshold for 11a bss */
+#define	ROAM_RSSI_11B_DEFAULT		14	/* rssi threshold for 11b bss */
+#define	ROAM_RSSI_11BONLY_DEFAULT	14	/* rssi threshold for 11b-only bss */
+#define	ROAM_RATE_11A_DEFAULT		2*12	/* tx rate thresh for 11a bss */
+#define	ROAM_RATE_11B_DEFAULT		2*5	/* tx rate thresh for 11b bss */
+#define	ROAM_RATE_11BONLY_DEFAULT	2*1	/* tx rate thresh for 11b-only bss */
+
+static	void scan_restart_pwrsav(void *);
+static	void scan_curchan(struct ieee80211com *, unsigned long);
+static	void scan_mindwell(struct ieee80211com *);
+static	void scan_next(void *);
+
+MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
+
+void
+ieee80211_scan_attach(struct ieee80211com *ic)
+{
+	struct scan_state *ss;
+
+	ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+	MALLOC(ss, struct scan_state *, sizeof(struct scan_state),
+		M_80211_SCAN, M_NOWAIT | M_ZERO);
+	if (ss == NULL) {
+		ic->ic_scan = NULL;
+		return;
+	}
+	callout_init(&ss->ss_scan_timer, CALLOUT_MPSAFE);
+	ic->ic_scan = &ss->base;
+
+	ic->ic_scan_curchan = scan_curchan;
+	ic->ic_scan_mindwell = scan_mindwell;
+
+	ic->ic_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
+	ic->ic_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
+	ic->ic_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
+	ic->ic_roam.rssi11a = ROAM_RSSI_11A_DEFAULT;
+	ic->ic_roam.rssi11b = ROAM_RSSI_11B_DEFAULT;
+	ic->ic_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT;
+	ic->ic_roam.rate11a = ROAM_RATE_11A_DEFAULT;
+	ic->ic_roam.rate11b = ROAM_RATE_11B_DEFAULT;
+	ic->ic_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT;
+}
+
+void
+ieee80211_scan_detach(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	if (ss != NULL) {
+		callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer);
+		if (ss->ss_ops != NULL) {
+			ss->ss_ops->scan_detach(ss);
+			ss->ss_ops = NULL;
+		}
+		ic->ic_flags &= ~IEEE80211_F_SCAN;
+		ic->ic_scan = NULL;
+		FREE(SCAN_PRIVATE(ss), M_80211_SCAN);
+	}
+}
+
+/*
+ * Simple-minded scanner module support.
+ */
+#define	IEEE80211_SCANNER_MAX	(IEEE80211_M_MONITOR+1)
+
+static const char *scan_modnames[IEEE80211_SCANNER_MAX] = {
+	"wlan_scan_sta",	/* IEEE80211_M_IBSS */
+	"wlan_scan_sta",	/* IEEE80211_M_STA */
+	"wlan_scan_wds",	/* IEEE80211_M_WDS */
+	"wlan_scan_sta",	/* IEEE80211_M_AHDEMO */
+	"wlan_scan_4",		/* n/a */
+	"wlan_scan_5",		/* n/a */
+	"wlan_scan_ap",		/* IEEE80211_M_HOSTAP */
+	"wlan_scan_7",		/* n/a */
+	"wlan_scan_monitor",	/* IEEE80211_M_MONITOR */
+};
+static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX];
+
+const struct ieee80211_scanner *
+ieee80211_scanner_get(enum ieee80211_opmode mode)
+{
+	if (mode >= IEEE80211_SCANNER_MAX)
+		return NULL;
+	/* NB: avoid monitor mode; there is no scan support */
+	if (mode != IEEE80211_M_MONITOR && scanners[mode] == NULL)
+		ieee80211_load_module(scan_modnames[mode]);
+	return scanners[mode];
+}
+
+void
+ieee80211_scanner_register(enum ieee80211_opmode mode,
+	const struct ieee80211_scanner *scan)
+{
+	if (mode >= IEEE80211_SCANNER_MAX)
+		return;
+	scanners[mode] = scan;
+}
+
+void
+ieee80211_scanner_unregister(enum ieee80211_opmode mode,
+	const struct ieee80211_scanner *scan)
+{
+	if (mode >= IEEE80211_SCANNER_MAX)
+		return;
+	if (scanners[mode] == scan)
+		scanners[mode] = NULL;
+}
+
+void
+ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
+{
+	int m;
+
+	for (m = 0; m < IEEE80211_SCANNER_MAX; m++)
+		if (scanners[m] == scan)
+			scanners[m] = NULL;
+}
+
+/*
+ * Update common scanner state to reflect the current
+ * operating mode.  This is called when the state machine
+ * is transitioned to RUN state w/o scanning--e.g. when
+ * operating in monitor mode.  The purpose of this is to
+ * ensure later callbacks find ss_ops set to properly
+ * reflect current operating mode.
+ */
+int
+ieee80211_scan_update(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+	const struct ieee80211_scanner *scan;
+
+	scan = ieee80211_scanner_get(ic->ic_opmode);
+	IEEE80211_LOCK(ic);
+	if (scan == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: no scanner support for mode %u\n",
+		    __func__, ic->ic_opmode);
+		/* XXX stat */
+	}
+	ss->ss_ic = ic;
+	if (ss->ss_ops != scan) {
+		/* switch scanners; detach old, attach new */
+		if (ss->ss_ops != NULL)
+			ss->ss_ops->scan_detach(ss);
+		if (scan != NULL && !scan->scan_attach(ss)) {
+			/* XXX attach failure */
+			/* XXX stat+msg */
+			ss->ss_ops = NULL;
+		} else
+			ss->ss_ops = scan;
+	}
+	IEEE80211_UNLOCK(ic);
+
+	return (scan != NULL);
+}
+
+static void
+change_channel(struct ieee80211com *ic,
+	struct ieee80211_channel *chan)
+{
+	ic->ic_curchan = chan;
+	ic->ic_set_channel(ic);
+}
+
+static char
+channel_type(const struct ieee80211_channel *c)
+{
+	if (IEEE80211_IS_CHAN_ST(c))
+		return 'S';
+	if (IEEE80211_IS_CHAN_108A(c))
+		return 'T';
+	if (IEEE80211_IS_CHAN_108G(c))
+		return 'G';
+	if (IEEE80211_IS_CHAN_HT(c))
+		return 'n';
+	if (IEEE80211_IS_CHAN_A(c))
+		return 'a';
+	if (IEEE80211_IS_CHAN_ANYG(c))
+		return 'g';
+	if (IEEE80211_IS_CHAN_B(c))
+		return 'b';
+	return 'f';
+}
+
+void
+ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
+{
+	struct ieee80211com *ic = ss->ss_ic;
+	const char *sep;
+	int i;
+
+	sep = "";
+	for (i = ss->ss_next; i < ss->ss_last; i++) {
+		const struct ieee80211_channel *c = ss->ss_chans[i];
+
+		printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c),
+			channel_type(c));
+		sep = ", ";
+	}
+}
+
+/*
+ * Enable station power save mode and start/restart the scanning thread.
+ */
+static void
+scan_restart_pwrsav(void *arg)
+{
+	struct scan_state *ss = (struct scan_state *) arg;
+	struct ieee80211com *ic = ss->base.ss_ic;
+	int delay;
+
+	ieee80211_sta_pwrsave(ic, 1);
+	/*
+	 * Use an initial 1ms delay to insure the null
+	 * data frame has a chance to go out.
+	 * XXX 1ms is a lot, better to trigger scan
+	 * on tx complete.
+	 */
+	delay = hz/1000;
+	if (delay < 1)
+		delay = 1;
+	ic->ic_scan_start(ic);			/* notify driver */
+	ss->ss_scanend = ticks + delay + ss->ss_duration;
+	ss->ss_iflags |= ISCAN_START;
+	callout_reset(&ss->ss_scan_timer, delay, scan_next, ss);
+}
+
+/*
+ * Start/restart scanning.  If we're operating in station mode
+ * and associated notify the ap we're going into power save mode
+ * and schedule a callback to initiate the work (where there's a
+ * better context for doing the work).  Otherwise, start the scan
+ * directly.
+ */
+static int
+scan_restart(struct scan_state *ss, u_int duration)
+{
+	struct ieee80211com *ic = ss->base.ss_ic;
+	int defer = 0;
+
+	if (ss->base.ss_next == ss->base.ss_last) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			"%s: no channels to scan\n", __func__);
+		return 0;
+	}
+	if (ic->ic_opmode == IEEE80211_M_STA &&
+	    ic->ic_state == IEEE80211_S_RUN) {
+		if ((ic->ic_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+			/*
+			 * Initiate power save before going off-channel.
+			 * Note that we cannot do this directly because
+			 * of locking issues; instead we defer it to a
+			 * tasklet.
+			 */
+			ss->ss_duration = duration;
+			defer = 1;
+		}
+	}
+
+	if (!defer) {
+		ic->ic_scan_start(ic);		/* notify driver */
+		ss->ss_scanend = ticks + duration;
+		ss->ss_iflags |= ISCAN_START;
+		callout_reset(&ss->ss_scan_timer, 0, scan_next, ss);
+	} else
+		scan_restart_pwrsav(ss);
+	return 1;
+}
+
+static void
+copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
+	int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+	if (nssid > IEEE80211_SCAN_MAX_SSID) {
+		/* XXX printf */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: too many ssid %d, ignoring all of them\n",
+		    __func__, nssid);
+		return;
+	}
+	memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0]));
+	ss->ss_nssid = nssid;
+}
+
+/*
+ * Start a scan unless one is already going.
+ */
+int
+ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
+	u_int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+	const struct ieee80211_scanner *scan;
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	scan = ieee80211_scanner_get(ic->ic_opmode);
+	if (scan == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: no scanner support for mode %u\n",
+		    __func__, ic->ic_opmode);
+		/* XXX stat */
+		return 0;
+	}
+
+	IEEE80211_LOCK(ic);
+	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+		    , __func__
+		    , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
+		    , duration
+		    , ieee80211_phymode_name[ic->ic_des_mode]
+		    , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
+		    , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+		    , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
+		    , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
+		);
+
+		ss->ss_ic = ic;
+		if (ss->ss_ops != scan) {
+			/* switch scanners; detach old, attach new */
+			if (ss->ss_ops != NULL)
+				ss->ss_ops->scan_detach(ss);
+			if (!scan->scan_attach(ss)) {
+				/* XXX attach failure */
+				/* XXX stat+msg */
+				ss->ss_ops = NULL;
+			} else
+				ss->ss_ops = scan;
+		}
+		if (ss->ss_ops != NULL) {
+			if ((flags & IEEE80211_SCAN_NOSSID) == 0)
+				copy_ssid(ic, ss, nssid, ssids);
+
+			/* NB: top 4 bits for internal use */
+			ss->ss_flags = flags & 0xfff;
+			if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+				ic->ic_stats.is_scan_active++;
+			else
+				ic->ic_stats.is_scan_passive++;
+			if (flags & IEEE80211_SCAN_FLUSH)
+				ss->ss_ops->scan_flush(ss);
+
+			/* NB: flush frames rx'd before 1st channel change */
+			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+			ss->ss_ops->scan_start(ss, ic);
+			if (scan_restart(SCAN_PRIVATE(ss), duration))
+				ic->ic_flags |= IEEE80211_F_SCAN;
+		}
+	} else {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: %s scan already in progress\n", __func__,
+		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
+	}
+	IEEE80211_UNLOCK(ic);
+
+	/* NB: racey, does it matter? */
+	return (ic->ic_flags & IEEE80211_F_SCAN);
+}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that
+ * fails then kick off a new scan.
+ */
+int
+ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
+	u_int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+	int checkscanlist = 0;
+
+	/*
+	 * Check if there's a list of scan candidates already.
+	 * XXX want more than the ap we're currently associated with
+	 */
+
+	IEEE80211_LOCK(ic);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+	    "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+	    , __func__
+	    , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
+	    , duration
+	    , ieee80211_phymode_name[ic->ic_des_mode]
+	    , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
+	    , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+	    , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
+	    , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
+	);
+
+	if (ss->ss_ops != NULL) {
+		/* XXX verify ss_ops matches ic->ic_opmode */
+		if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
+			/*
+			 * Update the ssid list and mark flags so if
+			 * we call start_scan it doesn't duplicate work.
+			 */
+			copy_ssid(ic, ss, nssid, ssids);
+			flags |= IEEE80211_SCAN_NOSSID;
+		}
+		if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
+		     time_before(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+			/*
+			 * We're not currently scanning and the cache is
+			 * deemed hot enough to consult.  Lock out others
+			 * by marking IEEE80211_F_SCAN while we decide if
+			 * something is already in the scan cache we can
+			 * use.  Also discard any frames that might come
+			 * in while temporarily marked as scanning.
+			 */
+			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+			ic->ic_flags |= IEEE80211_F_SCAN;
+			checkscanlist = 1;
+		}
+	}
+	IEEE80211_UNLOCK(ic);
+	if (checkscanlist) {
+		const struct ieee80211_scanner *scan;
+
+		scan = ieee80211_scanner_get(ic->ic_opmode);
+		if (scan == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			    "%s: no scanner support for mode %u\n",
+			    __func__, ic->ic_opmode);
+			/* XXX stat */
+			return 0;
+		}
+		if (scan == ss->ss_ops && ss->ss_ops->scan_end(ss, ic)) {
+			/* found an ap, just clear the flag */
+			ic->ic_flags &= ~IEEE80211_F_SCAN;
+			return 1;
+		}
+		/* no ap, clear the flag before starting a scan */
+		ic->ic_flags &= ~IEEE80211_F_SCAN;
+	}
+	return ieee80211_start_scan(ic, flags, duration, nssid, ssids);
+}
+
+/*
+ * Restart a previous scan.  If the previous scan completed
+ * then we start again using the existing channel list.
+ */
+int
+ieee80211_bg_scan(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	IEEE80211_LOCK(ic);
+	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+		u_int duration;
+		/*
+		 * Go off-channel for a fixed interval that is large
+		 * enough to catch most ap's but short enough that
+		 * we can return on-channel before our listen interval
+		 * expires.
+		 */
+		duration = IEEE80211_SCAN_OFFCHANNEL;
+
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: %s scan, ticks %u duration %lu\n", __func__,
+		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
+		    ticks, duration);
+
+		if (ss->ss_ops != NULL) {
+			ss->ss_ic = ic;
+			/*
+			 * A background scan does not select a new sta; it
+			 * just refreshes the scan cache.  Also, indicate
+			 * the scan logic should follow the beacon schedule:
+			 * we go off-channel and scan for a while, then
+			 * return to the bss channel to receive a beacon,
+			 * then go off-channel again.  All during this time
+			 * we notify the ap we're in power save mode.  When
+			 * the scan is complete we leave power save mode.
+			 * If any beacon indicates there are frames pending
+			 * for us then we drop out of power save mode
+			 * (and background scan) automatically by way of the
+			 * usual sta power save logic.
+			 */
+			ss->ss_flags |= IEEE80211_SCAN_NOPICK
+				     |  IEEE80211_SCAN_BGSCAN;
+			/* if previous scan completed, restart */
+			if (ss->ss_next >= ss->ss_last) {
+				ss->ss_next = 0;
+				if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+					ic->ic_stats.is_scan_active++;
+				else
+					ic->ic_stats.is_scan_passive++;
+				ss->ss_ops->scan_restart(ss, ic);
+			}
+			/* NB: flush frames rx'd before 1st channel change */
+			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+			ss->ss_maxdwell = duration;
+			if (scan_restart(SCAN_PRIVATE(ss), duration)) {
+				ic->ic_flags |= IEEE80211_F_SCAN;
+				ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
+			}
+		} else {
+			/* XXX msg+stat */
+		}
+	} else {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: %s scan already in progress\n", __func__,
+		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
+	}
+	IEEE80211_UNLOCK(ic);
+
+	/* NB: racey, does it matter? */
+	return (ic->ic_flags & IEEE80211_F_SCAN);
+}
+
+/*
+ * Cancel any scan currently going on.
+ */
+void
+ieee80211_cancel_scan(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	IEEE80211_LOCK(ic);
+	if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: cancel %s scan\n", __func__,
+		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
+			"active" : "passive");
+
+		/* clear bg scan NOPICK and mark cancel request */
+		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+		SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
+		/* force it to fire asap */
+		callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
+			0, scan_next, ss);
+	}
+	IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Public access to scan_next for drivers that manage
+ * scanning themselves (e.g. for firmware-based devices).
+ */
+void
+ieee80211_scan_next(struct ieee80211com *ic)
+{
+	/*
+	 * XXX: We might need/want to decouple context here by either:
+	 *  callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
+	 * or using a taskqueue.  Let's see what kind of problems direct
+	 * dispatch has for now.
+	 */
+	scan_next(ic->ic_scan);
+}
+
+/*
+ * Public access to scan_next for drivers that are not able to scan single
+ * channels (e.g. for firmware-based devices).
+ */
+void
+ieee80211_scan_done(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	ss->ss_next = ss->ss_last; /* all channels are complete */
+	scan_next(ss);
+}
+
+/*
+ * Scan curchan.  If this is an active scan and the channel
+ * is not marked passive then send probe request frame(s).
+ * Arrange for the channel change after maxdwell ticks.
+ */
+static void
+scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
+	    (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
+		struct ifnet *ifp = ic->ic_ifp;
+		int i;
+
+		/*
+		 * Send a broadcast probe request followed by
+		 * any specified directed probe requests.
+		 * XXX suppress broadcast probe req?
+		 * XXX remove dependence on ic/ic->ic_bss
+		 * XXX move to policy code?
+		 */
+		ieee80211_send_probereq(ic->ic_bss,
+			ic->ic_myaddr, ifp->if_broadcastaddr,
+			ifp->if_broadcastaddr,
+			"", 0,
+			ic->ic_opt_ie, ic->ic_opt_ie_len);
+		for (i = 0; i < ss->ss_nssid; i++)
+			ieee80211_send_probereq(ic->ic_bss,
+				ic->ic_myaddr, ifp->if_broadcastaddr,
+				ifp->if_broadcastaddr,
+				ss->ss_ssid[i].ssid,
+				ss->ss_ssid[i].len,
+				ic->ic_opt_ie, ic->ic_opt_ie_len);
+	}
+	callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
+		maxdwell, scan_next, ss);
+}
+
+/*
+ * Handle mindwell requirements completed; initiate a channel
+ * change to the next channel asap.
+ */
+static void
+scan_mindwell(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
+}
+
+/*
+ * Switch to the next channel marked for scanning.
+ */
+static void
+scan_next(void *arg)
+{
+#define	ISCAN_REP	(ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
+	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
+	struct ieee80211com *ic = ss->ss_ic;
+	struct ieee80211_channel *chan;
+	unsigned long maxdwell, scanend;
+	int scanning, scandone;
+
+	IEEE80211_LOCK(ic);
+	scanning = (ic->ic_flags & IEEE80211_F_SCAN) != 0;
+	IEEE80211_UNLOCK(ic);
+	if (!scanning)			/* canceled */
+		return;
+
+again:
+	scandone = (ss->ss_next >= ss->ss_last) ||
+		(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
+	scanend = SCAN_PRIVATE(ss)->ss_scanend;
+	if (!scandone &&
+	    (ss->ss_flags & IEEE80211_SCAN_GOTPICK) == 0 &&
+	    ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START) ||
+	     time_before(ticks + ss->ss_mindwell, scanend))) {
+		chan = ss->ss_chans[ss->ss_next++];
+
+		/*
+		 * Watch for truncation due to the scan end time.
+		 */
+		if (time_after(ticks + ss->ss_maxdwell, scanend))
+			maxdwell = scanend - ticks;
+		else
+			maxdwell = ss->ss_maxdwell;
+
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		    "%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n",
+		    __func__,
+		    ieee80211_chan2ieee(ic, ic->ic_curchan),
+		        channel_type(ic->ic_curchan),
+		    ieee80211_chan2ieee(ic, chan), channel_type(chan),
+		    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
+			(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
+			"active" : "passive",
+		    ss->ss_mindwell, maxdwell);
+
+		/*
+		 * Potentially change channel and phy mode.
+		 */
+		change_channel(ic, chan);
+
+		/*
+		 * Scan curchan.  Drivers for "intelligent hardware"
+		 * override ic_scan_curchan to tell the device to do
+		 * the work.  Otherwise we manage the work outselves;
+		 * sending a probe request (as needed), and arming the
+		 * timeout to switch channels after maxdwell ticks.
+		 */
+		ic->ic_scan_curchan(ic, maxdwell);
+
+		SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
+		/* clear mindwell lock and initial channel change flush */
+		SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
+	} else {
+		ic->ic_scan_end(ic);		/* notify driver */
+		/*
+		 * Record scan complete time.  Note that we also do
+		 * this when canceled so any background scan will
+		 * not be restarted for a while.
+		 */
+		if (scandone)
+			ic->ic_lastscan = ticks;
+		/* return to the bss channel */
+		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+		    ic->ic_curchan != ic->ic_bsschan)
+			change_channel(ic, ic->ic_bsschan);
+		/* clear internal flags and any indication of a pick */
+		SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
+		ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
+
+		/*
+		 * If not canceled and scan completed, do post-processing.
+		 * If the callback function returns 0, then it wants to
+		 * continue/restart scanning.  Unfortunately we needed to
+		 * notify the driver to end the scan above to avoid having
+		 * rx frames alter the scan candidate list.
+		 */
+		if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
+		    !ss->ss_ops->scan_end(ss, ic) &&
+		    (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
+		    time_before(ticks + ss->ss_mindwell, scanend)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			    "%s: done, restart "
+			    "[ticks %u, dwell min %lu scanend %lu]\n",
+			    __func__,
+			    ticks, ss->ss_mindwell, scanend);
+			ss->ss_next = 0;	/* reset to begining */
+			if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+				ic->ic_stats.is_scan_active++;
+			else
+				ic->ic_stats.is_scan_passive++;
+
+			ic->ic_scan_start(ic);	/* notify driver */
+			goto again;
+		} else {
+			/* past here, scandone is ``true'' if not in bg mode */
+			if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
+				scandone = 1;
+
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			    "%s: %s, "
+			    "[ticks %u, dwell min %lu scanend %lu]\n",
+			    __func__, scandone ? "done" : "stopped",
+			    ticks, ss->ss_mindwell, scanend);
+
+			/*
+			 * Clear the SCAN bit first in case frames are
+			 * pending on the station power save queue.  If
+			 * we defer this then the dispatch of the frames
+			 * may generate a request to cancel scanning.
+			 */
+			ic->ic_flags &= ~IEEE80211_F_SCAN;
+			/*
+			 * Drop out of power save mode when a scan has
+			 * completed.  If this scan was prematurely terminated
+			 * because it is a background scan then don't notify
+			 * the ap; we'll either return to scanning after we
+			 * receive the beacon frame or we'll drop out of power
+			 * save mode because the beacon indicates we have frames
+			 * waiting for us.
+			 */
+			if (scandone) {
+				ieee80211_sta_pwrsave(ic, 0);
+				if (ss->ss_next >= ss->ss_last) {
+					ieee80211_notify_scan_done(ic);
+					ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
+				}
+			}
+			SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL;
+			ss->ss_flags &=
+			    ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
+		}
+	}
+#undef ISCAN_REP
+}
+
+#ifdef IEEE80211_DEBUG
+static void
+dump_probe_beacon(uint8_t subtype, int isnew,
+	const uint8_t mac[IEEE80211_ADDR_LEN],
+	const struct ieee80211_scanparams *sp)
+{
+
+	printf("[%s] %s%s on chan %u (bss chan %u) ",
+	    ether_sprintf(mac), isnew ? "new " : "",
+	    ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
+	    IEEE80211_CHAN2IEEE(sp->curchan), sp->bchan);
+	ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
+	printf("\n");
+
+	if (isnew) {
+		printf("[%s] caps 0x%x bintval %u erp 0x%x", 
+			ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
+		if (sp->country != NULL) {
+#ifdef __FreeBSD__
+			printf(" country info %*D",
+				sp->country[1], sp->country+2, " ");
+#else
+			int i;
+			printf(" country info");
+			for (i = 0; i < sp->country[1]; i++)
+				printf(" %02x", sp->country[i+2]);
+#endif
+		}
+		printf("\n");
+	}
+}
+#endif /* IEEE80211_DEBUG */
+
+/*
+ * Process a beacon or probe response frame.
+ */
+void
+ieee80211_add_scan(struct ieee80211com *ic,
+	const struct ieee80211_scanparams *sp,
+	const struct ieee80211_frame *wh,
+	int subtype, int rssi, int noise, int rstamp)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	/*
+	 * Frames received during startup are discarded to avoid
+	 * using scan state setup on the initial entry to the timer
+	 * callback.  This can occur because the device may enable
+	 * rx prior to our doing the initial channel change in the
+	 * timer routine (we defer the channel change to the timer
+	 * code to simplify locking on linux).
+	 */
+	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
+		return;
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
+		dump_probe_beacon(subtype, 1, wh->i_addr2, sp);
+#endif
+	if (ss->ss_ops != NULL &&
+	    ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) {
+		/*
+		 * If we've reached the min dwell time terminate
+		 * the timer so we'll switch to the next channel.
+		 */
+		if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
+		    time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			    "%s: chan %3d%c min dwell met (%u > %lu)\n",
+			    __func__,
+			    ieee80211_chan2ieee(ic, ic->ic_curchan),
+				channel_type(ic->ic_curchan),
+			    ticks, SCAN_PRIVATE(ss)->ss_chanmindwell);
+			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
+			/*
+			 * NB: trigger at next clock tick or wait for the
+			 * hardware
+			 */
+			ic->ic_scan_mindwell(ic);
+		}
+	}
+}
+
+/*
+ * Timeout/age scan cache entries; called from sta timeout
+ * timer (XXX should be self-contained).
+ */
+void
+ieee80211_scan_timeout(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	if (ss->ss_ops != NULL)
+		ss->ss_ops->scan_age(ss);
+}
+
+/*
+ * Mark a scan cache entry after a successful associate.
+ */
+void
+ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	if (ss->ss_ops != NULL) {
+		IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN,
+			mac, "%s",  __func__);
+		ss->ss_ops->scan_assoc_success(ss, mac);
+	}
+}
+
+/*
+ * Demerit a scan cache entry after failing to associate.
+ */
+void
+ieee80211_scan_assoc_fail(struct ieee80211com *ic,
+	const uint8_t mac[], int reason)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	if (ss->ss_ops != NULL) {
+		IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, mac,
+			"%s: reason %u", __func__, reason);
+		ss->ss_ops->scan_assoc_fail(ss, mac, reason);
+	}
+}
+
+/*
+ * Iterate over the contents of the scan cache.
+ */
+void
+ieee80211_scan_iterate(struct ieee80211com *ic,
+	ieee80211_scan_iter_func *f, void *arg)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	if (ss->ss_ops != NULL)
+		ss->ss_ops->scan_iterate(ss, f, arg);
+}
+
+/*
+ * Flush the contents of the scan cache.
+ */
+void
+ieee80211_scan_flush(struct ieee80211com *ic)
+{
+	struct ieee80211_scan_state *ss = ic->ic_scan;
+
+	if (ss->ss_ops != NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			"%s\n",  __func__);
+		ss->ss_ops->scan_flush(ss);
+	}
+}
Index: ieee80211_var.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_var.h -L sys/net80211/ieee80211_var.h -u -r1.2 -r1.3
--- sys/net80211/ieee80211_var.h
+++ sys/net80211/ieee80211_var.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,7 +23,7 @@
  * (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.22.2.11 2006/03/13 03:05:48 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.52.2.2 2007/11/29 21:23:22 sam Exp $
  */
 #ifndef _NET80211_IEEE80211_VAR_H_
 #define _NET80211_IEEE80211_VAR_H_
@@ -56,24 +50,28 @@
 #include <net80211/ieee80211_crypto.h>
 #include <net80211/ieee80211_ioctl.h>		/* for ieee80211_stats */
 #include <net80211/ieee80211_node.h>
+#include <net80211/ieee80211_power.h>
 #include <net80211/ieee80211_proto.h>
+#include <net80211/ieee80211_scan.h>
 
 #define	IEEE80211_TXPOWER_MAX	100	/* .5 dbM (XXX units?) */
 #define	IEEE80211_TXPOWER_MIN	0	/* kill radio */
 
-#define	IEEE80211_DTIM_MAX	15	/* max DTIM period */
-#define	IEEE80211_DTIM_MIN	1	/* min DTIM period */
 #define	IEEE80211_DTIM_DEFAULT	1	/* default DTIM period */
-
-/* NB: min+max come from WiFi requirements */
-#define	IEEE80211_BINTVAL_MAX	1000	/* 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) */
 
 #define	IEEE80211_BMISS_MAX	2	/* maximum consecutive bmiss allowed */
-#define	IEEE80211_SWBMISS_THRESHOLD 50	/* s/w bmiss threshold (TU's) */
 #define	IEEE80211_HWBMISS_DEFAULT 7	/* h/w bmiss threshold (beacons) */
 
+#define	IEEE80211_BGSCAN_INTVAL_MIN	15	/* min bg scan intvl (secs) */
+#define	IEEE80211_BGSCAN_INTVAL_DEFAULT	(5*60)	/* default bg scan intvl */
+
+#define	IEEE80211_BGSCAN_IDLE_MIN	100	/* min idle time (ms) */
+#define	IEEE80211_BGSCAN_IDLE_DEFAULT	250	/* default idle time (ms) */
+
+#define	IEEE80211_SCAN_VALID_MIN	10	/* min scan valid time (secs) */
+#define	IEEE80211_SCAN_VALID_DEFAULT	60	/* default scan valid time */
+
 #define	IEEE80211_PS_SLEEP	0x1	/* STA is in power saving mode */
 #define	IEEE80211_PS_MAX_QUEUE	50	/* maximum saved packets */
 
@@ -83,9 +81,9 @@
 #define	IEEE80211_RTS_DEFAULT		IEEE80211_RTS_MAX
 #define	IEEE80211_FRAG_DEFAULT		IEEE80211_FRAG_MAX
 
-#define	IEEE80211_MS_TO_TU(x)	(((x) * 1024) / 1000)
-#define	IEEE80211_TU_TO_MS(x)	(((x) * 1000) / 1024)
-#define	IEEE80211_TU_TO_TICKS(x)(((x) * hz) / 1024)
+#define	IEEE80211_MS_TO_TU(x)	(((x) * 1000) / 1024)
+#define	IEEE80211_TU_TO_MS(x)	(((x) * 1024) / 1000)
+#define	IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000))
 
 struct ieee80211_aclator;
 struct sysctl_ctx_list;
@@ -93,108 +91,139 @@
 struct ieee80211com {
 	SLIST_ENTRY(ieee80211com) ic_next;
 	struct ifnet		*ic_ifp;	/* associated device */
+	ieee80211_com_lock_t	ic_comlock;	/* state update lock */
+	ieee80211_beacon_lock_t	ic_beaconlock;	/* beacon update lock */
 	struct ieee80211_stats	ic_stats;	/* statistics */
 	struct sysctl_ctx_list	*ic_sysctl;	/* dynamic sysctl context */
-	u_int32_t		ic_debug;	/* debug msg flags */
+	uint32_t		ic_debug;	/* debug msg flags */
 	int			ic_vap;		/* virtual AP index */
-	ieee80211_beacon_lock_t	ic_beaconlock;	/* beacon update lock */
+	int			ic_headroom;	/* driver tx headroom needs */
+	enum ieee80211_phytype	ic_phytype;	/* XXX wrong for multi-mode */
+	enum ieee80211_opmode	ic_opmode;	/* operation mode */
+	struct ifmedia		ic_media;	/* interface media config */
+	uint8_t			ic_myaddr[IEEE80211_ADDR_LEN];
 
-	int			(*ic_reset)(struct ifnet *);
-	void			(*ic_recv_mgmt)(struct ieee80211com *,
-				    struct mbuf *, struct ieee80211_node *,
-				    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 ieee80211_node *, int);
-	void			(*ic_updateslot)(struct ifnet *);
-	void			(*ic_set_tim)(struct ieee80211_node *, int);
-	u_int8_t		ic_myaddr[IEEE80211_ADDR_LEN];
+	uint32_t		ic_flags;	/* state flags */
+	uint32_t		ic_flags_ext;	/* extended state flags */
+	uint32_t		ic_flags_ven;	/* vendor state flags */
+	uint32_t		ic_caps;	/* capabilities */
+	uint32_t		ic_htcaps;	/* HT capabilities */
+	uint8_t			ic_modecaps[2];	/* set of mode capabilities */
+	uint16_t		ic_curmode;	/* current mode */
 	struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+	uint16_t		ic_bintval;	/* beacon interval */
+	uint16_t		ic_lintval;	/* listen interval */
+	uint16_t		ic_holdover;	/* PM hold over duration */
+	uint16_t		ic_txpowlimit;	/* global tx power limit */
+	int			ic_ampdu_rxmax;	/* A-MPDU rx limit (bytes) */
+	int			ic_ampdu_density;/* A-MPDU density */
+	int			ic_ampdu_limit;	/* A-MPDU tx limit (bytes) */
+	int			ic_amsdu_limit;	/* A-MSDU tx limit (bytes) */
+
+	/*
+	 * Channel state:
+	 *
+	 * ic_channels is the set of available channels for the device;
+	 *    it is setup by the driver
+	 * ic_nchans is the number of valid entries in ic_channels
+	 * ic_chan_avail is a bit vector of these channels used to check
+	 *    whether a channel is available w/o searching the channel table.
+	 * ic_chan_active is a (potentially) constrained subset of
+	 *    ic_chan_avail that reflects any mode setting or user-specified
+	 *    limit on the set of channels to use/scan
+	 * ic_curchan is the current channel the device is set to; it may
+	 *    be different from ic_bsschan when we are off-channel scanning
+	 *    or otherwise doing background work
+	 * ic_bsschan is the channel selected for operation; it may
+	 *    be undefined (IEEE80211_CHAN_ANYC)
+	 * ic_prevchan is a cached ``previous channel'' used to optimize
+	 *    lookups when switching back+forth between two channels
+	 *    (e.g. for dynamic turbo)
+	 */
+	int			ic_nchans;	/* # entries in ic_channels */
 	struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
-	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;
-	u_int32_t		ic_flags;	/* state flags */
-	u_int32_t		ic_flags_ext;	/* extended 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 */
+	uint8_t			ic_chan_avail[IEEE80211_CHAN_BYTES];
+	uint8_t			ic_chan_active[IEEE80211_CHAN_BYTES];
+	uint8_t			ic_chan_scan[IEEE80211_CHAN_BYTES];
+	struct ieee80211_channel *ic_curchan;	/* current channel */
+	struct ieee80211_channel *ic_bsschan;	/* bss channel */
+	struct ieee80211_channel *ic_prevchan;	/* previous channel */
+	int			ic_countrycode;	/* ISO country code */
+	uint16_t		ic_regdomain;	/* regulatory domain */
+	uint8_t			ic_location;	/* unknown, indoor, outdoor */
+
+	struct ieee80211_scan_state *ic_scan;	/* scan state */
 	enum ieee80211_roamingmode ic_roaming;	/* roaming mode */
+	int			ic_lastdata;	/* time of last data frame */
+	int			ic_lastscan;	/* time last scan completed */
+	int			ic_des_nssid;	/* # desired ssids */
+	struct ieee80211_scan_ssid ic_des_ssid[1];/* desired ssid table */
+	uint8_t			ic_des_bssid[IEEE80211_ADDR_LEN];
+	struct ieee80211_channel *ic_des_chan;	/* desired channel */
+	int			ic_des_mode;	/* desired phymode */
+	u_int			ic_bgscanidle;	/* bg scan idle threshold */
+	u_int			ic_bgscanintvl;	/* bg scan min interval */
+	u_int			ic_scanvalid;	/* scan cache valid threshold */
+	struct ieee80211_roam	ic_roam;	/* sta-mode roaming state */
+
 	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_int8_t		ic_dtim_period;	/* DTIM period */
-	u_int8_t		ic_dtim_count;	/* DTIM count for last bcn */
-	struct ifmedia		ic_media;	/* interface media config */
+
+	struct ieee80211_wme_state ic_wme;	/* WME/WMM state */
+	const struct ieee80211_aclator *ic_acl;	/* aclator glue */
+	void			*ic_as;		/* private aclator state */
+
+	enum ieee80211_protmode	ic_protmode;	/* 802.11g protection mode */
+	uint16_t		ic_nonerpsta;	/* # non-ERP stations */
+	uint16_t		ic_longslotsta;	/* # long slot time stations */
+	uint16_t		ic_sta_assoc;	/* stations associated */
+	uint16_t		ic_ht_sta_assoc;/* HT stations associated */
+	uint16_t		ic_ht40_sta_assoc;/* HT40 stations associated */
+	uint8_t			ic_curhtprotmode;/* HTINFO bss state */
+	enum ieee80211_protmode	ic_htprotmode;	/* HT protection mode */
+	int			ic_lastnonerp;	/* last time non-ERP sta noted*/
+	int			ic_lastnonht;	/* last time non-HT sta noted */
+
+	struct ifqueue		ic_mgtq;
+	enum ieee80211_state	ic_state;	/* 802.11 state */
+	struct callout		ic_mgtsend;	/* mgmt frame response timer */
+	uint32_t		*ic_aid_bitmap;	/* association id map */
+	uint16_t		ic_max_aid;
+	uint16_t		ic_ps_sta;	/* stations in power save */
+	uint16_t		ic_ps_pending;	/* ps sta's w/ pending frames */
+	uint8_t			*ic_tim_bitmap;	/* power-save stations w/ data*/
+	uint16_t		ic_tim_len;	/* ic_tim_bitmap size (bytes) */
+	uint8_t			ic_dtim_period;	/* DTIM period */
+	uint8_t			ic_dtim_count;	/* DTIM count for last bcn */
 	struct bpf_if		*ic_rawbpf;	/* packet filter structure */
 	struct ieee80211_node	*ic_bss;	/* information for this node */
-	struct ieee80211_channel *ic_ibss_chan;
-	struct ieee80211_channel *ic_curchan;	/* current channel */
-	int			ic_fixed_rate;	/* index to ic_sup_rates[] */
+	int			ic_fixed_rate;	/* 802.11 rate or -1 */
 	int			ic_mcast_rate;	/* rate for mcast frames */
-	u_int16_t		ic_rtsthreshold;
-	u_int16_t		ic_fragthreshold;
-	u_int8_t		ic_bmissthreshold;
-	u_int8_t		ic_bmiss_count;	/* current beacon miss count */
+	uint16_t		ic_rtsthreshold;
+	uint16_t		ic_fragthreshold;
+	uint8_t			ic_bmissthreshold;
+	uint8_t			ic_bmiss_count;	/* current beacon miss count */
 	int			ic_bmiss_max;	/* max bmiss before scan */
-	u_int16_t		ic_swbmiss_count;/* beacons in last period */
-	u_int16_t		ic_swbmiss_period;/* s/w bmiss period */
+	uint16_t		ic_swbmiss_count;/* beacons in last period */
+	uint16_t		ic_swbmiss_period;/* s/w bmiss period */
 	struct callout		ic_swbmiss;	/* s/w beacon miss timer */
-	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_bintval;	/* beacon 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_pad0;	/* was ic_bmisstimeout */
-	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;
-	u_int8_t		ic_des_essid[IEEE80211_NWID_LEN];
-	struct ieee80211_channel *ic_des_chan;	/* desired channel */
-	u_int8_t		ic_des_bssid[IEEE80211_ADDR_LEN];
+
+	uint16_t		ic_txmin;	/* min tx retry count */
+	uint16_t		ic_txmax;	/* max tx retry count */
+	uint16_t		ic_txlifetime;	/* tx lifetime */
+	struct callout		ic_inact;	/* inactivity timer wait */
 	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.
-	 */
+	uint16_t		ic_opt_ie_len;	/* length of ni_opt_ie */
 	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
@@ -203,14 +232,66 @@
 	const struct ieee80211_authenticator *ic_auth;
 	struct eapolcom		*ic_ec;	
 
+	/* send/recv 802.11 management frame */
+	int			(*ic_send_mgmt)(struct ieee80211com *,
+				    struct ieee80211_node *, int, int);
+	void			(*ic_recv_mgmt)(struct ieee80211com *,
+				    struct mbuf *, struct ieee80211_node *,
+				    int, int, int, uint32_t);
+	/* send raw 802.11 frame */
+	int			(*ic_raw_xmit)(struct ieee80211_node *,
+				    struct mbuf *,
+				    const struct ieee80211_bpf_params *);
+	/* reset device state after 802.11 parameter/state change */
+	int			(*ic_reset)(struct ifnet *);
+	/* [schedule] beacon frame update */
+	void			(*ic_update_beacon)(struct ieee80211com *, int);
+	/* update device state for 802.11 slot time change */
+	void			(*ic_updateslot)(struct ifnet *);
+	/* new station association callback/notification */
+	void			(*ic_newassoc)(struct ieee80211_node *, int);
+	/* node state management */
+	struct ieee80211_node	*(*ic_node_alloc)(struct ieee80211_node_table*);
+	void			(*ic_node_free)(struct ieee80211_node *);
+	void			(*ic_node_cleanup)(struct ieee80211_node *);
+	int8_t			(*ic_node_getrssi)(const struct ieee80211_node*);
+	void			(*ic_node_getsignal)(const struct ieee80211_node*,
+				    int8_t *, int8_t *);
+	/* scanning support */
+	void			(*ic_scan_start)(struct ieee80211com *);
+	void			(*ic_scan_end)(struct ieee80211com *);
+	void			(*ic_set_channel)(struct ieee80211com *);
+	void			(*ic_scan_curchan)(struct ieee80211com *,
+				    unsigned long);
+	void			(*ic_scan_mindwell)(struct ieee80211com *);
+	/* per-vap eventually... */
+	int			(*ic_newstate)(struct ieee80211com *,
+				    enum ieee80211_state, int);
+	void			(*ic_set_tim)(struct ieee80211_node *, int);
+
 	/*
-	 * 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.
+	 * 802.11n ADDBA support.  A simple/generic implementation
+	 * of A-MPDU tx aggregation is provided; the driver may
+	 * override these methods to provide their own support.
+	 * A-MPDU rx re-ordering happens automatically if the
+	 * driver passes out-of-order frames to ieee80211_input
+	 * from an assocated HT station.
 	 */
-	const struct ieee80211_aclator *ic_acl;
-	void			*ic_as;
-	u_int32_t		ic_pad[56];	/* future expansion */
+	void			(*ic_recv_action)(struct ieee80211_node *,
+				    const uint8_t *frm, const uint8_t *efrm);
+	int			(*ic_send_action)(struct ieee80211_node *,
+				    int category, int action,
+				    uint16_t args[4]);
+	/* start/stop doing A-MPDU tx aggregation for a station */
+	int			(*ic_addba_request)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *,
+				    int dialogtoken, int baparamset,
+				    int batimeout);
+	int			(*ic_addba_response)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *,
+				    int status, int baparamset, int batimeout);
+	void			(*ic_addba_stop)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *);
 };
 
 #define	IEEE80211_ADDR_EQ(a1,a2)	(memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
@@ -218,9 +299,10 @@
 
 /* ic_flags */
 /* NB: bits 0x4c available */
-#define	IEEE80211_F_FF		0x00000001	/* CONF: ATH FF enabled */
-#define	IEEE80211_F_TURBOP	0x00000002	/* CONF: ATH Turbo enabled*/
-#define	IEEE80211_F_BURST	0x00000004	/* CONF: bursting enabled */
+#define	IEEE80211_F_TURBOP	0x00000001	/* CONF: ATH Turbo enabled*/
+#define	IEEE80211_F_COMP	0x00000002	/* CONF: ATH comp enabled */
+#define	IEEE80211_F_FF		0x00000004	/* CONF: ATH FF enabled */
+#define	IEEE80211_F_BURST	0x00000008	/* CONF: bursting enabled */
 /* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */
 #define	IEEE80211_F_PRIVACY	0x00000010	/* CONF: privacy enabled */
 #define	IEEE80211_F_PUREG	0x00000020	/* CONF: 11g w/o 11b sta's */
@@ -240,7 +322,6 @@
 #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 */
@@ -248,15 +329,33 @@
 #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 */
+#define	IEEE80211_F_DOTH	0x40000000	/* CONF: 11h enabled */
+
+/* Atheros protocol-specific flags */
+#define	IEEE80211_F_ATHEROS \
+	(IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP)
+/* Check if an Atheros capability was negotiated for use */
+#define	IEEE80211_ATH_CAP(ic, ni, bit) \
+	((ic)->ic_flags & (ni)->ni_ath_flags & (bit))
 
 /* ic_flags_ext */
-#define	IEEE80211_FEXT_WDS	0x00000001	/* CONF: 4 addr allowed */
+#define	IEEE80211_FEXT_NONHT_PR	 0x00000001	/* STATUS: non-HT sta present */
+#define	IEEE80211_FEXT_INACT	 0x00000002	/* CONF: sta inact handling */
 /* 0x00000006 reserved */
-#define	IEEE80211_FEXT_BGSCAN	0x00000008	/* STATUS: enable full bgscan completion */
-#define	IEEE80211_FEXT_ERPUPDATE 0x00000200	/* STATUS: update ERP element */
-#define	IEEE80211_FEXT_SWBMISS	0x00000400	/* CONF: do bmiss in s/w */
+#define	IEEE80211_FEXT_BGSCAN	 0x00000008	/* STATUS: complete bgscan */
+#define	IEEE80211_FEXT_NONERP_PR 0x00000200	/* STATUS: non-ERP sta present*/
+#define	IEEE80211_FEXT_SWBMISS	 0x00000400	/* CONF: do bmiss in s/w */
 #define	IEEE80211_FEXT_PROBECHAN 0x00020000	/* CONF: probe passive channel*/
+#define	IEEE80211_FEXT_HT	 0x00080000	/* CONF: HT supported */
+#define	IEEE80211_FEXT_AMPDU_TX	 0x00100000	/* CONF: A-MPDU tx supported */
+#define	IEEE80211_FEXT_AMPDU_RX	 0x00200000	/* CONF: A-MPDU tx supported */
+#define	IEEE80211_FEXT_AMSDU_TX	 0x00400000	/* CONF: A-MSDU tx supported */
+#define	IEEE80211_FEXT_AMSDU_RX	 0x00800000	/* CONF: A-MSDU tx supported */
+#define	IEEE80211_FEXT_USEHT40	 0x01000000	/* CONF: 20/40 use enabled */
+#define	IEEE80211_FEXT_PUREN	 0x02000000	/* CONF: 11n w/o legacy sta's */
+#define	IEEE80211_FEXT_SHORTGI20 0x04000000	/* CONF: short GI in HT20 */
+#define	IEEE80211_FEXT_SHORTGI40 0x08000000	/* CONF: short GI in HT40 */
+#define	IEEE80211_FEXT_HTCOMPAT  0x10000000	/* CONF: HT vendor OUI's */
 
 /* ic_caps */
 #define	IEEE80211_C_WEP		0x00000001	/* CAPABILITY: WEP available */
@@ -289,27 +388,44 @@
 
 #define	IEEE80211_C_CRYPTO	0x0000002f	/* CAPABILITY: crypto alg's */
 
+/*
+ * ic_htcaps: HT-specific device/driver capabilities
+ *
+ * NB: the low 16-bits are the 802.11 definitions, the upper
+ *     16-bits are used to define s/w/driver capabilities.
+ */
+#define	IEEE80211_HTC_AMPDU	0x00010000	/* CAPABILITY: A-MPDU tx */
+#define	IEEE80211_HTC_AMSDU	0x00020000	/* CAPABILITY: A-MSDU tx */
+/* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */
+#define	IEEE80211_HTC_HT	0x00040000	/* CAPABILITY: HT operation */
+
 void	ieee80211_ifattach(struct ieee80211com *);
 void	ieee80211_ifdetach(struct ieee80211com *);
+const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic,
+		const struct ieee80211_channel *);
 void	ieee80211_announce(struct ieee80211com *);
+void	ieee80211_announce_channels(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]);
+struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]);
 int	ieee80211_media_change(struct ifnet *);
 void	ieee80211_media_status(struct ifnet *, struct ifmediareq *);
 int	ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t);
 int	ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t);
 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);
-u_int	ieee80211_mhz2ieee(u_int, u_int);
-u_int	ieee80211_chan2ieee(struct ieee80211com *, struct ieee80211_channel *);
+int	ieee80211_mhz2ieee(u_int, u_int);
+int	ieee80211_chan2ieee(struct ieee80211com *,
+		const struct ieee80211_channel *);
 u_int	ieee80211_ieee2mhz(u_int, u_int);
+struct ieee80211_channel *ieee80211_find_channel(struct ieee80211com *,
+		int freq, int flags);
+struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *,
+		int ieee, int flags);
 int	ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
-enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,
-		struct ieee80211_channel *);
+enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
 
 /* 
  * Key update synchronization methods.  XXX should not be visible.
@@ -339,7 +455,7 @@
 {
 	int size = ieee80211_hdrsize(data);
 	if (ic->ic_flags & IEEE80211_F_DATAPAD)
-		size = roundup(size, sizeof(u_int32_t));
+		size = roundup(size, sizeof(uint32_t));
 	return size;
 }
 
@@ -351,10 +467,31 @@
 {
 	int size = ieee80211_anyhdrsize(data);
 	if (ic->ic_flags & IEEE80211_F_DATAPAD)
-		size = roundup(size, sizeof(u_int32_t));
+		size = roundup(size, sizeof(uint32_t));
 	return size;
 }
 
+/*
+ * Notify a driver that beacon state has been updated.
+ */
+static __inline void
+ieee80211_beacon_notify(struct ieee80211com *ic, int what)
+{
+	if (ic->ic_state == IEEE80211_S_RUN)
+		ic->ic_update_beacon(ic, what);
+}
+
+/*
+ * Debugging facilities compiled in when IEEE80211_DEBUG is defined.
+ *
+ * The intent is that any problem in the net80211 layer can be
+ * diagnosed by inspecting the statistics (dumped by the wlanstats
+ * program) and/or the msgs generated by net80211.  Messages are
+ * broken into functional classes and can be controlled with the
+ * wlandebug program.  Certain of these msg groups are for facilities
+ * that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1X).
+ */
+#define	IEEE80211_MSG_11N	0x80000000	/* 11n mode debug */
 #define	IEEE80211_MSG_DEBUG	0x40000000	/* IFF_DEBUG equivalent */
 #define	IEEE80211_MSG_DUMPPKTS	0x20000000	/* IFF_LINK2 equivalant */
 #define	IEEE80211_MSG_CRYPTO	0x10000000	/* crypto work */
@@ -380,6 +517,10 @@
 #define	IEEE80211_MSG_DOTH	0x00000100	/* 802.11h support */
 #define	IEEE80211_MSG_INACT	0x00000080	/* inactivity handling */
 #define	IEEE80211_MSG_ROAM	0x00000040	/* sta-mode roaming */
+#define	IEEE80211_MSG_RATECTL	0x00000020	/* tx rate control */
+#define	IEEE80211_MSG_ACTION	0x00000010	/* action frame handling */
+#define	IEEE80211_MSG_WDS	0x00000008	/* WDS handling */
+#define	IEEE80211_MSG_IOCTL	0x00000004	/* ioctl handling */
 
 #define	IEEE80211_MSG_ANY	0xffffffff	/* anything */
 
@@ -403,7 +544,7 @@
 } while (0)
 void	ieee80211_note(struct ieee80211com *ic, const char *fmt, ...);
 void	ieee80211_note_mac(struct ieee80211com *ic,
-		const u_int8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
+		const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
 void	ieee80211_note_frame(struct ieee80211com *ic,
 		const struct ieee80211_frame *wh, const char *fmt, ...);
 #define	ieee80211_msg_debug(_ic) \
@@ -422,12 +563,44 @@
 	((_ic)->ic_debug & IEEE80211_MSG_SCAN)
 #define	ieee80211_msg_assoc(_ic) \
 	((_ic)->ic_debug & IEEE80211_MSG_ASSOC)
+
+/*
+ * 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)
+
+void ieee80211_discard_frame(struct ieee80211com *,
+	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+void ieee80211_discard_ie(struct ieee80211com *,
+	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+void ieee80211_discard_mac(struct ieee80211com *,
+	const uint8_t mac[IEEE80211_ADDR_LEN], const char *type,
+	const char *fmt, ...);
 #else
 #define	IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
+#define	IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...)
 #define	IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...)
 #define	IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...)
 #define	ieee80211_msg_dumppkts(_ic)	0
 #define	ieee80211_msg(_ic, _m)		0
+
+#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
 
 #endif /* _NET80211_IEEE80211_VAR_H_ */
Index: ieee80211_node.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_node.c -L sys/net80211/ieee80211_node.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_node.c
+++ sys/net80211/ieee80211_node.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.48.2.10 2006/03/13 03:05:47 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.88.2.3 2007/11/28 06:15:03 sam Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h> 
@@ -59,26 +53,30 @@
 #define	IEEE80211_AID_ISSET(b, w) \
 	((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
 
+#ifdef IEEE80211_DEBUG_REFCNT
+#define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line
+#else
+#define REFCNT_LOC "%s %p<%s> refcnt %d\n", __func__
+#endif
+
+static int ieee80211_sta_join1(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 int8_t node_getrssi(const struct ieee80211_node *);
+static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *);
 
 static void ieee80211_setup_node(struct ieee80211_node_table *,
-		struct ieee80211_node *, const u_int8_t *);
+		struct ieee80211_node *, const uint8_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 ieee80211_node *, int set);
 
 static void ieee80211_node_table_init(struct ieee80211com *ic,
 	struct ieee80211_node_table *nt, const char *name,
-	int inact, int keyixmax,
-	void (*timeout)(struct ieee80211_node_table *));
+	int inact, int keymaxix);
+static void ieee80211_node_table_reset(struct ieee80211_node_table *);
 static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
+static void ieee80211_erp_timeout(struct ieee80211com *);
 
 MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
 
@@ -90,6 +88,7 @@
 	ic->ic_node_free = node_free;
 	ic->ic_node_cleanup = node_cleanup;
 	ic->ic_node_getrssi = node_getrssi;
+	ic->ic_node_getsignal = node_getsignal;
 
 	/* default station inactivity timer setings */
 	ic->ic_inact_init = IEEE80211_INACT_INIT;
@@ -97,9 +96,12 @@
 	ic->ic_inact_run = IEEE80211_INACT_RUN;
 	ic->ic_inact_probe = IEEE80211_INACT_PROBE;
 
+	callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
+
 	/* NB: driver should override */
 	ic->ic_max_aid = IEEE80211_AID_DEF;
-	ic->ic_set_tim = ieee80211_set_tim;
+
+	ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */
 }
 
 void
@@ -109,30 +111,17 @@
 
 	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);
+	MALLOC(ic->ic_aid_bitmap, uint32_t *,
+		howmany(ic->ic_max_aid, 32) * sizeof(uint32_t),
+		M_80211_NODE, 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__);
-	}
-
 	ieee80211_node_table_init(ic, &ic->ic_sta, "station",
-		IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix,
-		ieee80211_timeout_stations);
-	ieee80211_node_table_init(ic, &ic->ic_scan, "scan",
-		IEEE80211_INACT_SCAN, 0,
-		ieee80211_timeout_scan_candidates);
+		IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix);
 
 	ieee80211_reset_bss(ic);
 	/*
@@ -182,16 +171,11 @@
 		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);
+		FREE(ic->ic_aid_bitmap, M_80211_NODE);
 		ic->ic_aid_bitmap = NULL;
 	}
-	if (ic->ic_tim_bitmap != NULL) {
-		FREE(ic->ic_tim_bitmap, M_DEVBUF);
-		ic->ic_tim_bitmap = NULL;
-	}
 }
 
 /* 
@@ -205,12 +189,18 @@
 
 	ni->ni_flags |= IEEE80211_NODE_AUTH;
 	ni->ni_inact_reload = ic->ic_inact_run;
+	ni->ni_inact = ni->ni_inact_reload;
 }
 
 void
 ieee80211_node_unauthorize(struct ieee80211_node *ni)
 {
+	struct ieee80211com *ic = ni->ni_ic;
+
 	ni->ni_flags &= ~IEEE80211_NODE_AUTH;
+	ni->ni_inact_reload = ic->ic_inact_auth;
+	if (ni->ni_inact > ni->ni_inact_reload)
+		ni->ni_inact = ni->ni_inact_reload;
 }
 
 /*
@@ -218,134 +208,28 @@
  * to insure a consistent view by drivers.
  */
 static void
-ieee80211_set_chan(struct ieee80211com *ic,
-	struct ieee80211_node *ni, struct ieee80211_channel *chan)
+ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
+	struct ieee80211_channel *chan = ic->ic_bsschan;
+
+#if 0
+	KASSERT(chan != IEEE80211_CHAN_ANYC, ("bss channel not setup"));
+#else
 	if (chan == IEEE80211_CHAN_ANYC)	/* XXX while scanning */
 		chan = ic->ic_curchan;
+#endif
 	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 channel set to scan based on the
- * of available channels and the current PHY mode.
- */
-static void
-ieee80211_reset_scan(struct ieee80211com *ic)
-{
-
-	/* 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));
-#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_curchan));
+	if (IEEE80211_IS_CHAN_HT(chan)) {
+		/*
+		 * XXX Gotta be careful here; the rate set returned by
+		 * ieee80211_get_suprates is actually any HT rate
+		 * set so blindly copying it will be bad.  We must
+		 * install the legacy rate est in ni_rates and the
+		 * HT rate set in ni_htrates.
+		 */
+		ni->ni_htrates = *ieee80211_get_suphtrates(ic, chan);
 	}
-#endif /* IEEE80211_DEBUG */
-}
-
-/*
- * Begin an active scan.
- */
-void
-ieee80211_begin_scan(struct ieee80211com *ic, int reset)
-{
-
-	ic->ic_scan.nt_scangen++;
-	/*
-	 * In all but hostap mode scanning starts off in
-	 * an active mode before switching to passive.
-	 */
-	if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
-		ic->ic_flags |= IEEE80211_F_ASCAN;
-		ic->ic_stats.is_scan_active++;
-	} else
-		ic->ic_stats.is_scan_passive++;
-	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(ic);
-	if (reset)
-		ieee80211_free_allnodes(&ic->ic_scan);
-
-	ic->ic_flags |= IEEE80211_F_SCAN;
-
-	/* Scan the next channel. */
-	ieee80211_next_scan(ic);
-}
-
-/*
- * Switch to the next channel marked for scanning.
- */
-int
-ieee80211_next_scan(struct ieee80211com *ic)
-{
-	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;
-	ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
-
-	chan = ic->ic_curchan;
-	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_curchan),
-			    ieee80211_chan2ieee(ic, chan));
-			ic->ic_curchan = chan;
-			/*
-			 * XXX drivers should do this as needed,
-			 * XXX for now maintain compatibility
-			 */
-			ic->ic_bss->ni_rates =
-				ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
-			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
-			return 1;
-		}
-	} while (chan != ic->ic_curchan);
-	ieee80211_end_scan(ic);
-	return 0;
+	ni->ni_rates = *ieee80211_get_suprates(ic, chan);
 }
 
 /*
@@ -367,7 +251,7 @@
 		ieee80211_send_probereq(ic->ic_bss,
 			ic->ic_myaddr, ifp->if_broadcastaddr,
 			ifp->if_broadcastaddr,
-			ic->ic_des_essid, ic->ic_des_esslen,
+			ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len,
 			ic->ic_opt_ie, ic->ic_opt_ie_len);
 	} else
 		ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
@@ -416,9 +300,10 @@
 		return;
 	}
 	IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
-	ni->ni_esslen = ic->ic_des_esslen;
-	memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
-	copy_bss(ni, ic->ic_bss);
+	ni->ni_esslen = ic->ic_des_ssid[0].len;
+	memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen);
+	if (ic->ic_bss != NULL)
+		copy_bss(ni, ic->ic_bss);
 	ni->ni_intval = ic->ic_bintval;
 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
 		ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
@@ -431,8 +316,11 @@
 		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 */
+		else {
+			get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN);
+			/* clear group bit, add local bit */
+			ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02;
+		}
 	} else if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
 		if (ic->ic_flags & IEEE80211_F_DESBSSID)
 			IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
@@ -442,36 +330,47 @@
 	/* 
 	 * Fix the channel and related attributes.
 	 */
-	ieee80211_set_chan(ic, ni, chan);
-	ic->ic_curchan = chan;
-	ic->ic_curmode = ieee80211_chan2mode(ic, chan);
+	ic->ic_bsschan = chan;
+	ieee80211_node_set_chan(ic, ni);
+	ic->ic_curmode = ieee80211_chan2mode(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);
+	if (IEEE80211_IS_CHAN_FULL(chan)) {
+		if (IEEE80211_IS_CHAN_ANYG(chan)) {
+			/*
+			 * Use a mixed 11b/11g rate set.
+			 */
+			ieee80211_set11gbasicrates(&ni->ni_rates,
+				IEEE80211_MODE_11G);
+		} else if (IEEE80211_IS_CHAN_B(chan)) {
+			/*
+			 * Force pure 11b rate set.
+			 */
+			ieee80211_set11gbasicrates(&ni->ni_rates,
+				IEEE80211_MODE_11B);
+		}
 	}
 
-	(void) ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+	(void) ieee80211_sta_join1(ieee80211_ref_node(ni));
 }
 
+/*
+ * Reset bss state on transition to the INIT state.
+ * Clear any stations from the table (they have been
+ * deauth'd) and reset the bss node (clears key, rate
+ * etc. state).
+ */
 void
 ieee80211_reset_bss(struct ieee80211com *ic)
 {
 	struct ieee80211_node *ni, *obss;
 
-	ieee80211_node_table_reset(&ic->ic_scan);
+	callout_drain(&ic->ic_inact);
 	ieee80211_node_table_reset(&ic->ic_sta);
+	ieee80211_reset_erp(ic);
 
-	ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
+	ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
 	KASSERT(ni != NULL, ("unable to setup inital BSS node"));
 	obss = ic->ic_bss;
 	ic->ic_bss = ieee80211_ref_node(ni);
@@ -482,21 +381,71 @@
 	}
 }
 
-/* XXX tunable */
-#define	STA_FAILS_MAX	2		/* assoc failures before ignored */
+static int
+match_ssid(const struct ieee80211_node *ni,
+	int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+	int i;
 
+	for (i = 0; i < nssid; i++) {
+		if (ni->ni_esslen == ssids[i].len &&
+		     memcmp(ni->ni_essid, ssids[i].ssid, ni->ni_esslen) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Test a node for suitability/compatibility.
+ */
 static int
-ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+        uint8_t rate;
+
+	if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
+		return 0;
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+			return 0;
+	} else {
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
+			return 0;
+	}
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+			return 0;
+	} else {
+		/* XXX does this mean privacy is supported or required? */
+		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
+			return 0;
+	}
+	rate = ieee80211_fix_rate(ni, &ni->ni_rates,
+	    IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
+	if (rate & IEEE80211_RATE_BASIC)
+		return 0;
+	if (ic->ic_des_nssid != 0 &&
+	    !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+		return 0;
+	if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+		return 0;
+	return 1;
+}
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Display node suitability/compatibility.
+ */
+static void
+check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
-        u_int8_t rate;
+        uint8_t rate;
         int fail;
 
 	fail = 0;
 	if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
 		fail |= 0x01;
-	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
-	    ni->ni_chan != ic->ic_des_chan)
-		fail |= 0x01;
 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
 		if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
 			fail |= 0x02;
@@ -512,241 +461,36 @@
 		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
 			fail |= 0x04;
 	}
-	rate = ieee80211_fix_rate(ni, IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
+	rate = ieee80211_fix_rate(ni, &ni->ni_rates,
+	     IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
 	if (rate & IEEE80211_RATE_BASIC)
 		fail |= 0x08;
-	if (ic->ic_des_esslen != 0 &&
-	    (ni->ni_esslen != ic->ic_des_esslen ||
-	     memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0))
+	if (ic->ic_des_nssid != 0 &&
+	    !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
 		fail |= 0x10;
 	if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
 	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
 		fail |= 0x20;
-	if (ni->ni_fails >= STA_FAILS_MAX)
-		fail |= 0x40;
-#ifdef IEEE80211_DEBUG
-	if (ieee80211_msg_scan(ic)) {
-		printf(" %c %s",
-		    fail & 0x40 ? '=' : fail & 0x80 ? '^' : fail ? '-' : '+',
-		    ether_sprintf(ni->ni_macaddr));
-		printf(" %s%c", ether_sprintf(ni->ni_bssid),
-		    fail & 0x20 ? '!' : ' ');
-		printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
-			fail & 0x01 ? '!' : ' ');
-		printf(" %+4d", ni->ni_rssi);
-		printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
-		    fail & 0x08 ? '!' : ' ');
-		printf(" %4s%c",
-		    (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
-		    (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
-		    "????",
-		    fail & 0x02 ? '!' : ' ');
-		printf(" %3s%c ",
-		    (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
-		    "wep" : "no",
-		    fail & 0x04 ? '!' : ' ');
-		ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
-		printf("%s\n", fail & 0x10 ? "!" : "");
-	}
-#endif
-	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)
-{
-	u_int8_t maxa, maxb;
-	u_int8_t rssia, rssib;
-	int weight;
-
-	/* 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;
-
-	/* compare count of previous failures */
-	weight = b->ni_fails - a->ni_fails;
-	if (abs(weight) > 1)
-		return weight;
-
-	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;
-}
-
-/*
- * 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);
-	ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
-}
 
-/*
- * Complete a scan of potential channels.
- */
-void
-ieee80211_end_scan(struct ieee80211com *ic)
-{
-	struct ieee80211_node_table *nt = &ic->ic_scan;
-	struct ieee80211_node *ni, *selbs;
-
-	ieee80211_cancel_scan(ic);
-	ieee80211_notify_scan_done(ic);
-
-	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-		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 unoccupied one.  If that fails, pick a channel that
-		 * looks to be quietest.
-		 */
-		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)) {
-				/*
-				 * 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;
-		}
-		/* no suitable channel, should not happen */
-	}
-
-	/*
-	 * 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) &&
-		    ic->ic_des_esslen != 0) {
-			ieee80211_create_ibss(ic, ic->ic_ibss_chan);
-			return;
-		}
-		/*
-		 * Decrement the failure counts so entries will be
-		 * reconsidered the next time around.  We really want
-		 * to do this only for sta's where we've previously
-		 * had some success.
-		 */
-		IEEE80211_NODE_LOCK(nt);
-		TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
-			if (ni->ni_fails)
-				ni->ni_fails--;
-		IEEE80211_NODE_UNLOCK(nt);
-		/*
-		 * Reset the list of channels to scan and start again.
-		 */
-		ieee80211_reset_scan(ic);
-		ic->ic_flags |= IEEE80211_F_SCAN;
-		ieee80211_next_scan(ic);
-		return;
-	}
-	selbs = NULL;
-	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 (ieee80211_match_bss(ic, ni) == 0) {
-			if (selbs == NULL)
-				selbs = ni;
-			else if (ieee80211_node_compare(ic, ni, selbs) > 0)
-				selbs = ni;
-		}
-	}
-	if (selbs != NULL)		/* NB: grab ref while dropping lock */
-		(void) ieee80211_ref_node(selbs);
-	IEEE80211_NODE_UNLOCK(nt);
-	if (selbs == NULL)
-		goto notfound;
-	if (!ieee80211_sta_join(ic, selbs)) {
-		ieee80211_free_node(selbs);
-		goto notfound;
-	}
+	printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr));
+	printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' ');
+	printf(" %3d%c",
+	    ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' ');
+	printf(" %+4d", ni->ni_rssi);
+	printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
+	    fail & 0x08 ? '!' : ' ');
+	printf(" %4s%c",
+	    (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
+	    (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
+	    "????",
+	    fail & 0x02 ? '!' : ' ');
+	printf(" %3s%c ",
+	    (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?  "wep" : "no",
+	    fail & 0x04 ? '!' : ' ');
+	ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
+	printf("%s\n", fail & 0x10 ? "!" : "");
 }
+#endif /* IEEE80211_DEBUG */
  
 /*
  * Handle 802.11 ad hoc network merge.  The
@@ -770,9 +514,14 @@
 		/* unchanged, nothing to do */
 		return 0;
 	}
-	if (ieee80211_match_bss(ic, ni) != 0) {	/* capabilities mismatch */
+	if (!check_bss(ic, ni)) {
+		/* capabilities mismatch */
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
 		    "%s: merge failed, capabilities mismatch\n", __func__);
+#ifdef IEEE80211_DEBUG
+		if (ieee80211_msg_assoc(ic))
+			check_bss_debug(ic, ni);
+#endif
 		ic->ic_stats.is_ibss_capmismatch++;
 		return 0;
 	}
@@ -783,26 +532,35 @@
 		ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
 		ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : ""
 	);
-	return ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+	return ieee80211_sta_join1(ieee80211_ref_node(ni));
+}
+
+/*
+ * Change the bss channel.
+ */
+void
+ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+	ic->ic_bsschan = c;
+	ic->ic_curchan = ic->ic_bsschan;
+	ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
+	ic->ic_set_channel(ic);
 }
 
 /*
  * 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)
+static int
+ieee80211_sta_join1(struct ieee80211_node *selbs)
 {
+	struct ieee80211com *ic = selbs->ni_ic;
 	struct ieee80211_node *obss;
+	int canreassoc;
 
 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
 		struct ieee80211_node_table *nt;
 		/*
-		 * Delete unusable rates; we've already checked
-		 * that the negotiated rate set is acceptable.
-		 */
-		ieee80211_fix_rate(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?
@@ -818,28 +576,106 @@
 	 * Committed to selbs, setup state.
 	 */
 	obss = ic->ic_bss;
+	/*
+	 * Check if old+new node have the same address in which
+	 * case we can reassociate when operating in sta mode.
+	 */
+	canreassoc = (obss != NULL &&
+		ic->ic_state == IEEE80211_S_RUN &&
+		IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr));
 	ic->ic_bss = selbs;		/* NB: caller assumed to bump refcnt */
 	if (obss != NULL) {
 		copy_bss(selbs, obss);
 		ieee80211_free_node(obss);
 	}
+
+	/*
+	 * Delete unusable rates; we've already checked
+	 * that the negotiated rate set is acceptable.
+	 */
+	ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates,
+		IEEE80211_F_DODEL | IEEE80211_F_JOIN);
+
+	ieee80211_setbsschan(ic, selbs->ni_chan);
 	/*
 	 * 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);
-	ic->ic_curchan = 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
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		if (canreassoc) {
+			/* Reassociate */
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
+		} else {
+			/*
+			 * Act as if we received a DEAUTH frame in case we
+			 * are invoked from the RUN state.  This will cause
+			 * us to try to re-authenticate if we are operating
+			 * as a station.
+			 */
+			ieee80211_new_state(ic, IEEE80211_S_AUTH,
+				IEEE80211_FC0_SUBTYPE_DEAUTH);
+		}
+	} else
 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
 	return 1;
 }
 
+int
+ieee80211_sta_join(struct ieee80211com *ic,
+	const struct ieee80211_scan_entry *se)
+{
+	struct ieee80211_node *ni;
+
+	ni = ieee80211_alloc_node(&ic->ic_sta, se->se_macaddr);
+	if (ni == NULL) {
+		/* XXX msg */
+		return 0;
+	}
+	/*
+	 * Expand scan state into node's format.
+	 * XXX may not need all this stuff
+	 */
+	IEEE80211_ADDR_COPY(ni->ni_bssid, se->se_bssid);
+	ni->ni_esslen = se->se_ssid[1];
+	memcpy(ni->ni_essid, se->se_ssid+2, ni->ni_esslen);
+	ni->ni_rstamp = se->se_rstamp;
+	ni->ni_tstamp.tsf = se->se_tstamp.tsf;
+	ni->ni_intval = se->se_intval;
+	ni->ni_capinfo = se->se_capinfo;
+	ni->ni_chan = se->se_chan;
+	ni->ni_timoff = se->se_timoff;
+	ni->ni_fhdwell = se->se_fhdwell;
+	ni->ni_fhindex = se->se_fhindex;
+	ni->ni_erp = se->se_erp;
+	ni->ni_rssi = se->se_rssi;
+	ni->ni_noise = se->se_noise;
+	if (se->se_htcap_ie != NULL) {
+		ieee80211_saveie(&ni->ni_htcap_ie, se->se_htcap_ie);
+		ieee80211_parse_htcap(ni, ni->ni_htcap_ie);
+	}
+	if (se->se_wpa_ie != NULL)
+		ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie);
+	if (se->se_rsn_ie != NULL)
+		ieee80211_saveie(&ni->ni_rsn_ie, se->se_rsn_ie);
+	if (se->se_wme_ie != NULL)
+		ieee80211_saveie(&ni->ni_wme_ie, se->se_wme_ie);
+	if (se->se_ath_ie != NULL)
+		ieee80211_saveath(ni, se->se_ath_ie);
+
+	ic->ic_dtim_period = se->se_dtimperiod;
+	ic->ic_dtim_count = 0;
+
+	/* NB: must be after ni_chan is setup */
+	ieee80211_setup_rates(ni, se->se_rates, se->se_xrates,
+		IEEE80211_F_DOSORT);
+
+	return ieee80211_sta_join1(ieee80211_ref_node(ni));
+}
+
 /*
  * Leave the specified IBSS/BSS network.  The node is assumed to
  * be passed in with a held reference.
@@ -872,17 +708,23 @@
 {
 #define	N(a)	(sizeof(a)/sizeof(a[0]))
 	struct ieee80211com *ic = ni->ni_ic;
-	int i, qlen;
+	int i;
 
 	/* NB: preserve ni_table */
 	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
-		ic->ic_ps_sta--;
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			ic->ic_ps_sta--;
 		ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
 		    "[%s] power save mode off, %u sta's in ps mode\n",
 		    ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
 	}
 	/*
+	 * Cleanup any HT-related state.
+	 */
+	if (ni->ni_flags & IEEE80211_NODE_HT)
+		ieee80211_ht_node_cleanup(ni);
+	/*
 	 * Clear AREF flag that marks the authorization refcnt bump
 	 * has happened.  This is probably not needed as the node
 	 * should always be removed from the table so not found but
@@ -893,13 +735,12 @@
 	/*
 	 * Drain power save queue and, if needed, clear TIM.
 	 */
-	IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen);
-	if (qlen != 0 && ic->ic_set_tim != NULL)
+	if (ieee80211_node_saveq_drain(ni) != 0 && ic->ic_set_tim != NULL)
 		ic->ic_set_tim(ni, 0);
 
 	ni->ni_associd = 0;
 	if (ni->ni_challenge != NULL) {
-		FREE(ni->ni_challenge, M_DEVBUF);
+		FREE(ni->ni_challenge, M_80211_NODE);
 		ni->ni_challenge = NULL;
 	}
 	/*
@@ -933,22 +774,33 @@
 
 	ic->ic_node_cleanup(ni);
 	if (ni->ni_wpa_ie != NULL)
-		FREE(ni->ni_wpa_ie, M_DEVBUF);
+		FREE(ni->ni_wpa_ie, M_80211_NODE);
+	if (ni->ni_rsn_ie != NULL)
+		FREE(ni->ni_rsn_ie, M_80211_NODE);
 	if (ni->ni_wme_ie != NULL)
-		FREE(ni->ni_wme_ie, M_DEVBUF);
+		FREE(ni->ni_wme_ie, M_80211_NODE);
+	if (ni->ni_ath_ie != NULL)
+		FREE(ni->ni_ath_ie, M_80211_NODE);
 	IEEE80211_NODE_SAVEQ_DESTROY(ni);
 	FREE(ni, M_80211_NODE);
 }
 
-static u_int8_t
+static int8_t
 node_getrssi(const struct ieee80211_node *ni)
 {
 	return ni->ni_rssi;
 }
 
 static void
+node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
+{
+	*rssi = ni->ni_rssi;
+	*noise = ni->ni_noise;
+}
+
+static void
 ieee80211_setup_node(struct ieee80211_node_table *nt,
-	struct ieee80211_node *ni, const u_int8_t *macaddr)
+	struct ieee80211_node *ni, const uint8_t *macaddr)
 {
 	struct ieee80211com *ic = nt->nt_ic;
 	int hash;
@@ -966,6 +818,7 @@
 	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;
+	ni->ni_ath_defkeyix = 0x7fff;
 	IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
 
 	IEEE80211_NODE_LOCK(nt);
@@ -977,7 +830,7 @@
 }
 
 struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
 {
 	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
@@ -997,7 +850,7 @@
  * once the send completes.
  */
 struct ieee80211_node *
-ieee80211_tmp_node(struct ieee80211com *ic, const u_int8_t *macaddr)
+ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr)
 {
 	struct ieee80211_node *ni;
 
@@ -1011,7 +864,7 @@
 		ieee80211_node_initref(ni);		/* mark referenced */
 		ni->ni_txpower = ic->ic_bss->ni_txpower;
 		/* NB: required by ieee80211_fix_rate */
-		ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+		ieee80211_node_set_chan(ic, ni);
 		ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey,
 			IEEE80211_KEYIX_NONE);
 		/* XXX optimize away */
@@ -1027,7 +880,7 @@
 }
 
 struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr)
 {
 	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
@@ -1042,7 +895,7 @@
 		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);
-		ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+		ieee80211_node_set_chan(ic, ni);
 		ni->ni_rsn = ic->ic_bss->ni_rsn;
 	} else
 		ic->ic_stats.is_rx_nodealloc++;
@@ -1052,10 +905,10 @@
 static struct ieee80211_node *
 #ifdef IEEE80211_DEBUG_REFCNT
 _ieee80211_find_node_debug(struct ieee80211_node_table *nt,
-	const u_int8_t *macaddr, const char *func, int line)
+	const uint8_t *macaddr, const char *func, int line)
 #else
 _ieee80211_find_node(struct ieee80211_node_table *nt,
-	const u_int8_t *macaddr)
+	const uint8_t *macaddr)
 #endif
 {
 	struct ieee80211_node *ni;
@@ -1087,9 +940,9 @@
 struct ieee80211_node *
 #ifdef IEEE80211_DEBUG_REFCNT
 ieee80211_find_node_debug(struct ieee80211_node_table *nt,
-	const u_int8_t *macaddr, const char *func, int line)
+	const uint8_t *macaddr, const char *func, int line)
 #else
-ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
 #endif
 {
 	struct ieee80211_node *ni;
@@ -1108,7 +961,7 @@
  */
 struct ieee80211_node *
 ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
-	const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+	const uint8_t macaddr[IEEE80211_ADDR_LEN])
 {
 	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
@@ -1121,158 +974,30 @@
 		ni->ni_rates = ic->ic_bss->ni_rates;
 		if (ic->ic_newassoc != NULL)
 			ic->ic_newassoc(ni, 1);
-		/* XXX not right for 802.1x/WPA */
-		ieee80211_node_authorize(ni);
 		if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
 			/*
-			 * Blindly propagate capabilities based on the
-			 * local configuration.  In particular this permits
-			 * us to use QoS to disable ACK's.
+			 * In adhoc demo mode there are no management
+			 * frames to use to discover neighbor capabilities,
+			 * so blindly propagate the local configuration 
+			 * so we can do interesting things (e.g. use
+			 * WME to disable ACK's).
 			 */
 			if (ic->ic_flags & IEEE80211_F_WME)
 				ni->ni_flags |= IEEE80211_NODE_QOS;
+			if (ic->ic_flags & IEEE80211_F_FF)
+				ni->ni_flags |= IEEE80211_NODE_FF;
 		}
+		/* XXX not right for 802.1x/WPA */
+		ieee80211_node_authorize(ni);
 	}
 	return ni;
 }
 
-#ifdef IEEE80211_DEBUG
-static void
-dump_probe_beacon(u_int8_t subtype, int isnew,
-	const u_int8_t mac[IEEE80211_ADDR_LEN],
-	const struct ieee80211_scanparams *sp)
-{
-
-	printf("[%s] %s%s on chan %u (bss chan %u) ",
-	    ether_sprintf(mac), isnew ? "new " : "",
-	    ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
-	    sp->chan, sp->bchan);
-	ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
-	printf("\n");
-
-	if (isnew) {
-		printf("[%s] caps 0x%x bintval %u erp 0x%x", 
-			ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
-		if (sp->country != NULL) {
-#ifdef __FreeBSD__
-			printf(" country info %*D",
-				sp->country[1], sp->country+2, " ");
-#else
-			int i;
-			printf(" country info");
-			for (i = 0; i < sp->country[1]; i++)
-				printf(" %02x", sp->country[i+2]);
-#endif
-		}
-		printf("\n");
-	}
-}
-#endif /* IEEE80211_DEBUG */
-
-static void
-saveie(u_int8_t **iep, const u_int8_t *ie)
-{
-
-	if (ie == NULL)
-		*iep = NULL;
-	else
-		ieee80211_saveie(iep, ie);
-}
-
-/*
- * Process a beacon or probe response frame.
- */
-void
-ieee80211_add_scan(struct ieee80211com *ic,
-	const struct ieee80211_scanparams *sp,
-	const struct ieee80211_frame *wh,
-	int subtype, int rssi, int rstamp)
-{
-#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-	struct ieee80211_node_table *nt = &ic->ic_scan;
-	struct ieee80211_node *ni;
-	int newnode = 0;
-
-	ni = ieee80211_find_node(nt, wh->i_addr2);
-	if (ni == NULL) {
-		/*
-		 * Create a new entry.
-		 */
-		ni = ic->ic_node_alloc(nt);
-		if (ni == NULL) {
-			ic->ic_stats.is_rx_nodealloc++;
-			return;
-		}
-		ieee80211_setup_node(nt, ni, wh->i_addr2);
-		/*
-		 * XXX 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_set_chan(ic, ni, ic->ic_curchan);
-		ni->ni_rsn = ic->ic_bss->ni_rsn;
-		newnode = 1;
-	}
-#ifdef IEEE80211_DEBUG
-	if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
-		dump_probe_beacon(subtype, newnode, wh->i_addr2, sp);
-#endif
-	/* XXX ap beaconing multiple ssid w/ same bssid */
-	if (sp->ssid[1] != 0 &&
-	    (ISPROBE(subtype) || ni->ni_esslen == 0)) {
-		ni->ni_esslen = sp->ssid[1];
-		memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
-		memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
-	}
-	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.data, sp->tstamp, sizeof(ni->ni_tstamp));
-	ni->ni_intval = sp->bintval;
-	ni->ni_capinfo = sp->capinfo;
-	ni->ni_chan = &ic->ic_channels[sp->chan];
-	ni->ni_fhdwell = sp->fhdwell;
-	ni->ni_fhindex = sp->fhindex;
-	ni->ni_erp = sp->erp;
-	if (sp->tim != NULL) {
-		struct ieee80211_tim_ie *ie =
-		    (struct ieee80211_tim_ie *) sp->tim;
-
-		ni->ni_dtim_count = ie->tim_count;
-		ni->ni_dtim_period = ie->tim_period;
-	}
-	/*
-	 * 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 = sp->timoff;
-	/*
-	 * Record optional information elements that might be
-	 * used by applications or drivers.
-	 */
-	saveie(&ni->ni_wme_ie, sp->wme);
-	saveie(&ni->ni_wpa_ie, sp->wpa);
-
-	/* NB: must be after ni_chan is setup */
-	ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT);
-
-	if (!newnode)
-		ieee80211_free_node(ni);
-#undef ISPROBE
-}
-
 void
 ieee80211_init_neighbor(struct ieee80211_node *ni,
 	const struct ieee80211_frame *wh,
 	const struct ieee80211_scanparams *sp)
 {
-
-	IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
-	    "%s: %p<%s>\n", __func__, ni, ether_sprintf(ni->ni_macaddr));
 	ni->ni_esslen = sp->ssid[1];
 	memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
 	IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
@@ -1288,9 +1013,15 @@
 		ieee80211_saveie(&ni->ni_wme_ie, sp->wme);
 	if (sp->wpa != NULL)
 		ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa);
+	if (sp->rsn != NULL)
+		ieee80211_saveie(&ni->ni_rsn_ie, sp->rsn);
+	if (sp->ath != NULL)
+		ieee80211_saveath(ni, sp->ath);
 
 	/* NB: must be after ni_chan is setup */
-	ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT);
+	ieee80211_setup_rates(ni, sp->rates, sp->xrates,
+		IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+		IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
 }
 
 /*
@@ -1323,6 +1054,9 @@
 	((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)
+#define	IS_BAR(wh) \
+	((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_BAR)
+
 /*
  * Locate the node for sender, track state, and then pass the
  * (referenced) node up to the 802.11 layer for its use.  We
@@ -1343,17 +1077,11 @@
 	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
 
-	/* 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? */
+	nt = &ic->ic_sta;
 	IEEE80211_NODE_LOCK(nt);
-	if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+	if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
 		ni = _ieee80211_find_node(nt, wh->i_addr1);
 	else
 		ni = _ieee80211_find_node(nt, wh->i_addr2);
@@ -1385,19 +1113,14 @@
 	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
 
-	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;
+	nt = &ic->ic_sta;
 	IEEE80211_NODE_LOCK(nt);
 	if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax)
 		ni = nt->nt_keyixmap[keyix];
 	else
 		ni = NULL;
 	if (ni == NULL) {
-		if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+		if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
 			ni = _ieee80211_find_node(nt, wh->i_addr1);
 		else
 			ni = _ieee80211_find_node(nt, wh->i_addr2);
@@ -1419,13 +1142,13 @@
 				nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni);
 			}
 		}
-	} else {
+	} else
 		ieee80211_ref_node(ni);
-	}
 	IEEE80211_NODE_UNLOCK(nt);
 
 	return ni;
 }
+#undef IS_BAR
 #undef IS_PSPOLL
 #undef IS_CTL
 
@@ -1435,10 +1158,10 @@
  */
 struct ieee80211_node *
 #ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_txnode_debug(struct ieee80211com *ic, const u_int8_t *macaddr,
+ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr,
 	const char *func, int line)
 #else
-ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr)
+ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
 #endif
 {
 	struct ieee80211_node_table *nt = &ic->ic_sta;
@@ -1454,8 +1177,19 @@
 	IEEE80211_NODE_LOCK(nt);
 	if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
 		ni = ieee80211_ref_node(ic->ic_bss);
-	else
+	else {
 		ni = _ieee80211_find_node(nt, macaddr);
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP && 
+		    (ni != NULL && ni->ni_associd == 0)) {
+			/*
+			 * Station is not associated; don't permit the
+			 * data frame to be sent by returning NULL.  This
+			 * is kinda a kludge but the least intrusive way
+			 * to add this check into all drivers.
+			 */
+			ieee80211_unref_node(&ni);	/* NB: null's ni */
+		}
+	}
 	IEEE80211_NODE_UNLOCK(nt);
 
 	if (ni == NULL) {
@@ -1480,60 +1214,21 @@
 }
 
 /*
- * Like find but search based on the channel too.
- */
-struct ieee80211_node *
-#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;
-
-	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_chan == chan) {
-			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<%s> refcnt %d\n", __func__,
-#endif
-			    ni, ether_sprintf(ni->ni_macaddr),
-			    ieee80211_node_refcnt(ni));
-			break;
-		}
-	}
-	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 uint8_t *macaddr, u_int ssidlen, const uint8_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)
+	const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid)
 #endif
 {
 #define	MATCH_SSID(ni, ssid, ssidlen) \
 	(ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0)
-	static const u_int8_t zeromac[IEEE80211_ADDR_LEN];
-	struct ieee80211com *ic = nt->nt_ic;
+	static const uint8_t zeromac[IEEE80211_ADDR_LEN];
 	struct ieee80211_node *ni;
 	int hash;
 
@@ -1557,14 +1252,8 @@
 	}
 	if (ni != NULL) {
 		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<%s> refcnt %d\n", __func__,
-#endif
-		     ni, ether_sprintf(ni->ni_macaddr),
+		IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+		     REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr),
 		     ieee80211_node_refcnt(ni));
 	}
 	IEEE80211_NODE_UNLOCK(nt);
@@ -1748,46 +1437,6 @@
 		}
 		node_reclaim(nt, ni);
 	}
-	ieee80211_reset_erp(ic);
-}
-
-static void
-ieee80211_free_allnodes(struct ieee80211_node_table *nt)
-{
-
-	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_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;
-	}
-	TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, tni) {
-		if (ni->ni_inact && --ni->ni_inact == 0) {
-			IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
-			    "[%s] scan candidate purged from cache "
-			    "(refcnt %u)\n", ether_sprintf(ni->ni_macaddr),
-			    ieee80211_node_refcnt(ni));
-			node_reclaim(nt, ni);
-		}
-	}
-	IEEE80211_NODE_UNLOCK(nt);
-
-	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
 }
 
 /*
@@ -1812,8 +1461,6 @@
 		   ic->ic_opmode == IEEE80211_M_AHDEMO);
 	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:
 	IEEE80211_NODE_LOCK(nt);
 	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
@@ -1826,7 +1473,8 @@
 		 * will be reclaimed when the last reference to them
 		 * goes away (when frame xmits complete).
 		 */
-		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		     ic->ic_opmode == IEEE80211_M_STA) &&
 		    (ni->ni_flags & IEEE80211_NODE_AREF) == 0)
 			continue;
 		/*
@@ -1839,59 +1487,38 @@
 			m_freem(ni->ni_rxfrag[0]);
 			ni->ni_rxfrag[0] = NULL;
 		}
+		if (ni->ni_inact > 0)
+			ni->ni_inact--;
 		/*
 		 * 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 || isadhoc) {
 			/*
-			 * 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.
+			 * Age frames on the power save queue.
 			 */
-			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, "[%s] discard frame, age %u\n", ether_sprintf(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,
-					    "[%s] discard %u frames for age\n",
-					    ether_sprintf(ni->ni_macaddr),
-					    discard);
-					IEEE80211_NODE_STAT_ADD(ni,
-						ps_discard, discard);
-					if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0)
-						ic->ic_set_tim(ni, 0);
-				}
-			}
+			if (ieee80211_node_saveq_age(ni) != 0 &&
+			    IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
+			    ic->ic_set_tim != NULL)
+				ic->ic_set_tim(ni, 0);
 			/*
 			 * 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...).
+			 *
+			 * XXX don't probe the station unless we've
+			 *     received a frame from them (and have
+			 *     some idea of the rates they are capable
+			 *     of); this will get fixed more properly
+			 *     soon with better handling of the rate set.
 			 */
-			if (0 < ni->ni_inact &&
-			    ni->ni_inact <= ic->ic_inact_probe) {
+			if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+			    (0 < ni->ni_inact &&
+			     ni->ni_inact <= ic->ic_inact_probe) &&
+			    ni->ni_rates.rs_nrates != 0) {
 				IEEE80211_NOTE(ic,
 				    IEEE80211_MSG_INACT | IEEE80211_MSG_NODE,
 				    ni, "%s",
@@ -1910,7 +1537,8 @@
 				goto restart;
 			}
 		}
-		if (ni->ni_inact <= 0) {
+		if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+		    ni->ni_inact <= 0) {
 			IEEE80211_NOTE(ic,
 			    IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni,
 			    "station timed out due to inactivity "
@@ -1927,9 +1555,10 @@
 			 * completed or by ieee80211_node_leave.
 			 *
 			 * 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.
+			 * in case the driver takes a lock, as this can result
+			 * in a LOR between the node lock and the driver lock.
 			 */
+			ieee80211_ref_node(ni);
 			IEEE80211_NODE_UNLOCK(nt);
 			if (ni->ni_associd != 0) {
 				IEEE80211_SEND_MGMT(ic, ni,
@@ -1937,6 +1566,7 @@
 				    IEEE80211_REASON_AUTH_EXPIRE);
 			}
 			ieee80211_node_leave(ic, ni);
+			ieee80211_free_node(ni);
 			ic->ic_stats.is_node_timeout++;
 			goto restart;
 		}
@@ -1944,8 +1574,23 @@
 	IEEE80211_NODE_UNLOCK(nt);
 
 	IEEE80211_SCAN_UNLOCK(nt);
+}
 
-	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
+void
+ieee80211_node_timeout(void *arg)
+{
+	struct ieee80211com *ic = arg;
+
+	ieee80211_scan_timeout(ic);
+	ieee80211_timeout_stations(&ic->ic_sta);
+
+	IEEE80211_LOCK(ic);
+	ieee80211_erp_timeout(ic);
+	ieee80211_ht_timeout(ic);
+	IEEE80211_UNLOCK(ic);
+
+	callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
+		ieee80211_node_timeout, ic);
 }
 
 void
@@ -1983,18 +1628,24 @@
 	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_txseqs[IEEE80211_NONQOS_TID],
+		ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT,
+		ni->ni_rxseqs[IEEE80211_NONQOS_TID] & 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("\trstamp %u rssi %d noise %d intval %u capinfo 0x%x\n",
+		ni->ni_rstamp, ni->ni_rssi, ni->ni_noise,
+		ni->ni_intval, ni->ni_capinfo);
 	printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n",
 		ether_sprintf(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);
+	printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n",
+		ni->ni_htcap, ni->ni_htparam,
+		ni->ni_htctlchan, ni->ni_ht2ndchan);
+	printf("\thtopmode %x htstbc %x chw %u\n",
+		ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw);
 }
 
 void
@@ -2004,6 +1655,13 @@
 		(ieee80211_iter_func *) ieee80211_dump_node, nt);
 }
 
+void
+ieee80211_notify_erp(struct ieee80211com *ic)
+{
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+		ieee80211_beacon_notify(ic, IEEE80211_BEACON_ERP);
+}
+
 /*
  * Handle a station joining an 11g network.
  */
@@ -2011,6 +1669,8 @@
 ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
 
+	IEEE80211_LOCK_ASSERT(ic);
+
 	/*
 	 * Station isn't capable of short slot time.  Bump
 	 * the count of long slot time stations and disable
@@ -2020,11 +1680,18 @@
 	 */
 	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
 		ic->ic_longslotsta++;
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
-		    "[%s] station needs long slot time, count %d\n",
-		    ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta);
+		IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+		    "station needs long slot time, count %d",
+		    ic->ic_longslotsta);
 		/* XXX vap's w/ conflicting needs won't work */
-		ieee80211_set_shortslottime(ic, 0);
+		if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) {
+			/*
+			 * Don't force slot time when switched to turbo
+			 * mode as non-ERP stations won't be present; this
+			 * need only be done when on the normal G channel.
+			 */
+			ieee80211_set_shortslottime(ic, 0);
+		}
 	}
 	/*
 	 * If the new station is not an ERP station
@@ -2033,30 +1700,30 @@
 	 */
 	if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
 		ic->ic_nonerpsta++;
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
-		    "[%s] station is !ERP, %d non-ERP stations associated\n",
-		    ether_sprintf(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;
-		}
+		IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+		    "station is !ERP, %d non-ERP stations associated",
+		    ic->ic_nonerpsta);
 		/*
 		 * 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,
-			    "[%s] station needs long preamble\n",
-			    ether_sprintf(ni->ni_macaddr));
+			IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+			    "%s", "station needs long preamble");
 			ic->ic_flags |= IEEE80211_F_USEBARKER;
 			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
 		}
-		if (ic->ic_nonerpsta == 1)
-			ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
+		/*
+		 * If protection is configured, enable it.
+		 */
+		if (ic->ic_protmode != IEEE80211_PROT_NONE &&
+		    ic->ic_nonerpsta == 1 &&
+		    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "%s: enable use of protection\n", __func__);
+			ic->ic_flags |= IEEE80211_F_USEPROT;
+			ieee80211_notify_erp(ic);
+		}
 	} else
 		ni->ni_flags |= IEEE80211_NODE_ERP;
 }
@@ -2067,8 +1734,9 @@
 	int newassoc;
 
 	if (ni->ni_associd == 0) {
-		u_int16_t aid;
+		uint16_t aid;
 
+		IEEE80211_LOCK(ic);
 		/*
 		 * It would be good to search the bitmap
 		 * more efficiently, but this will do for now.
@@ -2079,40 +1747,70 @@
 				break;
 		}
 		if (aid >= ic->ic_max_aid) {
+			IEEE80211_UNLOCK(ic);
 			IEEE80211_SEND_MGMT(ic, ni, resp,
 			    IEEE80211_REASON_ASSOC_TOOMANY);
 			ieee80211_node_leave(ic, ni);
 			return;
 		}
 		ni->ni_associd = aid | 0xc000;
+		ni->ni_jointime = time_uptime;
 		IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
 		ic->ic_sta_assoc++;
-		newassoc = 1;
-		if (ic->ic_curmode == IEEE80211_MODE_11G)
+
+		if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+			ieee80211_ht_node_join(ni);
+		if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+		    IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
 			ieee80211_node_join_11g(ic, ni);
+		IEEE80211_UNLOCK(ic);
+
+		newassoc = 1;
 	} else
 		newassoc = 0;
 
-	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
-	    "[%s] station %sassociated at aid %d: %s preamble, %s slot time%s%s\n",
-	    ether_sprintf(ni->ni_macaddr), newassoc ? "" : "re",
+	IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+	    "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
 	    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" : ""
+	    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+	    ni->ni_flags & IEEE80211_NODE_HT ?
+		(ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
+	    ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
+	    IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
+		", fast-frames" : "",
+	    IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
+		", turbo" : ""
 	);
 
 	/* give driver a chance to setup state like ni_txrate */
 	if (ic->ic_newassoc != NULL)
 		ic->ic_newassoc(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);
+	ieee80211_notify_node_join(ic, ni,
+	    resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+}
+
+static void
+disable_protection(struct ieee80211com *ic)
+{
+	KASSERT(ic->ic_nonerpsta == 0 &&
+	    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0,
+	   ("%d non ERP stations, flags 0x%x", ic->ic_nonerpsta,
+	   ic->ic_flags_ext));
+
+	ic->ic_flags &= ~IEEE80211_F_USEPROT;
+	/* XXX verify mode? */
+	if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
+		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+	}
+	ieee80211_notify_erp(ic);
 }
 
 /*
@@ -2122,9 +1820,11 @@
 ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
 
-	KASSERT(ic->ic_curmode == IEEE80211_MODE_11G,
-	     ("not in 11g, bss %u:0x%x, curmode %u", ni->ni_chan->ic_freq,
-	      ni->ni_chan->ic_flags, ic->ic_curmode));
+	IEEE80211_LOCK_ASSERT(ic);
+
+	KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan),
+	     ("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq,
+	      ic->ic_bsschan->ic_flags, ic->ic_curmode));
 
 	/*
 	 * If a long slot station do the slot time bookkeeping.
@@ -2133,9 +1833,9 @@
 		KASSERT(ic->ic_longslotsta > 0,
 		    ("bogus long slot station count %d", ic->ic_longslotsta));
 		ic->ic_longslotsta--;
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
-		    "[%s] long slot time station leaves, count now %d\n",
-		    ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta);
+		IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+		    "long slot time station leaves, count now %d",
+		    ic->ic_longslotsta);
 		if (ic->ic_longslotsta == 0) {
 			/*
 			 * Re-enable use of short slot time if supported
@@ -2157,27 +1857,44 @@
 		KASSERT(ic->ic_nonerpsta > 0,
 		    ("bogus non-ERP station count %d", ic->ic_nonerpsta));
 		ic->ic_nonerpsta--;
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
-		    "[%s] non-ERP station leaves, count now %d\n",
-		    ether_sprintf(ni->ni_macaddr), ic->ic_nonerpsta);
-		if (ic->ic_nonerpsta == 0) {
+		IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+		    "non-ERP station leaves, count now %d%s", ic->ic_nonerpsta,
+		    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
+			" (non-ERP sta present)" : "");
+		if (ic->ic_nonerpsta == 0 &&
+		    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 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;
-			}
-			ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
+			disable_protection(ic);
 		}
 	}
 }
 
 /*
+ * Time out presence of an overlapping bss with non-ERP
+ * stations.  When operating in hostap mode we listen for
+ * beacons from other stations and if we identify a non-ERP
+ * station is present we enable protection.  To identify
+ * when all non-ERP stations are gone we time out this
+ * condition.
+ */
+static void
+ieee80211_erp_timeout(struct ieee80211com *ic)
+{
+
+	IEEE80211_LOCK_ASSERT(ic);
+
+	if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
+	    time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "%s\n", "age out non-ERP sta present on channel");
+		ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
+		if (ic->ic_nonerpsta == 0)
+			disable_protection(ic);
+	}
+}
+
+/*
  * Handle bookkeeping for station deauthentication/disassociation
  * when operating as an ap.
  */
@@ -2186,13 +1903,10 @@
 {
 	struct ieee80211_node_table *nt = ni->ni_table;
 
-	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
-	    "[%s] station with aid %d leaves\n",
-	    ether_sprintf(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,
+	IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+	    "station with aid %d leaves", IEEE80211_NODE_AID(ni));
+
+	KASSERT(ic->ic_opmode != IEEE80211_M_STA,
 		("unexpected operating mode %u", ic->ic_opmode));
 	/*
 	 * If node wasn't previously associated all
@@ -2209,12 +1923,18 @@
 	 */
 	if (ic->ic_auth->ia_node_leave != NULL)
 		ic->ic_auth->ia_node_leave(ic, ni);
+
+	IEEE80211_LOCK(ic);
 	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)
+	if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+		ieee80211_ht_node_leave(ni);
+	if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+	    IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
 		ieee80211_node_leave_11g(ic, ni);
+	IEEE80211_UNLOCK(ic);
 	/*
 	 * Cleanup station state.  In particular clear various
 	 * state that might otherwise be reused if the node
@@ -2237,38 +1957,30 @@
 		ieee80211_free_node(ni);
 }
 
-u_int8_t
+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;
+	int rssi_samples;
+	int32_t rssi_total;
 	struct ieee80211_node *ni;
 
 	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);
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+			    (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)) {
+				int8_t rssi = ic->ic_node_getrssi(ni);
+				if (rssi != 0) {
+					rssi_samples++;
+					rssi_total += rssi;
+				}
 			}
 		break;
 	case IEEE80211_M_MONITOR:	/* XXX */
@@ -2283,35 +1995,16 @@
 #undef NZ
 }
 
-/*
- * Indicate whether there are frames queued for a station in power-save mode.
- */
-static void
-ieee80211_set_tim(struct ieee80211_node *ni, int set)
+void
+ieee80211_getsignal(struct ieee80211com *ic, int8_t *rssi, int8_t *noise)
 {
-	struct ieee80211com *ic = ni->ni_ic;
-	u_int16_t aid;
 
-	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);
+	if (ic->ic_bss == NULL)		/* NB: shouldn't happen */
+		return;
+	ic->ic_node_getsignal(ic->ic_bss, rssi, noise);
+	/* for non-station mode return avg'd rssi accounting */
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		*rssi = ieee80211_getrssi(ic);
 }
 
 /*
@@ -2321,8 +2014,7 @@
 static void
 ieee80211_node_table_init(struct ieee80211com *ic,
 	struct ieee80211_node_table *nt,
-	const char *name, int inact, int keyixmax,
-	void (*timeout)(struct ieee80211_node_table *))
+	const char *name, int inact, int keyixmax)
 {
 
 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
@@ -2336,7 +2028,6 @@
 	nt->nt_name = name;
 	nt->nt_scangen = 1;
 	nt->nt_inact_init = inact;
-	nt->nt_timeout = timeout;
 	nt->nt_keyixmax = keyixmax;
 	if (nt->nt_keyixmax > 0) {
 		MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
@@ -2350,7 +2041,7 @@
 		nt->nt_keyixmap = NULL;
 }
 
-void
+static void
 ieee80211_node_table_reset(struct ieee80211_node_table *nt)
 {
 
@@ -2358,7 +2049,6 @@
 		"%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);
 }
--- /dev/null
+++ sys/net80211/ieee80211_power.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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_power.h,v 1.1 2007/06/11 03:36:54 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_POWER_H_
+#define _NET80211_IEEE80211_POWER_H_
+
+struct ieee80211com;
+
+void	ieee80211_power_attach(struct ieee80211com *);
+void	ieee80211_power_lateattach(struct ieee80211com *);
+void	ieee80211_power_detach(struct ieee80211com *);
+
+int	ieee80211_node_saveq_drain(struct ieee80211_node *);
+int	ieee80211_node_saveq_age(struct ieee80211_node *);
+void	ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
+void	ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
+void	ieee80211_sta_pwrsave(struct ieee80211com *, int enable);
+
+void	ieee80211_power_poll(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_POWER_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_power.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_power.c,v 1.2 2007/09/17 19:07:22 sam Exp $");
+
+/*
+ * IEEE 802.11 power save support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/kernel.h>
+ 
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+static void ieee80211_set_tim(struct ieee80211_node *ni, int set);
+
+void
+ieee80211_power_attach(struct ieee80211com *ic)
+{
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+	    ic->ic_opmode == IEEE80211_M_IBSS) {
+		/* NB: driver should override */
+		ic->ic_set_tim = ieee80211_set_tim;
+	}
+}
+
+void
+ieee80211_power_lateattach(struct ieee80211com *ic)
+{
+	/*
+	 * Allocate these only if needed.  Beware that we
+	 * know adhoc mode doesn't support ATIM yet...
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t);
+		MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len,
+			M_DEVBUF, M_NOWAIT | M_ZERO);
+		if (ic->ic_tim_bitmap == NULL) {
+			printf("%s: no memory for TIM bitmap!\n", __func__);
+			/* XXX good enough to keep from crashing? */
+			ic->ic_tim_len = 0;
+		}
+	}
+}
+
+void
+ieee80211_power_detach(struct ieee80211com *ic)
+{
+	if (ic->ic_tim_bitmap != NULL) {
+		FREE(ic->ic_tim_bitmap, M_DEVBUF);
+		ic->ic_tim_bitmap = NULL;
+	}
+}
+
+/*
+ * Clear any frames queued on a node's power save queue.
+ * The number of frames that were present is returned.
+ */
+int
+ieee80211_node_saveq_drain(struct ieee80211_node *ni)
+{
+	int qlen;
+
+	IEEE80211_NODE_SAVEQ_LOCK(ni);
+	qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
+	_IF_DRAIN(&ni->ni_savedq);
+	IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+	return qlen;
+}
+
+/*
+ * 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
+ * so we can check and/or adjust only the head of the list.
+ * If a frame's age exceeds the threshold then discard it.
+ * The number of frames discarded is returned so the caller
+ * can check if it needs to adjust the tim.
+ */
+int
+ieee80211_node_saveq_age(struct ieee80211_node *ni)
+{
+	int discard = 0;
+
+	if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+		struct mbuf *m;
+
+		IEEE80211_NODE_SAVEQ_LOCK(ni);
+		while (IF_POLL(&ni->ni_savedq, m) != NULL &&
+		     M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(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);
+
+		IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni,
+		    "discard %u frames for age", discard);
+		IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard);
+	}
+	return discard;
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static void
+ieee80211_set_tim(struct ieee80211_node *ni, int set)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t aid;
+
+	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--;
+		}
+		/* NB: we know ic is in RUN state so no need to check */
+		ic->ic_update_beacon(ic, IEEE80211_BEACON_TIM);
+	}
+	IEEE80211_BEACON_UNLOCK(ic);
+}
+
+/*
+ * 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 ieee80211_node *ni, struct mbuf *m)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	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,
+			"[%s] pwr save q overflow, drops %d (size %d)\n",
+			ether_sprintf(ni->ni_macaddr), 
+			ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
+#ifdef IEEE80211_DEBUG
+		if (ieee80211_msg_dumppkts(ic))
+			ieee80211_dump_pkt(ic, 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.
+	 */
+	/* TU -> secs.  XXX handle overflow? */
+	age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000;
+	_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
+	IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		"[%s] save frame with age %d, %u now queued\n",
+		ether_sprintf(ni->ni_macaddr), age, qlen);
+
+	if (qlen == 1 && ic->ic_set_tim != NULL)
+		ic->ic_set_tim(ni, 1);
+}
+
+/*
+ * Handle station power-save state change.
+ */
+void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m, *mhead, *mtail;
+	int mcount;
+
+	if (enable) {
+		if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+			ic->ic_ps_sta++;
+		ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+		IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+		    "power save mode on, %u sta's in ps mode", 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_NOTE(ic, IEEE80211_MSG_POWER, ni,
+	    "power save mode off, %u sta's in ps mode", 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) {
+		if (ic->ic_set_tim != NULL)
+			ic->ic_set_tim(ni, 0);		/* just in case */
+		return;
+	}
+	IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+	    "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni));
+	/*
+	 * Unload the frames from the ps q but don't send them
+	 * to the driver yet.  We do this in two stages to minimize
+	 * locking but also because there's no easy way to preserve
+	 * ordering given the existing ifnet access mechanisms.
+	 * XXX could be optimized
+	 */
+	IEEE80211_NODE_SAVEQ_LOCK(ni);
+	mcount = IEEE80211_NODE_SAVEQ_QLEN(ni);
+	mhead = mtail = NULL;
+	for (;;) {
+		_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
+		if (m == NULL)
+			break;
+		if (mhead == NULL) {
+			mhead = m;
+			m->m_nextpkt = NULL;
+		} else
+			mtail->m_nextpkt = m;
+		mtail = m;
+	}
+	IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+	if (mhead != NULL) {
+		/* XXX need different driver interface */
+		/* XXX bypasses q max */
+		IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount);
+	}
+	if (ic->ic_set_tim != NULL)
+		ic->ic_set_tim(ni, 0);
+}
+
+/*
+ * Handle power-save state change in station mode.
+ */
+void
+ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
+{
+	struct ieee80211_node *ni = ic->ic_bss;
+	int qlen;
+
+	if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0)))
+		return;
+
+	IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+	    "sta power save mode %s", enable ? "on" : "off");
+	if (!enable) {
+		ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+		ieee80211_send_nulldata(ieee80211_ref_node(ni));
+		/*
+		 * Flush any queued frames; we can do this immediately
+		 * because we know they'll be queued behind the null
+		 * data frame we send the ap.
+		 * XXX can we use a data frame to take us out of ps?
+		 */
+		qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
+		if (qlen != 0) {
+			IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+			    "flush ps queue, %u packets queued", qlen);
+			for (;;) {
+				struct mbuf *m;
+
+				IEEE80211_NODE_SAVEQ_LOCK(ni);
+				_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
+				IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+				if (m == NULL)
+					break;
+				/* XXX need different driver interface */
+				/* XXX bypasses q max */
+				IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+			}
+		}
+	} else {
+		ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+		ieee80211_send_nulldata(ieee80211_ref_node(ni));
+	}
+}
--- /dev/null
+++ sys/net80211/ieee80211_ht.c
@@ -0,0 +1,2002 @@
+/*-
+ * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ht.c,v 1.4.2.3.2.1 2008/02/05 18:19:12 sam Exp $");
+#endif
+
+/*
+ * IEEE 802.11n protocol support.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h> 
+#include <sys/endian.h>
+ 
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+/* define here, used throughout file */
+#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
+#define	SM(_v, _f)	(((_v) << _f##_S) & _f)
+
+/* XXX need max array size */
+/* NB: these are for HT20 w/ long GI */
+const int ieee80211_htrates[16] = {
+	13,		/* IFM_IEEE80211_MCS0 */
+	26,		/* IFM_IEEE80211_MCS1 */
+	39,		/* IFM_IEEE80211_MCS2 */
+	52,		/* IFM_IEEE80211_MCS3 */
+	78,		/* IFM_IEEE80211_MCS4 */
+	104,		/* IFM_IEEE80211_MCS5 */
+	117,		/* IFM_IEEE80211_MCS6 */
+	130,		/* IFM_IEEE80211_MCS7 */
+	26,		/* IFM_IEEE80211_MCS8 */
+	52,		/* IFM_IEEE80211_MCS9 */
+	78,		/* IFM_IEEE80211_MCS10 */
+	104,		/* IFM_IEEE80211_MCS11 */
+	156,		/* IFM_IEEE80211_MCS12 */
+	208,		/* IFM_IEEE80211_MCS13 */
+	234,		/* IFM_IEEE80211_MCS14 */
+	260,		/* IFM_IEEE80211_MCS15 */
+};
+
+static const struct ieee80211_htrateset ieee80211_rateset_11n =
+	{ 16, {
+	/* MCS: 6.5   13 19.5   26   39  52 58.5  65  13  26 */
+	          0,   1,   2,   3,   4,  5,   6,  7,  8,  9,
+	/*       39   52   78  104  117, 130 */
+		 10,  11,  12,  13,  14,  15 }
+	};
+
+#ifdef IEEE80211_AMPDU_AGE
+/* XXX public for sysctl hookup */
+int	ieee80211_ampdu_age = -1;	/* threshold for ampdu reorder q (ms) */
+#endif
+int	ieee80211_recv_bar_ena = 1;
+
+#define	IEEE80211_AGGR_TIMEOUT	msecs_to_ticks(250)
+#define	IEEE80211_AGGR_MINRETRY	msecs_to_ticks(10*1000)
+#define	IEEE80211_AGGR_MAXTRIES	3
+
+static int ieee80211_addba_request(struct ieee80211_node *ni,
+	struct ieee80211_tx_ampdu *tap,
+	int dialogtoken, int baparamset, int batimeout);
+static int ieee80211_addba_response(struct ieee80211_node *ni,
+	struct ieee80211_tx_ampdu *tap,
+	int code, int baparamset, int batimeout);
+static void ieee80211_addba_stop(struct ieee80211_node *ni,
+	struct ieee80211_tx_ampdu *tap);
+static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
+	const uint8_t *frm, const uint8_t *efrm);
+
+void
+ieee80211_ht_attach(struct ieee80211com *ic)
+{
+#ifdef IEEE80211_AMPDU_AGE
+	if (ieee80211_ampdu_age == -1)
+		ieee80211_ampdu_age = msecs_to_ticks(500);
+#endif
+
+	/* setup default aggregation policy */
+	ic->ic_recv_action = ieee80211_aggr_recv_action;
+	ic->ic_send_action = ieee80211_send_action;
+	ic->ic_addba_request = ieee80211_addba_request;
+	ic->ic_addba_response = ieee80211_addba_response;
+	ic->ic_addba_stop = ieee80211_addba_stop;
+
+	ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
+	ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
+
+	/* XXX get from driver */
+	ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
+	ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
+	ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
+	ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
+
+	if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+		/*
+		 * Device is HT capable; enable all HT-related
+		 * facilities by default.
+		 * XXX these choices may be too aggressive.
+		 */
+		ic->ic_flags_ext |= IEEE80211_FEXT_HT
+				 |  IEEE80211_FEXT_HTCOMPAT
+				 ;
+		if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
+			ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+		/* XXX infer from channel list? */
+		if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+			ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
+			if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
+				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+		}
+		/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
+		ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+		if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
+			ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+		ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+		if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
+			ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+	}
+}
+
+void
+ieee80211_ht_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+ht_announce(struct ieee80211com *ic, int mode,
+	const struct ieee80211_htrateset *rs)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	int i, rate, mword;
+
+	if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]);
+	for (i = 0; i < rs->rs_nrates; i++) {
+		mword = ieee80211_rate2media(ic,
+		    rs->rs_rates[i] | IEEE80211_RATE_MCS, mode);
+		if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
+			continue;
+		rate = ieee80211_htrates[rs->rs_rates[i]];
+		printf("%s%d%sMbps", (i != 0 ? " " : ""),
+		    rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
+	}
+	printf("\n");
+}
+
+void
+ieee80211_ht_announce(struct ieee80211com *ic)
+{
+	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
+		ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n);
+	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
+		ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n);
+}
+
+const struct ieee80211_htrateset *
+ieee80211_get_suphtrates(struct ieee80211com *ic,
+	const struct ieee80211_channel *c)
+{
+	return &ieee80211_rateset_11n;
+}
+
+/*
+ * Receive processing.
+ */
+
+/*
+ * Decap the encapsulated A-MSDU frames and dispatch all but
+ * the last for delivery.  The last frame is returned for 
+ * delivery via the normal path.
+ */
+struct mbuf *
+ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	int framelen;
+	struct mbuf *n;
+
+	/* discard 802.3 header inserted by ieee80211_decap */
+	m_adj(m, sizeof(struct ether_header));
+
+	ic->ic_stats.is_amsdu_decap++;
+
+	for (;;) {
+		/*
+		 * Decap the first frame, bust it apart from the
+		 * remainder and deliver.  We leave the last frame
+		 * delivery to the caller (for consistency with other
+		 * code paths, could also do it here).
+		 */
+		m = ieee80211_decap1(m, &framelen);
+		if (m == NULL) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+			    ni->ni_macaddr, "a-msdu", "%s", "decap failed");
+			ic->ic_stats.is_amsdu_tooshort++;
+			return NULL;
+		}
+		if (m->m_pkthdr.len == framelen)
+			break;
+		n = m_split(m, framelen, M_NOWAIT);
+		if (n == NULL) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+			    ni->ni_macaddr, "a-msdu",
+			    "%s", "unable to split encapsulated frames");
+			ic->ic_stats.is_amsdu_split++;
+			m_freem(m);			/* NB: must reclaim */
+			return NULL;
+		}
+		ieee80211_deliver_data(ic, ni, m);
+
+		/*
+		 * Remove frame contents; each intermediate frame
+		 * is required to be aligned to a 4-byte boundary.
+		 */
+		m = n;
+		m_adj(m, roundup2(framelen, 4) - framelen);	/* padding */
+	}
+	return m;				/* last delivered by caller */
+}
+
+/*
+ * Start A-MPDU rx/re-order processing for the specified TID.
+ */
+static void
+ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
+{
+	memset(rap, 0, sizeof(*rap));
+	rap->rxa_wnd = (bufsiz == 0) ?
+	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+	rap->rxa_start = start;
+	rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
+}
+
+/*
+ * Purge all frames in the A-MPDU re-order queue.
+ */
+static void
+ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
+{
+	struct mbuf *m;
+	int i;
+
+	for (i = 0; i < rap->rxa_wnd; i++) {
+		m = rap->rxa_m[i];
+		if (m != NULL) {
+			rap->rxa_m[i] = NULL;
+			rap->rxa_qbytes -= m->m_pkthdr.len;
+			m_freem(m);
+			if (--rap->rxa_qframes == 0)
+				break;
+		}
+	}
+	KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0,
+	    ("lost %u data, %u frames on ampdu rx q",
+	    rap->rxa_qbytes, rap->rxa_qframes));
+}
+
+/*
+ * Stop A-MPDU rx processing for the specified TID.
+ */
+static void
+ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
+{
+	rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+	ampdu_rx_purge(rap);
+}
+
+/*
+ * Dispatch a frame from the A-MPDU reorder queue.  The
+ * frame is fed back into ieee80211_input marked with an
+ * M_AMPDU flag so it doesn't come back to us (it also
+ * permits ieee80211_input to optimize re-processing).
+ */
+static __inline void
+ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
+{
+	m->m_flags |= M_AMPDU;	/* bypass normal processing */
+	/* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */
+	(void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0);
+}
+
+/*
+ * Dispatch as many frames as possible from the re-order queue.
+ * Frames will always be "at the front"; we process all frames
+ * up to the first empty slot in the window.  On completion we
+ * cleanup state if there are still pending frames in the current
+ * BA window.  We assume the frame at slot 0 is already handled
+ * by the caller; we always start at slot 1.
+ */
+static void
+ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+	int i;
+
+	/* flush run of frames */
+	for (i = 1; i < rap->rxa_wnd; i++) {
+		m = rap->rxa_m[i];
+		if (m == NULL)
+			break;
+		rap->rxa_m[i] = NULL;
+		rap->rxa_qbytes -= m->m_pkthdr.len;
+		rap->rxa_qframes--;
+
+		ampdu_dispatch(ni, m);
+	}
+	/*
+	 * If frames remain, copy the mbuf pointers down so
+	 * they correspond to the offsets in the new window.
+	 */
+	if (rap->rxa_qframes != 0) {
+		int n = rap->rxa_qframes, j;
+		for (j = i+1; j < rap->rxa_wnd; j++) {
+			if (rap->rxa_m[j] != NULL) {
+				rap->rxa_m[j-i] = rap->rxa_m[j];
+				rap->rxa_m[j] = NULL;
+				if (--n == 0)
+					break;
+			}
+		}
+		KASSERT(n == 0, ("lost %d frames", n));
+		ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+	}
+	/*
+	 * Adjust the start of the BA window to
+	 * reflect the frames just dispatched.
+	 */
+	rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
+	ic->ic_stats.is_ampdu_rx_oor += i;
+}
+
+#ifdef IEEE80211_AMPDU_AGE
+/*
+ * Dispatch all frames in the A-MPDU re-order queue.
+ */
+static void
+ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+	int i;
+
+	for (i = 0; i < rap->rxa_wnd; i++) {
+		m = rap->rxa_m[i];
+		if (m == NULL)
+			continue;
+		rap->rxa_m[i] = NULL;
+		rap->rxa_qbytes -= m->m_pkthdr.len;
+		rap->rxa_qframes--;
+		ic->ic_stats.is_ampdu_rx_oor++;
+
+		ampdu_dispatch(ni, m);
+		if (rap->rxa_qframes == 0)
+			break;
+	}
+}
+#endif /* IEEE80211_AMPDU_AGE */
+
+/*
+ * Dispatch all frames in the A-MPDU re-order queue
+ * preceding the specified sequence number.  This logic
+ * handles window moves due to a received MSDU or BAR.
+ */
+static void
+ampdu_rx_flush_upto(struct ieee80211_node *ni,
+	struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+	ieee80211_seq seqno;
+	int i;
+
+	/*
+	 * Flush any complete MSDU's with a sequence number lower
+	 * than winstart.  Gaps may exist.  Note that we may actually
+	 * dispatch frames past winstart if a run continues; this is
+	 * an optimization that avoids having to do a separate pass
+	 * to dispatch frames after moving the BA window start.
+	 */
+	seqno = rap->rxa_start;
+	for (i = 0; i < rap->rxa_wnd; i++) {
+		m = rap->rxa_m[i];
+		if (m != NULL) {
+			rap->rxa_m[i] = NULL;
+			rap->rxa_qbytes -= m->m_pkthdr.len;
+			rap->rxa_qframes--;
+			ic->ic_stats.is_ampdu_rx_oor++;
+
+			ampdu_dispatch(ni, m);
+		} else {
+			if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart))
+				break;
+		}
+		seqno = IEEE80211_SEQ_INC(seqno);
+	}
+	/*
+	 * If frames remain, copy the mbuf pointers down so
+	 * they correspond to the offsets in the new window.
+	 */
+	if (rap->rxa_qframes != 0) {
+		int n = rap->rxa_qframes, j;
+		for (j = i+1; j < rap->rxa_wnd; j++) {
+			if (rap->rxa_m[j] != NULL) {
+				rap->rxa_m[j-i] = rap->rxa_m[j];
+				rap->rxa_m[j] = NULL;
+				if (--n == 0)
+					break;
+			}
+		}
+		KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d "
+		    "BA win <%d:%d> winstart %d",
+		    __func__, n, rap->rxa_qframes, i, rap->rxa_start,
+		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+		    winstart));
+		ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+	}
+	/*
+	 * Move the start of the BA window; we use the
+	 * sequence number of the last MSDU that was
+	 * passed up the stack+1 or winstart if stopped on
+	 * a gap in the reorder buffer.
+	 */
+	rap->rxa_start = seqno;
+}
+
+/*
+ * Process a received QoS data frame for an HT station.  Handle
+ * A-MPDU reordering: if this frame is received out of order
+ * and falls within the BA window hold onto it.  Otherwise if
+ * this frame completes a run, flush any pending frames.  We
+ * return 1 if the frame is consumed.  A 0 is returned if
+ * the frame should be processed normally by the caller.
+ */
+int
+ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
+{
+#define	IEEE80211_FC0_QOSDATA \
+	(IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
+#define	PROCESS		0	/* caller should process frame */
+#define	CONSUMED	1	/* frame consumed, caller does nothing */
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_qosframe *wh;
+	struct ieee80211_rx_ampdu *rap;
+	ieee80211_seq rxseq;
+	uint8_t tid;
+	int off;
+
+	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
+
+	/* NB: m_len known to be sufficient */
+	wh = mtod(m, struct ieee80211_qosframe *);
+	KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data"));
+
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+		tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
+	else
+		tid = wh->i_qos[0];
+	tid &= IEEE80211_QOS_TID;
+	rap = &ni->ni_rx_ampdu[tid];
+	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+		/*
+		 * No ADDBA request yet, don't touch.
+		 */
+		return PROCESS;
+	}
+	rxseq = le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+	rap->rxa_nframes++;
+again:
+	if (rxseq == rap->rxa_start) {
+		/*
+		 * First frame in window.
+		 */
+		if (rap->rxa_qframes != 0) {
+			/*
+			 * Dispatch as many packets as we can.
+			 */
+			KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup"));
+			ampdu_dispatch(ni, m);
+			ampdu_rx_dispatch(rap, ni);
+			return CONSUMED;
+		} else {
+			/*
+			 * In order; advance window and notify
+			 * caller to dispatch directly.
+			 */
+			rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
+			return PROCESS;
+		}
+	}
+	/*
+	 * Frame is out of order; store if in the BA window.
+	 */
+	/* calculate offset in BA window */
+	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
+	if (off < rap->rxa_wnd) {
+		/*
+		 * Common case (hopefully): in the BA window.
+		 * Sec 9.10.7.6 a) (D2.04 p.118 line 47)
+		 */
+#ifdef IEEE80211_AMPDU_AGE
+		/* 
+		 * Check for frames sitting too long in the reorder queue.
+		 * This should only ever happen if frames are not delivered
+		 * without the sender otherwise notifying us (e.g. with a
+		 * BAR to move the window).  Typically this happens because
+		 * of vendor bugs that cause the sequence number to jump.
+		 * When this happens we get a gap in the reorder queue that
+		 * leaves frame sitting on the queue until they get pushed
+		 * out due to window moves.  When the vendor does not send
+		 * BAR this move only happens due to explicit packet sends
+		 *
+		 * NB: we only track the time of the oldest frame in the
+		 * reorder q; this means that if we flush we might push
+		 * frames that still "new"; if this happens then subsequent
+		 * frames will result in BA window moves which cost something
+		 * but is still better than a big throughput dip.
+		 */
+		if (rap->rxa_qframes != 0) {
+			/* XXX honor batimeout? */
+			if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
+				/*
+				 * Too long since we received the first
+				 * frame; flush the reorder buffer.
+				 */
+				if (rap->rxa_qframes != 0) {
+					ic->ic_stats.is_ampdu_rx_age +=
+					    rap->rxa_qframes;
+					ampdu_rx_flush(ni, rap);
+				}
+				rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
+				return PROCESS;
+			}
+		} else {
+			/*
+			 * First frame, start aging timer.
+			 */
+			rap->rxa_age = ticks;
+		}
+#endif /* IEEE80211_AMPDU_AGE */
+		/* save packet */
+		if (rap->rxa_m[off] == NULL) {
+			rap->rxa_m[off] = m;
+			rap->rxa_qframes++;
+			rap->rxa_qbytes += m->m_pkthdr.len;
+			ic->ic_stats.is_ampdu_rx_reorder++;
+		} else {
+			IEEE80211_DISCARD_MAC(ic,
+			    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
+			    ni->ni_macaddr, "a-mpdu duplicate",
+			    "seqno %u tid %u BA win <%u:%u>",
+			    rxseq, tid, rap->rxa_start,
+			    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1));
+			ic->ic_stats.is_rx_dup++;
+			IEEE80211_NODE_STAT(ni, rx_dup);
+			m_freem(m);
+		}
+		return CONSUMED;
+	}
+	if (off < IEEE80211_SEQ_BA_RANGE) {
+		/*
+		 * Outside the BA window, but within range;
+		 * flush the reorder q and move the window.
+		 * Sec 9.10.7.6 b) (D2.04 p.118 line 60)
+		 */
+		IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+		    "move BA win <%u:%u> (%u frames) rxseq %u tid %u",
+		    rap->rxa_start,
+		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+		    rap->rxa_qframes, rxseq, tid);
+		ic->ic_stats.is_ampdu_rx_move++;
+
+		/*
+		 * The spec says to flush frames up to but not including:
+		 * 	WinStart_B = rxseq - rap->rxa_wnd + 1
+		 * Then insert the frame or notify the caller to process
+		 * it immediately.  We can safely do this by just starting
+		 * over again because we know the frame will now be within
+		 * the BA window.
+		 */
+		/* NB: rxa_wnd known to be >0 */
+		ampdu_rx_flush_upto(ni, rap,
+		    IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1));
+		goto again;
+	} else {
+		/*
+		 * Outside the BA window and out of range; toss.
+		 * Sec 9.10.7.6 c) (D2.04 p.119 line 16)
+		 */
+		IEEE80211_DISCARD_MAC(ic,
+		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
+		    "MSDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
+		    rap->rxa_start,
+		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+		    rap->rxa_qframes, rxseq, tid,
+		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
+		ic->ic_stats.is_ampdu_rx_drop++;
+		IEEE80211_NODE_STAT(ni, rx_drop);
+		m_freem(m);
+		return CONSUMED;
+	}
+#undef CONSUMED
+#undef PROCESS
+#undef IEEE80211_FC0_QOSDATA
+}
+
+/*
+ * Process a BAR ctl frame.  Dispatch all frames up to
+ * the sequence number of the frame.  If this frame is
+ * out of range it's discarded.
+ */
+void
+ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_frame_bar *wh;
+	struct ieee80211_rx_ampdu *rap;
+	ieee80211_seq rxseq;
+	int tid, off;
+
+	if (!ieee80211_recv_bar_ena) {
+#if 0
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_11N,
+		    ni->ni_macaddr, "BAR", "%s", "processing disabled");
+#endif
+		ic->ic_stats.is_ampdu_bar_bad++;
+		return;
+	}
+	wh = mtod(m0, struct ieee80211_frame_bar *);
+	/* XXX check basic BAR */
+	tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID);
+	rap = &ni->ni_rx_ampdu[tid];
+	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+		/*
+		 * No ADDBA request yet, don't touch.
+		 */
+		IEEE80211_DISCARD_MAC(ic,
+		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
+		    ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
+		ic->ic_stats.is_ampdu_bar_bad++;
+		return;
+	}
+	ic->ic_stats.is_ampdu_bar_rx++;
+	rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+	if (rxseq == rap->rxa_start)
+		return;
+	/* calculate offset in BA window */
+	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
+	if (off < IEEE80211_SEQ_BA_RANGE) {
+		/*
+		 * Flush the reorder q up to rxseq and move the window.
+		 * Sec 9.10.7.6 a) (D2.04 p.119 line 22)
+		 */
+		IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+		    "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
+		    rap->rxa_start,
+		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+		    rap->rxa_qframes, rxseq, tid);
+		ic->ic_stats.is_ampdu_bar_move++;
+
+		ampdu_rx_flush_upto(ni, rap, rxseq);
+		if (off >= rap->rxa_wnd) {
+			/*
+			 * BAR specifies a window start to the right of BA
+			 * window; we must move it explicitly since
+			 * ampdu_rx_flush_upto will not.
+			 */
+			rap->rxa_start = rxseq;
+		}
+	} else {
+		/*
+		 * Out of range; toss.
+		 * Sec 9.10.7.6 b) (D2.04 p.119 line 41)
+		 */
+		IEEE80211_DISCARD_MAC(ic,
+		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
+		    "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
+		    rap->rxa_start,
+		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+		    rap->rxa_qframes, rxseq, tid,
+		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
+		ic->ic_stats.is_ampdu_bar_oow++;
+		IEEE80211_NODE_STAT(ni, rx_drop);
+	}
+}
+
+/*
+ * Setup HT-specific state in a node.  Called only
+ * when HT use is negotiated so we don't do extra
+ * work for temporary and/or legacy sta's.
+ */
+void
+ieee80211_ht_node_init(struct ieee80211_node *ni, const uint8_t *htcap)
+{
+	struct ieee80211_tx_ampdu *tap;
+	int ac;
+
+	if (ni->ni_flags & IEEE80211_NODE_HT) {
+		/*
+		 * Clean AMPDU state on re-associate.  This handles the case
+		 * where a station leaves w/o notifying us and then returns
+		 * before node is reaped for inactivity.
+		 */
+		ieee80211_ht_node_cleanup(ni);
+	}
+	ieee80211_parse_htcap(ni, htcap);
+	for (ac = 0; ac < WME_NUM_AC; ac++) {
+		tap = &ni->ni_tx_ampdu[ac];
+		tap->txa_ac = ac;
+		/* NB: further initialization deferred */
+	}
+	ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
+}
+
+/*
+ * Cleanup HT-specific state in a node.  Called only
+ * when HT use has been marked.
+ */
+void
+ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	int i;
+
+	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
+
+	/* XXX optimize this */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
+		if (tap->txa_flags & IEEE80211_AGGR_SETUP) {
+			/*
+			 * Stop BA stream if setup so driver has a chance
+			 * to reclaim any resources it might have allocated.
+			 */
+			ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]);
+			IEEE80211_TAPQ_DESTROY(tap);
+			/* NB: clearing NAK means we may re-send ADDBA */ 
+			tap->txa_flags &=
+			    ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
+		}
+	}
+	for (i = 0; i < WME_NUM_TID; i++)
+		ampdu_rx_stop(&ni->ni_rx_ampdu[i]);
+
+	ni->ni_htcap = 0;
+	ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT |
+		IEEE80211_NODE_AMPDU);
+}
+
+static struct ieee80211_channel *
+findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
+{
+	return ieee80211_find_channel(ic, c->ic_freq,
+	    (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags);
+}
+
+/*
+ * Adjust a channel to be HT/non-HT according to the vap's configuration.
+ */
+struct ieee80211_channel *
+ieee80211_ht_adjust_channel(struct ieee80211com *ic,
+	struct ieee80211_channel *chan, int flags)
+{
+	struct ieee80211_channel *c;
+
+	if (flags & IEEE80211_FEXT_HT) {
+		/* promote to HT if possible */
+		if (flags & IEEE80211_FEXT_USEHT40) {
+			if (!IEEE80211_IS_CHAN_HT40(chan)) {
+				/* NB: arbitrarily pick ht40+ over ht40- */
+				c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U);
+				if (c == NULL)
+					c = findhtchan(ic, chan,
+						IEEE80211_CHAN_HT40D);
+				if (c == NULL)
+					c = findhtchan(ic, chan,
+						IEEE80211_CHAN_HT20);
+				if (c != NULL)
+					chan = c;
+			}
+		} else if (!IEEE80211_IS_CHAN_HT20(chan)) {
+			c = findhtchan(ic, chan, IEEE80211_CHAN_HT20);
+			if (c != NULL)
+				chan = c;
+		}
+	} else if (IEEE80211_IS_CHAN_HT(chan)) {
+		/* demote to legacy, HT use is disabled */
+		c = ieee80211_find_channel(ic, chan->ic_freq,
+		    chan->ic_flags &~ IEEE80211_CHAN_HT);
+		if (c != NULL)
+			chan = c;
+	}
+	return chan;
+}
+
+/*
+ * Setup HT-specific state for a legacy WDS peer.
+ */
+void
+ieee80211_ht_wds_init(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_tx_ampdu *tap;
+	int ac;
+
+	KASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
+
+	/* XXX check scan cache in case peer has an ap and we have info */
+	/*
+	 * If setup with a legacy channel; locate an HT channel.
+	 * Otherwise if the inherited channel (from a companion
+	 * AP) is suitable use it so we use the same location
+	 * for the extension channel).
+	 */
+	ni->ni_chan = ieee80211_ht_adjust_channel(ic, ni->ni_chan,
+	    ic->ic_flags_ext);
+
+	ni->ni_htcap = 0;
+	if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+		ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20;
+	if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+		ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40;
+		ni->ni_chw = 40;
+		if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
+			ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
+		else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
+			ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+			ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40;
+	} else {
+		ni->ni_chw = 20;
+		ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE;
+	}
+	ni->ni_htctlchan = ni->ni_chan->ic_ieee;
+
+	ni->ni_htopmode = 0;		/* XXX need protection state */
+	ni->ni_htstbc = 0;		/* XXX need info */
+
+	for (ac = 0; ac < WME_NUM_AC; ac++) {
+		tap = &ni->ni_tx_ampdu[ac];
+		tap->txa_ac = ac;
+	}
+	/* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */
+	ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
+}
+
+/*
+ * Notify hostap vaps of a change in the HTINFO ie.
+ */
+static void
+htinfo_notify(struct ieee80211com *ic)
+{
+	if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+		return;
+	IEEE80211_NOTE(ic,
+	    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+	    ic->ic_bss,
+	    "HT bss occupancy change: %d sta, %d ht, "
+	    "%d ht40%s, HT protmode now 0x%x"
+	    , ic->ic_sta_assoc
+	    , ic->ic_ht_sta_assoc
+	    , ic->ic_ht40_sta_assoc
+	    , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
+		 ", non-HT sta present" : ""
+	    , ic->ic_curhtprotmode);
+	ieee80211_beacon_notify(ic, IEEE80211_BEACON_HTINFO);
+}
+
+/*
+ * Calculate HT protection mode from current
+ * state and handle updates.
+ */
+static void
+htinfo_update(struct ieee80211com *ic)
+{
+	uint8_t protmode;
+
+	if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
+		protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
+		         | IEEE80211_HTINFO_NONHT_PRESENT;
+	} else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
+		protmode = IEEE80211_HTINFO_OPMODE_MIXED
+		         | IEEE80211_HTINFO_NONHT_PRESENT;
+	} else if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && 
+	    ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
+		protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
+	} else {
+		protmode = IEEE80211_HTINFO_OPMODE_PURE;
+	}
+	if (protmode != ic->ic_curhtprotmode) {
+		ic->ic_curhtprotmode = protmode;
+		htinfo_notify(ic);
+	}
+}
+
+/*
+ * Handle an HT station joining a BSS.
+ */
+void
+ieee80211_ht_node_join(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	IEEE80211_LOCK_ASSERT(ic);
+
+	if (ni->ni_flags & IEEE80211_NODE_HT) {
+		ic->ic_ht_sta_assoc++;
+		if (ni->ni_chw == 40)
+			ic->ic_ht40_sta_assoc++;
+	}
+	htinfo_update(ic);
+}
+
+/*
+ * Handle an HT station leaving a BSS.
+ */
+void
+ieee80211_ht_node_leave(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	IEEE80211_LOCK_ASSERT(ic);
+
+	if (ni->ni_flags & IEEE80211_NODE_HT) {
+		ic->ic_ht_sta_assoc--;
+		if (ni->ni_chw == 40)
+			ic->ic_ht40_sta_assoc--;
+	}
+	htinfo_update(ic);
+}
+
+/*
+ * Public version of htinfo_update; used for processing
+ * beacon frames from overlapping bss in hostap_recv_mgmt.
+ */
+void
+ieee80211_htinfo_update(struct ieee80211com *ic, int protmode)
+{
+	if (protmode != ic->ic_curhtprotmode) {
+		ic->ic_curhtprotmode = protmode;
+		htinfo_notify(ic);
+	}
+}
+
+/*
+ * Time out presence of an overlapping bss with non-HT
+ * stations.  When operating in hostap mode we listen for
+ * beacons from other stations and if we identify a non-HT
+ * station is present we update the opmode field of the
+ * HTINFO ie.  To identify when all non-HT stations are
+ * gone we time out this condition.
+ */
+void
+ieee80211_ht_timeout(struct ieee80211com *ic)
+{
+	IEEE80211_LOCK_ASSERT(ic);
+
+	if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) &&
+	    time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
+#if 0
+		IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+		    "%s", "time out non-HT STA present on channel");
+#endif
+		ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
+		htinfo_update(ic);
+	}
+}
+
+/* unalligned little endian access */     
+#define LE_READ_2(p)					\
+	((uint16_t)					\
+	 ((((const uint8_t *)(p))[0]      ) |		\
+	  (((const uint8_t *)(p))[1] <<  8)))
+
+/*
+ * Process an 802.11n HT capabilities ie.
+ */
+void
+ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	if (ie[0] == IEEE80211_ELEMID_VENDOR) {
+		/*
+		 * Station used Vendor OUI ie to associate;
+		 * mark the node so when we respond we'll use
+		 * the Vendor OUI's and not the standard ie's.
+		 */
+		ni->ni_flags |= IEEE80211_NODE_HTCOMPAT;
+		ie += 4;
+	} else
+		ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT;
+
+	ni->ni_htcap = LE_READ_2(ie +
+		__offsetof(struct ieee80211_ie_htcap, hc_cap));
+	ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
+	/* XXX needed or will ieee80211_parse_htinfo always be called? */
+	ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
+		     (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
+}
+
+/*
+ * Process an 802.11n HT info ie and update the node state.
+ * Note that we handle use this information to identify the
+ * correct channel (HT20, HT40+, HT40-, legacy).  The caller
+ * is responsible for insuring any required channel change is
+ * done (e.g. in sta mode when parsing the contents of a
+ * beacon frame).
+ */
+void
+ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+ 	const struct ieee80211_ie_htinfo *htinfo;
+	struct ieee80211_channel *c;
+	uint16_t w;
+	int htflags, chanflags;
+
+	if (ie[0] == IEEE80211_ELEMID_VENDOR)
+		ie += 4;
+ 	htinfo = (const struct ieee80211_ie_htinfo *) ie;
+	ni->ni_htctlchan = htinfo->hi_ctrlchannel;
+	ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
+	w = LE_READ_2(&htinfo->hi_byte2);
+	ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
+	w = LE_READ_2(&htinfo->hi_byte45);
+	ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS);
+	/*
+	 * Handle 11n channel switch.  Use the received HT ie's to
+	 * identify the right channel to use.  If we cannot locate it
+	 * in the channel table then fallback to legacy operation.
+	 */
+	htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ?
+	    IEEE80211_CHAN_HT20 : 0;
+	/* NB: honor operating mode constraint */
+	if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
+	    (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) {
+		if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
+			htflags = IEEE80211_CHAN_HT40U;
+		else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
+			htflags = IEEE80211_CHAN_HT40D;
+	}
+	chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
+	if (chanflags != ni->ni_chan->ic_flags) {
+		c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
+		if (c == NULL && htflags != IEEE80211_CHAN_HT20) {
+			/*
+			 * No HT40 channel entry in our table; fall back
+			 * to HT20 operation.  This should not happen.
+			 */
+			c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
+			IEEE80211_NOTE(ni->ni_ic,
+			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+			    "no HT40 channel (freq %u), falling back to HT20",
+			    ni->ni_chan->ic_freq);
+			/* XXX stat */
+		}
+		if (c != NULL && c != ni->ni_chan) {
+			IEEE80211_NOTE(ni->ni_ic,
+			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+			    "switch station to HT%d channel %u/0x%x",
+			    IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
+			    c->ic_freq, c->ic_flags);
+			ni->ni_chan = c;
+		}
+		/* NB: caller responsible for forcing any channel change */
+	}
+	/* update node's tx channel width */
+	ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20;
+}
+
+/*
+ * Install received HT rate set by parsing the HT cap ie.
+ */
+int
+ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	const struct ieee80211_ie_htcap *htcap;
+	struct ieee80211_htrateset *rs;
+	int i;
+
+	rs = &ni->ni_htrates;
+	memset(rs, 0, sizeof(*rs));
+	if (ie != NULL) {
+		if (ie[0] == IEEE80211_ELEMID_VENDOR)
+			ie += 4;
+		htcap = (const struct ieee80211_ie_htcap *) ie;
+		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
+			if (isclr(htcap->hc_mcsset, i))
+				continue;
+			if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
+				IEEE80211_NOTE(ic,
+				    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
+				    "WARNING, HT rate set too large; only "
+				    "using %u rates", IEEE80211_HTRATE_MAXSIZE);
+				ic->ic_stats.is_rx_rstoobig++;
+				break;
+			}
+			rs->rs_rates[rs->rs_nrates++] = i;
+		}
+	}
+	return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags);
+}
+
+/*
+ * Mark rates in a node's HT rate set as basic according
+ * to the information in the supplied HT info ie.
+ */
+void
+ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
+{
+	const struct ieee80211_ie_htinfo *htinfo;
+	struct ieee80211_htrateset *rs;
+	int i, j;
+
+	if (ie[0] == IEEE80211_ELEMID_VENDOR)
+		ie += 4;
+	htinfo = (const struct ieee80211_ie_htinfo *) ie;
+	rs = &ni->ni_htrates;
+	if (rs->rs_nrates == 0) {
+		IEEE80211_NOTE(ni->ni_ic,
+		    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
+		    "%s", "WARNING, empty HT rate set");
+		return;
+	}
+	for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
+		if (isclr(htinfo->hi_basicmcsset, i))
+			continue;
+		for (j = 0; j < rs->rs_nrates; j++)
+			if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i)
+				rs->rs_rates[j] |= IEEE80211_RATE_BASIC;
+	}
+}
+
+static void
+addba_timeout(void *arg)
+{
+	struct ieee80211_tx_ampdu *tap = arg;
+
+	/* XXX ? */
+	tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+	tap->txa_attempts++;
+}
+
+static void
+addba_start_timeout(struct ieee80211_tx_ampdu *tap)
+{
+	/* XXX use CALLOUT_PENDING instead? */
+	callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT,
+	    addba_timeout, tap);
+	tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
+	tap->txa_lastrequest = ticks;
+}
+
+static void
+addba_stop_timeout(struct ieee80211_tx_ampdu *tap)
+{
+	/* XXX use CALLOUT_PENDING instead? */
+	if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
+		callout_stop(&tap->txa_timer);
+		tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+	}
+}
+
+/*
+ * Default method for requesting A-MPDU tx aggregation.
+ * We setup the specified state block and start a timer
+ * to wait for an ADDBA response frame.
+ */
+static int
+ieee80211_addba_request(struct ieee80211_node *ni,
+	struct ieee80211_tx_ampdu *tap,
+	int dialogtoken, int baparamset, int batimeout)
+{
+	int bufsiz;
+
+	/* XXX locking */
+	tap->txa_token = dialogtoken;
+	tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE;
+	tap->txa_start = tap->txa_seqstart = 0;
+	bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+	tap->txa_wnd = (bufsiz == 0) ?
+	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+	addba_start_timeout(tap);
+	return 1;
+}
+
+/*
+ * Default method for processing an A-MPDU tx aggregation
+ * response.  We shutdown any pending timer and update the
+ * state block according to the reply.
+ */
+static int
+ieee80211_addba_response(struct ieee80211_node *ni,
+	struct ieee80211_tx_ampdu *tap,
+	int status, int baparamset, int batimeout)
+{
+	int bufsiz;
+
+	/* XXX locking */
+	addba_stop_timeout(tap);
+	if (status == IEEE80211_STATUS_SUCCESS) {
+		bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+		/* XXX override our request? */
+		tap->txa_wnd = (bufsiz == 0) ?
+		    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+		tap->txa_flags |= IEEE80211_AGGR_RUNNING;
+	} else {
+		/* mark tid so we don't try again */
+		tap->txa_flags |= IEEE80211_AGGR_NAK;
+	}
+	return 1;
+}
+
+/*
+ * Default method for stopping A-MPDU tx aggregation.
+ * Any timer is cleared and we drain any pending frames.
+ */
+static void
+ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+	/* XXX locking */
+	addba_stop_timeout(tap);
+	if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
+		/* clear aggregation queue */
+		ieee80211_drain_ifq(&tap->txa_q);
+		tap->txa_flags &= ~IEEE80211_AGGR_RUNNING;
+	}
+	tap->txa_attempts = 0;
+}
+
+/*
+ * Process a received action frame using the default aggregation
+ * policy.  We intercept ADDBA-related frames and use them to
+ * update our aggregation state.  All other frames are passed up
+ * for processing by ieee80211_recv_action.
+ */
+static void
+ieee80211_aggr_recv_action(struct ieee80211_node *ni,
+	const uint8_t *frm, const uint8_t *efrm)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	const struct ieee80211_action *ia;
+	struct ieee80211_rx_ampdu *rap;
+	struct ieee80211_tx_ampdu *tap;
+	uint8_t dialogtoken;
+	uint16_t baparamset, batimeout, baseqctl, code;
+	uint16_t args[4];
+	int tid, ac, bufsiz;
+
+	ia = (const struct ieee80211_action *) frm;
+	switch (ia->ia_category) {
+	case IEEE80211_ACTION_CAT_BA:
+		switch (ia->ia_action) {
+		case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+			dialogtoken = frm[2];
+			baparamset = LE_READ_2(frm+3);
+			batimeout = LE_READ_2(frm+5);
+			baseqctl = LE_READ_2(frm+7);
+
+			tid = MS(baparamset, IEEE80211_BAPS_TID);
+			bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+			    "recv ADDBA request: dialogtoken %u "
+			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
+			    "baseqctl %d:%d",
+			    dialogtoken, baparamset, tid, bufsiz, batimeout,
+			    MS(baseqctl, IEEE80211_BASEQ_START),
+			    MS(baseqctl, IEEE80211_BASEQ_FRAG));
+
+			rap = &ni->ni_rx_ampdu[tid];
+
+			/* Send ADDBA response */
+			args[0] = dialogtoken;
+			/*
+			 * NB: We ack only if the sta associated with HT and
+			 * the ap is configured to do AMPDU rx (the latter
+			 * violates the 11n spec and is mostly for testing).
+			 */
+			if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
+			    (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
+				ampdu_rx_start(rap, bufsiz,
+				    MS(baseqctl, IEEE80211_BASEQ_START));
+
+				args[1] = IEEE80211_STATUS_SUCCESS;
+			} else {
+				IEEE80211_NOTE(ic,
+				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+				    ni, "reject ADDBA request: %s",
+				    ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
+				       "administratively disabled" :
+				       "not negotiated for station");
+				ic->ic_stats.is_addba_reject++;
+				args[1] = IEEE80211_STATUS_UNSPECIFIED;
+			}
+			/* XXX honor rap flags? */
+			args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
+				| SM(tid, IEEE80211_BAPS_TID)
+				| SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
+				;
+			args[3] = 0;
+			ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
+				IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
+			return;
+
+		case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+			dialogtoken = frm[2];
+			code = LE_READ_2(frm+3);
+			baparamset = LE_READ_2(frm+5);
+			tid = MS(baparamset, IEEE80211_BAPS_TID);
+			bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+			batimeout = LE_READ_2(frm+7);
+
+			ac = TID_TO_WME_AC(tid);
+			tap = &ni->ni_tx_ampdu[ac];
+			if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+				IEEE80211_DISCARD_MAC(ic,
+				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+				    ni->ni_macaddr, "ADDBA response",
+				    "no pending ADDBA, tid %d dialogtoken %u "
+				    "code %d", tid, dialogtoken, code);
+				ic->ic_stats.is_addba_norequest++;
+				return;
+			}
+			if (dialogtoken != tap->txa_token) {
+				IEEE80211_DISCARD_MAC(ic,
+				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+				    ni->ni_macaddr, "ADDBA response",
+				    "dialogtoken mismatch: waiting for %d, "
+				    "received %d, tid %d code %d",
+				    tap->txa_token, dialogtoken, tid, code);
+				ic->ic_stats.is_addba_badtoken++;
+				return;
+			}
+
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+			    "recv ADDBA response: dialogtoken %u code %d "
+			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
+			    dialogtoken, code, baparamset, tid, bufsiz,
+			    batimeout);
+			ic->ic_addba_response(ni, tap,
+				code, baparamset, batimeout);
+			return;
+
+		case IEEE80211_ACTION_BA_DELBA:
+			baparamset = LE_READ_2(frm+2);
+			code = LE_READ_2(frm+4);
+
+			tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+			    "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
+			    "code %d", baparamset, tid,
+			    MS(baparamset, IEEE80211_DELBAPS_INIT), code);
+
+			if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
+				ac = TID_TO_WME_AC(tid);
+				tap = &ni->ni_tx_ampdu[ac];
+				ic->ic_addba_stop(ni, tap);
+			} else {
+				rap = &ni->ni_rx_ampdu[tid];
+				ampdu_rx_stop(rap);
+			}
+			return;
+		}
+		break;
+	}
+	ieee80211_recv_action(ni, frm, efrm);
+}
+
+/*
+ * Process a received 802.11n action frame.
+ * Aggregation-related frames are assumed to be handled
+ * already; we handle any other frames we can, otherwise
+ * complain about being unsupported (with debugging).
+ */
+void
+ieee80211_recv_action(struct ieee80211_node *ni,
+	const uint8_t *frm, const uint8_t *efrm)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	const struct ieee80211_action *ia;
+	int chw;
+
+	ia = (const struct ieee80211_action *) frm;
+	switch (ia->ia_category) {
+	case IEEE80211_ACTION_CAT_BA:
+		IEEE80211_NOTE(ic,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+		    "%s: BA action %d not implemented", __func__,
+		    ia->ia_action);
+		ic->ic_stats.is_rx_mgtdiscard++;
+		break;
+	case IEEE80211_ACTION_CAT_HT:
+		switch (ia->ia_action) {
+		case IEEE80211_ACTION_HT_TXCHWIDTH:
+			chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20;
+			if (chw != ni->ni_chw) {
+				ni->ni_chw = chw;
+				ni->ni_flags |= IEEE80211_NODE_CHWUPDATE;
+			}
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+		            "%s: HT txchwidth, width %d (%s)",
+			    __func__, chw,
+			    ni->ni_flags & IEEE80211_NODE_CHWUPDATE ?
+				"new" : "no change");
+			break;
+		case IEEE80211_ACTION_HT_MIMOPWRSAVE:
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+		            "%s: HT MIMO PS", __func__);
+			break;
+		default:
+			IEEE80211_NOTE(ic,
+			   IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+		           "%s: HT action %d not implemented", __func__,
+			   ia->ia_action);
+			ic->ic_stats.is_rx_mgtdiscard++;
+			break;
+		}
+		break;
+	default:
+		IEEE80211_NOTE(ic,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+		    "%s: category %d not implemented", __func__,
+		    ia->ia_category);
+		ic->ic_stats.is_rx_mgtdiscard++;
+		break;
+	}
+}
+
+/*
+ * Transmit processing.
+ */
+
+/*
+ * Request A-MPDU tx aggregation.  Setup local state and
+ * issue an ADDBA request.  BA use will only happen after
+ * the other end replies with ADDBA response.
+ */
+int
+ieee80211_ampdu_request(struct ieee80211_node *ni,
+	struct ieee80211_tx_ampdu *tap)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t args[4];
+	int tid, dialogtoken;
+	static int tokens = 0;	/* XXX */
+
+	/* XXX locking */
+	if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
+		/* do deferred setup of state */
+		IEEE80211_TAPQ_INIT(tap);
+		callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
+		tap->txa_flags |= IEEE80211_AGGR_SETUP;
+	}
+	if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
+	    (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
+		/*
+		 * Don't retry too often; IEEE80211_AGGR_MINRETRY
+		 * defines the minimum interval we'll retry after
+		 * IEEE80211_AGGR_MAXTRIES failed attempts to
+		 * negotiate use.
+		 */
+		return 0;
+	}
+	/* XXX hack for not doing proper locking */
+	tap->txa_flags &= ~IEEE80211_AGGR_NAK;
+
+	dialogtoken = (tokens+1) % 63;		/* XXX */
+
+	tid = WME_AC_TO_TID(tap->txa_ac);
+	args[0] = dialogtoken;
+	args[1]	= IEEE80211_BAPS_POLICY_IMMEDIATE
+		| SM(tid, IEEE80211_BAPS_TID)
+		| SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ)
+		;
+	args[2] = 0;	/* batimeout */
+	args[3] = SM(0, IEEE80211_BASEQ_START)
+		| SM(0, IEEE80211_BASEQ_FRAG)
+		;
+	/* NB: do first so there's no race against reply */
+	if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
+		/* unable to setup state, don't make request */
+		IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_11N,
+		    ni, "%s: could not setup BA stream for AC %d",
+		    __func__, tap->txa_ac);
+		/* defer next try so we don't slam the driver with requests */
+		tap->txa_attempts = IEEE80211_AGGR_MAXTRIES;
+		tap->txa_lastrequest = ticks;
+		return 0;
+	}
+	tokens = dialogtoken;			/* allocate token */
+	return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
+		IEEE80211_ACTION_BA_ADDBA_REQUEST, args);
+}
+
+/*
+ * Terminate an AMPDU tx stream.  State is reclaimed
+ * and the peer notified with a DelBA Action frame.
+ */
+void
+ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t args[4];
+
+	/* XXX locking */
+	if (IEEE80211_AMPDU_RUNNING(tap)) {
+		IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+		    ni, "%s: stop BA stream for AC %d", __func__, tap->txa_ac);
+		ic->ic_stats.is_ampdu_stop++;
+
+		ic->ic_addba_stop(ni, tap);
+		args[0] = WME_AC_TO_TID(tap->txa_ac);
+		args[1] = IEEE80211_DELBAPS_INIT;
+		args[2] = 1;				/* XXX reason code */
+		ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
+			IEEE80211_ACTION_BA_DELBA, args);
+	} else {
+		IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+		    ni, "%s: BA stream for AC %d not running",
+		    __func__, tap->txa_ac);
+		ic->ic_stats.is_ampdu_stop_failed++;
+	}
+}
+
+/*
+ * Transmit a BAR frame to the specified node.  The
+ * BAR contents are drawn from the supplied aggregation
+ * state associated with the node.
+ */
+int
+ieee80211_send_bar(struct ieee80211_node *ni,
+	const struct ieee80211_tx_ampdu *tap)
+{
+#define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ieee80211_frame_min *wh;
+	struct mbuf *m;
+	uint8_t *frm;
+	uint16_t barctl, barseqctl;
+	int tid, ret;
+
+	ieee80211_ref_node(ni);
+
+	m = ieee80211_getmgtframe(&frm,
+		ic->ic_headroom + sizeof(struct ieee80211_frame_min),
+		sizeof(struct ieee80211_ba_request)
+	);
+	if (m == NULL)
+		senderr(ENOMEM, is_tx_nobuf);
+
+	wh = mtod(m, struct ieee80211_frame_min *);
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+		IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
+	wh->i_fc[1] = 0;
+	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+
+	tid = WME_AC_TO_TID(tap->txa_ac);
+	barctl 	= (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
+			IEEE80211_BAPS_POLICY_IMMEDIATE :
+			IEEE80211_BAPS_POLICY_DELAYED)
+		| SM(tid, IEEE80211_BAPS_TID)
+		| SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ)
+		;
+	barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START)
+		| SM(0, IEEE80211_BASEQ_FRAG)
+		;
+	ADDSHORT(frm, barctl);
+	ADDSHORT(frm, barseqctl);
+	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+
+	IEEE80211_NODE_STAT(ni, tx_mgmt);	/* XXX tx_ctl? */
+
+	IEEE80211_NOTE(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+	    ni, "send bar frame (tid %u start %u) on channel %u",
+	    tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+	m->m_pkthdr.rcvif = (void *)ni;
+	IF_ENQUEUE(&ic->ic_mgtq, m);		/* cheat */
+	if_start(ifp);
+
+	return 0;
+bad:
+	ieee80211_free_node(ni);
+	return ret;
+#undef ADDSHORT
+#undef senderr
+}
+
+/*
+ * Send an action management frame.  The arguments are stuff
+ * into a frame without inspection; the caller is assumed to
+ * prepare them carefully (e.g. based on the aggregation state).
+ */
+int
+ieee80211_send_action(struct ieee80211_node *ni,
+	int category, int action, uint16_t args[4])
+{
+#define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+	uint8_t *frm;
+	uint16_t baparamset;
+	int ret;
+
+	KASSERT(ni != NULL, ("null node"));
+
+	/*
+	 * Hold a reference on the node so it doesn't go away until after
+	 * the xmit is complete all the way in the driver.  On error we
+	 * will remove our reference.
+	 */
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
+		__func__, __LINE__,
+		ni, ether_sprintf(ni->ni_macaddr),
+		ieee80211_node_refcnt(ni)+1);
+	ieee80211_ref_node(ni);
+
+	m = ieee80211_getmgtframe(&frm,
+		ic->ic_headroom + sizeof(struct ieee80211_frame),
+		  sizeof(uint16_t)	/* action+category */
+		/* XXX may action payload */
+		+ sizeof(struct ieee80211_action_ba_addbaresponse)
+	);
+	if (m == NULL)
+		senderr(ENOMEM, is_tx_nobuf);
+
+	*frm++ = category;
+	*frm++ = action;
+	switch (category) {
+	case IEEE80211_ACTION_CAT_BA:
+		switch (action) {
+		case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+			    "send ADDBA request: dialogtoken %d "
+			    "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
+			    args[0], args[1], MS(args[1], IEEE80211_BAPS_TID),
+			    args[2], args[3]);
+
+			*frm++ = args[0];	/* dialog token */
+			ADDSHORT(frm, args[1]);	/* baparamset */
+			ADDSHORT(frm, args[2]);	/* batimeout */
+			ADDSHORT(frm, args[3]);	/* baseqctl */
+			break;
+		case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+			    "send ADDBA response: dialogtoken %d status %d "
+			    "baparamset 0x%x (tid %d) batimeout %d",
+			    args[0], args[1], args[2],
+			    MS(args[2], IEEE80211_BAPS_TID), args[3]);
+
+			*frm++ = args[0];	/* dialog token */
+			ADDSHORT(frm, args[1]);	/* statuscode */
+			ADDSHORT(frm, args[2]);	/* baparamset */
+			ADDSHORT(frm, args[3]);	/* batimeout */
+			break;
+		case IEEE80211_ACTION_BA_DELBA:
+			/* XXX */
+			baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
+				   | SM(args[1], IEEE80211_DELBAPS_INIT)
+				   ;
+			ADDSHORT(frm, baparamset);
+			ADDSHORT(frm, args[2]);	/* reason code */
+
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+			    "send DELBA action: tid %d, initiator %d reason %d",
+			    args[0], args[1], args[2]);
+			break;
+		default:
+			goto badaction;
+		}
+		break;
+	case IEEE80211_ACTION_CAT_HT:
+		switch (action) {
+		case IEEE80211_ACTION_HT_TXCHWIDTH:
+			IEEE80211_NOTE(ic,
+			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+			    ni, "send HT txchwidth: width %d",
+			    IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20
+			);
+			*frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 
+				IEEE80211_A_HT_TXCHWIDTH_2040 :
+				IEEE80211_A_HT_TXCHWIDTH_20;
+			break;
+		default:
+			goto badaction;
+		}
+		break;
+	default:
+	badaction:
+		IEEE80211_NOTE(ic,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+		    "%s: unsupported category %d action %d", __func__,
+		    category, action);
+		senderr(EINVAL, is_tx_unknownmgt);
+		/* NOTREACHED */
+	}
+	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+
+	ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
+	if (ret != 0)
+		goto bad;
+	return 0;
+bad:
+	ieee80211_free_node(ni);
+	return ret;
+#undef ADDSHORT
+#undef senderr
+}
+
+/*
+ * Construct the MCS bit mask for inclusion
+ * in an HT information element.
+ */
+static void 
+ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
+{
+	int i;
+
+	for (i = 0; i < rs->rs_nrates; i++) {
+		int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+		if (r < IEEE80211_HTRATE_MAXSIZE) {	/* XXX? */
+			/* NB: this assumes a particular implementation */
+			setbit(frm, r);
+		}
+	}
+}
+
+/*
+ * Add body of an HTCAP information element.
+ */
+static uint8_t *
+ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
+{
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t caps;
+	int rxmax, density;
+
+	/* HT capabilities */
+	caps = ic->ic_htcaps & 0xffff;
+	/*
+	 * Note channel width depends on whether we are operating as
+	 * a sta or not.  When operating as a sta we are generating
+	 * a request based on our desired configuration.  Otherwise
+	 * we are operational and the channel attributes identify
+	 * how we've been setup (which might be different if a fixed
+	 * channel is specified).
+	 */
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		/* override 20/40 use based on config */
+		if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+			caps |= IEEE80211_HTCAP_CHWIDTH40;
+		else
+			caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+		/* use advertised setting (XXX locally constraint) */
+		rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
+		density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
+	} else {
+		/* override 20/40 use based on current channel */
+		if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+			caps |= IEEE80211_HTCAP_CHWIDTH40;
+		else
+			caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+		rxmax = ic->ic_ampdu_rxmax;
+		density = ic->ic_ampdu_density;
+	}
+	/* adjust short GI based on channel and config */
+	if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
+		caps &= ~IEEE80211_HTCAP_SHORTGI20;
+	if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
+	    (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
+		caps &= ~IEEE80211_HTCAP_SHORTGI40;
+	ADDSHORT(frm, caps);
+
+	/* HT parameters */
+	*frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU)
+	     | SM(density, IEEE80211_HTCAP_MPDUDENSITY)
+	     ;
+	frm++;
+
+	/* pre-zero remainder of ie */
+	memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - 
+		__offsetof(struct ieee80211_ie_htcap, hc_mcsset));
+
+	/* supported MCS set */
+	/*
+	 * XXX it would better to get the rate set from ni_htrates
+	 * so we can restrict it but for sta mode ni_htrates isn't
+	 * setup when we're called to form an AssocReq frame so for
+	 * now we're restricted to the default HT rate set.
+	 */
+	ieee80211_set_htrates(frm, &ieee80211_rateset_11n);
+
+	frm += sizeof(struct ieee80211_ie_htcap) -
+		__offsetof(struct ieee80211_ie_htcap, hc_mcsset);
+	return frm;
+#undef ADDSHORT
+}
+
+/*
+ * Add 802.11n HT capabilities information element
+ */
+uint8_t *
+ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni)
+{
+	frm[0] = IEEE80211_ELEMID_HTCAP;
+	frm[1] = sizeof(struct ieee80211_ie_htcap) - 2;
+	return ieee80211_add_htcap_body(frm + 2, ni);
+}
+
+/*
+ * Add Broadcom OUI wrapped standard HTCAP ie; this is
+ * used for compatibility w/ pre-draft implementations.
+ */
+uint8_t *
+ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni)
+{
+	frm[0] = IEEE80211_ELEMID_VENDOR;
+	frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2;
+	frm[2] = (BCM_OUI >> 0) & 0xff;
+	frm[3] = (BCM_OUI >> 8) & 0xff;
+	frm[4] = (BCM_OUI >> 16) & 0xff;
+	frm[5] = BCM_OUI_HTCAP;
+	return ieee80211_add_htcap_body(frm + 6, ni);
+}
+
+/*
+ * Construct the MCS bit mask of basic rates
+ * for inclusion in an HT information element.
+ */
+static void
+ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
+{
+	int i;
+
+	for (i = 0; i < rs->rs_nrates; i++) {
+		int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+		if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) &&
+		    r < IEEE80211_HTRATE_MAXSIZE) {
+			/* NB: this assumes a particular implementation */
+			setbit(frm, r);
+		}
+	}
+}
+
+/*
+ * Update the HTINFO ie for a beacon frame.
+ */
+void
+ieee80211_ht_update_beacon(struct ieee80211com *ic,
+	struct ieee80211_beacon_offsets *bo)
+{
+#define	PROTMODE	(IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
+	struct ieee80211_ie_htinfo *ht =
+	   (struct ieee80211_ie_htinfo *) bo->bo_htinfo;
+
+	/* XXX only update on channel change */
+	ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+	ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
+	if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+	else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+	else
+		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
+	if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+		ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+	/* protection mode */
+	ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
+
+	/* XXX propagate to vendor ie's */
+#undef PROTMODE
+}
+
+/*
+ * Add body of an HTINFO information element.
+ *
+ * NB: We don't use struct ieee80211_ie_htinfo because we can
+ * be called to fillin both a standard ie and a compat ie that
+ * has a vendor OUI at the front.
+ */
+static uint8_t *
+ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	/* pre-zero remainder of ie */
+	memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
+
+	/* primary/control channel center */
+	*frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+
+	frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
+	if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+		frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+	else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+		frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+	else
+		frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
+	if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+		frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+	frm[1] = ic->ic_curhtprotmode;
+
+	frm += 5;
+
+	/* basic MCS set */
+	ieee80211_set_basic_htrates(frm, &ni->ni_htrates);
+	frm += sizeof(struct ieee80211_ie_htinfo) -
+		__offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset);
+	return frm;
+}
+
+/*
+ * Add 802.11n HT information information element.
+ */
+uint8_t *
+ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni)
+{
+	frm[0] = IEEE80211_ELEMID_HTINFO;
+	frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2;
+	return ieee80211_add_htinfo_body(frm + 2, ni);
+}
+
+/*
+ * Add Broadcom OUI wrapped standard HTINFO ie; this is
+ * used for compatibility w/ pre-draft implementations.
+ */
+uint8_t *
+ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni)
+{
+	frm[0] = IEEE80211_ELEMID_VENDOR;
+	frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2;
+	frm[2] = (BCM_OUI >> 0) & 0xff;
+	frm[3] = (BCM_OUI >> 8) & 0xff;
+	frm[4] = (BCM_OUI >> 16) & 0xff;
+	frm[5] = BCM_OUI_HTINFO;
+	return ieee80211_add_htinfo_body(frm + 6, ni);
+}
Index: ieee80211_proto.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_proto.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_proto.c -L sys/net80211/ieee80211_proto.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_proto.c
+++ sys/net80211/ieee80211_proto.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.17.2.9 2006/03/13 03:10:31 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.42.2.3 2007/11/28 06:11:18 sam Exp $");
 
 /*
  * IEEE 802.11 protocol support.
@@ -41,8 +35,8 @@
 
 #include <sys/param.h>
 #include <sys/kernel.h>
-#include <sys/systm.h> 
- 
+#include <sys/systm.h>
+
 #include <sys/socket.h>
 
 #include <net/if.h>
@@ -61,7 +55,7 @@
 	"assoc_req",	"assoc_resp",	"reassoc_req",	"reassoc_resp",
 	"probe_req",	"probe_resp",	"reserved#6",	"reserved#7",
 	"beacon",	"atim",		"disassoc",	"auth",
-	"deauth",	"reserved#13",	"reserved#14",	"reserved#15"
+	"deauth",	"action",	"reserved#14",	"reserved#15"
 };
 const char *ieee80211_ctl_subtype_name[] = {
 	"reserved#0",	"reserved#1",	"reserved#2",	"reserved#3",
@@ -69,12 +63,25 @@
 	"reserved#8",	"reserved#9",	"ps_poll",	"rts",
 	"cts",		"ack",		"cf_end",	"cf_end_ack"
 };
+const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
+	"IBSS",		/* IEEE80211_M_IBSS */
+	"STA",		/* IEEE80211_M_STA */
+	"#2",
+	"AHDEMO",	/* IEEE80211_M_AHDEMO */
+	"#4", "#5",
+	"HOSTAP",	/* IEEE80211_M_HOSTAP */
+	"#7",
+	"MONITOR"	/* IEEE80211_M_MONITOR */
+};
 const char *ieee80211_state_name[IEEE80211_S_MAX] = {
 	"INIT",		/* IEEE80211_S_INIT */
 	"SCAN",		/* IEEE80211_S_SCAN */
 	"AUTH",		/* IEEE80211_S_AUTH */
 	"ASSOC",	/* IEEE80211_S_ASSOC */
-	"RUN"		/* IEEE80211_S_RUN */
+	"CAC",		/* IEEE80211_S_CAC */
+	"RUN",		/* IEEE80211_S_RUN */
+	"CSA",		/* IEEE80211_S_CSA */
+	"SLEEP",	/* IEEE80211_S_SLEEP */
 };
 const char *ieee80211_wme_acnames[] = {
 	"WME_AC_BE",
@@ -86,6 +93,11 @@
 
 static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
 
+static void
+null_update_beacon(struct ieee80211com *ic, int item)
+{
+}
+
 void
 ieee80211_proto_attach(struct ieee80211com *ic)
 {
@@ -99,6 +111,7 @@
 	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
 	ic->ic_bmiss_max = IEEE80211_BMISS_MAX;
 	callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE);
+	callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE);
 	ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
 	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
 	ic->ic_roaming = IEEE80211_ROAMING_AUTO;
@@ -110,10 +123,12 @@
 
 	/* protocol state change handler */
 	ic->ic_newstate = ieee80211_newstate;
+	ic->ic_update_beacon = null_update_beacon;
 
 	/* initialize management frame handlers */
 	ic->ic_recv_mgmt = ieee80211_recv_mgmt;
 	ic->ic_send_mgmt = ieee80211_send_mgmt;
+	ic->ic_raw_xmit = ieee80211_raw_xmit;
 }
 
 void
@@ -128,7 +143,7 @@
 	if (ic->ic_auth->ia_detach)
 		ic->ic_auth->ia_detach(ic);
 
-	IF_DRAIN(&ic->ic_mgtq);
+	ieee80211_drain_ifq(&ic->ic_mgtq);
 	mtx_destroy(&ic->ic_mgtq.ifq_mtx);
 
 	/*
@@ -232,9 +247,9 @@
 }
 
 void
-ieee80211_print_essid(const u_int8_t *essid, int len)
+ieee80211_print_essid(const uint8_t *essid, int len)
 {
-	const u_int8_t *p; 
+	const uint8_t *p;
 	int i;
 
 	if (len > IEEE80211_NWID_LEN)
@@ -257,7 +272,8 @@
 }
 
 void
-ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi)
+ieee80211_dump_pkt(struct ieee80211com *ic,
+	const uint8_t *buf, int len, int rate, int rssi)
 {
 	const struct ieee80211_frame *wh;
 	int i;
@@ -280,7 +296,7 @@
 		printf("(%s)", ether_sprintf(wh->i_addr2));
 		break;
 	case IEEE80211_FC1_DIR_DSTODS:
-		printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1]));
+		printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1]));
 		printf("->%s", ether_sprintf(wh->i_addr3));
 		printf("(%s", ether_sprintf(wh->i_addr2));
 		printf("->%s)", ether_sprintf(wh->i_addr1));
@@ -299,12 +315,22 @@
 		printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
 		break;
 	}
+	if (IEEE80211_QOS_HAS_SEQ(wh)) {
+		const struct ieee80211_qosframe *qwh = 
+			(const struct ieee80211_qosframe *)buf;
+		printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID,
+			qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : "");
+	}
 	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);
+		int off;
+
+		off = ieee80211_anyhdrspace(ic, wh);
+		printf(" WEP [IV %.02x %.02x %.02x",
+			buf[off+0], buf[off+1], buf[off+2]);
+		if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV)
+			printf(" %.02x %.02x %.02x",
+				buf[off+4], buf[off+5], buf[off+6]);
+		printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6);
 	}
 	if (rate >= 0)
 		printf(" %dM", rate / 2);
@@ -321,29 +347,33 @@
 	}
 }
 
+static __inline int
+findrix(const struct ieee80211_rateset *rs, int r)
+{
+	int i;
+
+	for (i = 0; i < rs->rs_nrates; i++)
+		if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r)
+			return i;
+	return -1;
+}
+
 int
-ieee80211_fix_rate(struct ieee80211_node *ni, int flags)
+ieee80211_fix_rate(struct ieee80211_node *ni,
+	struct ieee80211_rateset *nrs, int flags)
 {
 #define	RV(v)	((v) & IEEE80211_RATE_VAL)
 	struct ieee80211com *ic = ni->ni_ic;
-	int i, j, ignore, error;
+	int i, j, rix, error;
 	int okrate, badrate, fixedrate;
-	struct ieee80211_rateset *srs, *nrs;
-	u_int8_t r;
+	const struct ieee80211_rateset *srs;
+	uint8_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 == IEEE80211_FIXED_RATE_NONE)
-		flags &= ~IEEE80211_F_DOFRATE;
 	error = 0;
-	okrate = badrate = fixedrate = 0;
-	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
-	nrs = &ni->ni_rates;
+	okrate = badrate = 0;
+	fixedrate = IEEE80211_FIXED_RATE_NONE;
+	srs = ieee80211_get_suprates(ic, ni->ni_chan);
 	for (i = 0; i < nrs->rs_nrates; ) {
-		ignore = 0;
 		if (flags & IEEE80211_F_DOSORT) {
 			/*
 			 * Sort rates.
@@ -358,63 +388,50 @@
 		}
 		r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
 		badrate = r;
-		if (flags & IEEE80211_F_DOFRATE) {
-			/*
-			 * Check any fixed rate is included. 
-			 */
-			if (r == RV(srs->rs_rates[ic->ic_fixed_rate]))
-				fixedrate = r;
-		}
+		/*
+		 * Check for fixed rate.
+		 */
+		if (r == ic->ic_fixed_rate)
+			fixedrate = r;
+		/*
+		 * Check against supported rates.
+		 */
+		rix = findrix(srs, r);
 		if (flags & IEEE80211_F_DONEGO) {
-			/*
-			 * Check against supported rates.
-			 */
-			for (j = 0; j < srs->rs_nrates; j++) {
-				if (r == RV(srs->rs_rates[j])) {
-					/*
-					 * Overwrite with the supported rate
-					 * value so any basic rate bit is set.
-					 * This insures that response we send
-					 * to stations have the necessary basic
-					 * rate bit set.
-					 */
-					nrs->rs_rates[i] = srs->rs_rates[j];
-					break;
-				}
-			}
-			if (j == srs->rs_nrates) {
+			if (rix < 0) {
 				/*
 				 * A rate in the node's rate set is not
 				 * supported.  If this is a basic rate and we
-				 * are operating as an AP then this is an error.
+				 * are operating as a STA then this is an error.
 				 * Otherwise we just discard/ignore the rate.
-				 * Note that this is important for 11b stations
-				 * when they want to associate with an 11g AP.
 				 */
-				if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+				if ((flags & IEEE80211_F_JOIN) &&
 				    (nrs->rs_rates[i] & IEEE80211_RATE_BASIC))
 					error++;
-				ignore++;
+			} else if ((flags & IEEE80211_F_JOIN) == 0) {
+				/*
+				 * Overwrite with the supported rate
+				 * value so any basic rate bit is set.
+				 */
+				nrs->rs_rates[i] = srs->rs_rates[rix];
 			}
 		}
-		if (flags & IEEE80211_F_DODEL) {
+		if ((flags & IEEE80211_F_DODEL) && rix < 0) {
 			/*
 			 * Delete unacceptable rates.
 			 */
-			if (ignore) {
-				nrs->rs_nrates--;
-				for (j = i; j < nrs->rs_nrates; j++)
-					nrs->rs_rates[j] = nrs->rs_rates[j + 1];
-				nrs->rs_rates[j] = 0;
-				continue;
-			}
+			nrs->rs_nrates--;
+			for (j = i; j < nrs->rs_nrates; j++)
+				nrs->rs_rates[j] = nrs->rs_rates[j + 1];
+			nrs->rs_rates[j] = 0;
+			continue;
 		}
-		if (!ignore)
+		if (rix >= 0)
 			okrate = nrs->rs_rates[i];
 		i++;
 	}
 	if (okrate == 0 || error != 0 ||
-	    ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
+	    ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate))
 		return badrate | IEEE80211_RATE_BASIC;
 	else
 		return RV(okrate);
@@ -436,14 +453,15 @@
 	 * the driver is capable of doing it.
 	 */
 	ieee80211_set_shortslottime(ic,
-		ic->ic_curmode == IEEE80211_MODE_11A ||
-		(ic->ic_curmode == IEEE80211_MODE_11G &&
+		IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+		IEEE80211_IS_CHAN_HT(ic->ic_curchan) ||
+		(IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
 		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 ||
+	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
 	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
 		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
 		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
@@ -507,14 +525,17 @@
 void
 ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
 {
-	static const struct ieee80211_rateset basic[] = {
-	    { 0 },			/* IEEE80211_MODE_AUTO */
+	static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = {
+	    { .rs_nrates = 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 */
+	    { .rs_nrates = 0 },		/* IEEE80211_MODE_FH */
 					/* IEEE80211_MODE_PUREG (not yet) */
 	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+	    { 3, { 12, 24, 48 } },	/* IEEE80211_MODE_11NA */
+					/* IEEE80211_MODE_11NG (mixed b/g) */
+	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },
 	};
 	int i, j;
 
@@ -532,76 +553,97 @@
  * 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;
+	uint8_t aifsn;
+	uint8_t logcwmin;
+	uint8_t logcwmax;
+	uint16_t txopLimit;
+	uint8_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 */ 
+	{ 3, 4,  6,  0, 0 },	/* IEEE80211_MODE_AUTO */
+	{ 3, 4,  6,  0, 0 },	/* IEEE80211_MODE_11A */
+	{ 3, 4,  6,  0, 0 },	/* IEEE80211_MODE_11B */
+	{ 3, 4,  6,  0, 0 },	/* IEEE80211_MODE_11G */
+	{ 3, 4,  6,  0, 0 },	/* IEEE80211_MODE_FH */
+	{ 2, 3,  5,  0, 0 },	/* IEEE80211_MODE_TURBO_A */
+	{ 2, 3,  5,  0, 0 },	/* IEEE80211_MODE_TURBO_G */
+	{ 2, 3,  5,  0, 0 },	/* IEEE80211_MODE_STURBO_A */
+	{ 3, 4,  6,  0, 0 },	/* IEEE80211_MODE_11NA */
+	{ 3, 4,  6,  0, 0 },	/* IEEE80211_MODE_11NG */
 };
 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 */ 
+	{ 7, 4, 10,  0, 0 },	/* IEEE80211_MODE_AUTO */
+	{ 7, 4, 10,  0, 0 },	/* IEEE80211_MODE_11A */
+	{ 7, 4, 10,  0, 0 },	/* IEEE80211_MODE_11B */
+	{ 7, 4, 10,  0, 0 },	/* IEEE80211_MODE_11G */
+	{ 7, 4, 10,  0, 0 },	/* IEEE80211_MODE_FH */
+	{ 7, 3, 10,  0, 0 },	/* IEEE80211_MODE_TURBO_A */
+	{ 7, 3, 10,  0, 0 },	/* IEEE80211_MODE_TURBO_G */
+	{ 7, 3, 10,  0, 0 },	/* IEEE80211_MODE_STURBO_A */
+	{ 7, 4, 10,  0, 0 },	/* IEEE80211_MODE_11NA */
+	{ 7, 4, 10,  0, 0 },	/* IEEE80211_MODE_11NG */
 };
 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 */ 
+	{ 1, 3, 4,  94, 0 },	/* IEEE80211_MODE_AUTO */
+	{ 1, 3, 4,  94, 0 },	/* IEEE80211_MODE_11A */
+	{ 1, 3, 4, 188, 0 },	/* IEEE80211_MODE_11B */
+	{ 1, 3, 4,  94, 0 },	/* IEEE80211_MODE_11G */
+	{ 1, 3, 4, 188, 0 },	/* IEEE80211_MODE_FH */
+	{ 1, 2, 3,  94, 0 },	/* IEEE80211_MODE_TURBO_A */
+	{ 1, 2, 3,  94, 0 },	/* IEEE80211_MODE_TURBO_G */
+	{ 1, 2, 3,  94, 0 },	/* IEEE80211_MODE_STURBO_A */
+	{ 1, 3, 4,  94, 0 },	/* IEEE80211_MODE_11NA */
+	{ 1, 3, 4,  94, 0 },	/* IEEE80211_MODE_11NG */
 };
 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 */ 
+	{ 1, 2, 3,  47, 0 },	/* IEEE80211_MODE_AUTO */
+	{ 1, 2, 3,  47, 0 },	/* IEEE80211_MODE_11A */
+	{ 1, 2, 3, 102, 0 },	/* IEEE80211_MODE_11B */
+	{ 1, 2, 3,  47, 0 },	/* IEEE80211_MODE_11G */
+	{ 1, 2, 3, 102, 0 },	/* IEEE80211_MODE_FH */
+	{ 1, 2, 2,  47, 0 },	/* IEEE80211_MODE_TURBO_A */
+	{ 1, 2, 2,  47, 0 },	/* IEEE80211_MODE_TURBO_G */
+	{ 1, 2, 2,  47, 0 },	/* IEEE80211_MODE_STURBO_A */
+	{ 1, 2, 3,  47, 0 },	/* IEEE80211_MODE_11NA */
+	{ 1, 2, 3,  47, 0 },	/* IEEE80211_MODE_11NG */
 };
 
 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 */ 
+	{ 3, 4, 10,  0, 0 },	/* IEEE80211_MODE_AUTO */
+	{ 3, 4, 10,  0, 0 },	/* IEEE80211_MODE_11A */
+	{ 3, 4, 10,  0, 0 },	/* IEEE80211_MODE_11B */
+	{ 3, 4, 10,  0, 0 },	/* IEEE80211_MODE_11G */
+	{ 3, 4, 10,  0, 0 },	/* IEEE80211_MODE_FH */
+	{ 2, 3, 10,  0, 0 },	/* IEEE80211_MODE_TURBO_A */
+	{ 2, 3, 10,  0, 0 },	/* IEEE80211_MODE_TURBO_G */
+	{ 2, 3, 10,  0, 0 },	/* IEEE80211_MODE_STURBO_A */
+	{ 3, 4, 10,  0, 0 },	/* IEEE80211_MODE_11NA */
+	{ 3, 4, 10,  0, 0 },	/* IEEE80211_MODE_11NG */
 };
 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 */ 
+	{ 2, 3, 4,  94, 0 },	/* IEEE80211_MODE_AUTO */
+	{ 2, 3, 4,  94, 0 },	/* IEEE80211_MODE_11A */
+	{ 2, 3, 4, 188, 0 },	/* IEEE80211_MODE_11B */
+	{ 2, 3, 4,  94, 0 },	/* IEEE80211_MODE_11G */
+	{ 2, 3, 4, 188, 0 },	/* IEEE80211_MODE_FH */
+	{ 2, 2, 3,  94, 0 },	/* IEEE80211_MODE_TURBO_A */
+	{ 2, 2, 3,  94, 0 },	/* IEEE80211_MODE_TURBO_G */
+	{ 2, 2, 3,  94, 0 },	/* IEEE80211_MODE_STURBO_A */
+	{ 2, 3, 4,  94, 0 },	/* IEEE80211_MODE_11NA */
+	{ 2, 3, 4,  94, 0 },	/* IEEE80211_MODE_11NG */
 };
 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 */ 
+	{ 2, 2, 3,  47, 0 },	/* IEEE80211_MODE_AUTO */
+	{ 2, 2, 3,  47, 0 },	/* IEEE80211_MODE_11A */
+	{ 2, 2, 3, 102, 0 },	/* IEEE80211_MODE_11B */
+	{ 2, 2, 3,  47, 0 },	/* IEEE80211_MODE_11G */
+	{ 2, 2, 3, 102, 0 },	/* IEEE80211_MODE_FH */
+	{ 1, 2, 2,  47, 0 },	/* IEEE80211_MODE_TURBO_A */
+	{ 1, 2, 2,  47, 0 },	/* IEEE80211_MODE_TURBO_G */
+	{ 1, 2, 2,  47, 0 },	/* IEEE80211_MODE_STURBO_A */
+	{ 2, 2, 3,  47, 0 },	/* IEEE80211_MODE_11NA */
+	{ 2, 2, 3,  47, 0 },	/* IEEE80211_MODE_11NG */
 };
 
 void
@@ -610,29 +652,40 @@
 	struct ieee80211_wme_state *wme = &ic->ic_wme;
 	const paramType *pPhyParam, *pBssPhyParam;
 	struct wmeParams *wmep;
+	enum ieee80211_phymode mode;
 	int i;
 
 	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
 		return;
 
+	/*
+	 * Select mode; we can be called early in which case we
+	 * always use auto mode.  We know we'll be called when
+	 * entering the RUN state with bsschan setup properly
+	 * so state will eventually get set correctly
+	 */
+	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
+		mode = ieee80211_chan2mode(ic->ic_bsschan);
+	else
+		mode = IEEE80211_MODE_AUTO;
 	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];
+			pPhyParam = &phyParamForAC_BK[mode];
+			pBssPhyParam = &phyParamForAC_BK[mode];
 			break;
 		case WME_AC_VI:
-			pPhyParam = &phyParamForAC_VI[ic->ic_curmode];
-			pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode];
+			pPhyParam = &phyParamForAC_VI[mode];
+			pBssPhyParam = &bssPhyParamForAC_VI[mode];
 			break;
 		case WME_AC_VO:
-			pPhyParam = &phyParamForAC_VO[ic->ic_curmode];
-			pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode];
+			pPhyParam = &phyParamForAC_VO[mode];
+			pBssPhyParam = &bssPhyParamForAC_VO[mode];
 			break;
 		case WME_AC_BE:
 		default:
-			pPhyParam = &phyParamForAC_BE[ic->ic_curmode];
-			pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode];
+			pPhyParam = &phyParamForAC_BE[mode];
+			pBssPhyParam = &bssPhyParamForAC_BE[mode];
 			break;
 		}
 
@@ -700,17 +753,21 @@
 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 */ 
+		{ 2, 4, 10, 64, 0 },	/* IEEE80211_MODE_AUTO */
+		{ 2, 4, 10, 64, 0 },	/* IEEE80211_MODE_11A */
+		{ 2, 5, 10, 64, 0 },	/* IEEE80211_MODE_11B */
+		{ 2, 4, 10, 64, 0 },	/* IEEE80211_MODE_11G */
+		{ 2, 5, 10, 64, 0 },	/* IEEE80211_MODE_FH */
+		{ 1, 3, 10, 64, 0 },	/* IEEE80211_MODE_TURBO_A */
+		{ 1, 3, 10, 64, 0 },	/* IEEE80211_MODE_TURBO_G */
+		{ 1, 3, 10, 64, 0 },	/* IEEE80211_MODE_STURBO_A */
+		{ 2, 4, 10, 64, 0 },	/* IEEE80211_MODE_11NA */ /*XXXcheck*/
+		{ 2, 4, 10, 64, 0 },	/* IEEE80211_MODE_11NG */ /*XXXcheck*/
 	};
 	struct ieee80211_wme_state *wme = &ic->ic_wme;
 	const struct wmeParams *wmep;
 	struct wmeParams *chanp, *bssp;
+	enum ieee80211_phymode mode;
 	int i;
 
        	/* set up the channel access parameters for the physical device */
@@ -731,6 +788,17 @@
 	}
 
 	/*
+	 * Select mode; we can be called early in which case we
+	 * always use auto mode.  We know we'll be called when
+	 * entering the RUN state with bsschan setup properly
+	 * so state will eventually get set correctly
+	 */
+	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
+		mode = ieee80211_chan2mode(ic->ic_bsschan);
+	else
+		mode = IEEE80211_MODE_AUTO;
+
+	/*
 	 * 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
@@ -746,15 +814,14 @@
 		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_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn;
 		chanp->wmep_logcwmin = bssp->wmep_logcwmin =
-			phyParam[ic->ic_curmode].logcwmin;
+			phyParam[mode].logcwmin;
 		chanp->wmep_logcwmax = bssp->wmep_logcwmax =
-			phyParam[ic->ic_curmode].logcwmax;
+			phyParam[mode].logcwmax;
 		chanp->wmep_txopLimit = bssp->wmep_txopLimit =
 			(ic->ic_flags & IEEE80211_F_BURST) ?
-				phyParam[ic->ic_curmode].txopLimit : 0;		
+				phyParam[mode].txopLimit : 0;		
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
 			"%s: %s [acm %u aifsn %u log2(cwmin) %u "
 			"log2(cwmax) %u txpoLimit %u]\n", __func__
@@ -769,7 +836,7 @@
 	
 	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] = {
+        	static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
               		3,	/* IEEE80211_MODE_AUTO */
               		3,	/* IEEE80211_MODE_11A */
               		4,	/* IEEE80211_MODE_11B */
@@ -777,12 +844,14 @@
               		4,	/* IEEE80211_MODE_FH */
               		3,	/* IEEE80211_MODE_TURBO_A */
               		3,	/* IEEE80211_MODE_TURBO_G */
+              		3,	/* IEEE80211_MODE_STURBO_A */
+              		3,	/* IEEE80211_MODE_11NA */
+              		3,	/* IEEE80211_MODE_11NG */
 		};
 		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];
+		chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode];
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
 			"%s: %s log2(cwmin) %u\n", __func__
 			, ieee80211_wme_acnames[WME_AC_BE]
@@ -796,7 +865,7 @@
 		 */
 		wme->wme_bssChanParams.cap_info =
 			(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
-		ic->ic_flags |= IEEE80211_F_WMEUPDATE;
+		ieee80211_beacon_notify(ic, IEEE80211_BEACON_WME);
 	}
 
 	wme->wme_update(ic);
@@ -819,6 +888,75 @@
 	}
 }
 
+/*
+ * Start a device.  If this is the first vap running on the
+ * underlying device then we first bring it up.
+ */
+int
+ieee80211_init(struct ieee80211com *ic, int forcescan)
+{
+
+	IEEE80211_DPRINTF(ic,
+		IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+		"%s\n", "start running");
+
+	/*
+	 * Kick the 802.11 state machine as appropriate.
+	 */
+	if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
+		if (ic->ic_opmode == IEEE80211_M_STA) {
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+		} else {
+			/*
+			 * For monitor+wds modes there's nothing to do but
+			 * start running.  Otherwise, if this is the first
+			 * vap to be brought up, start a scan which may be
+			 * preempted if the station is locked to a particular
+			 * channel.
+			 */
+			if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+			    ic->ic_opmode == IEEE80211_M_WDS) {
+				ic->ic_state = IEEE80211_S_INIT;	/* XXX*/
+				ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+			} else
+				ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+		}
+	}
+	return 0;
+}
+
+/*
+ * Switch between turbo and non-turbo operating modes.
+ * Use the specified channel flags to locate the new
+ * channel, update 802.11 state, and then call back into
+ * the driver to effect the change.
+ */
+void
+ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
+{
+	struct ieee80211_channel *chan;
+
+	chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags);
+	if (chan == NULL) {		/* XXX should not happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+		    "%s: no channel with freq %u flags 0x%x\n",
+		    __func__, ic->ic_bsschan->ic_freq, newflags);
+		return;
+	}
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+	    "%s: %s -> %s (freq %u flags 0x%x)\n", __func__,
+	    ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)],
+	    ieee80211_phymode_name[ieee80211_chan2mode(chan)],
+	    chan->ic_freq, chan->ic_flags);
+
+	ic->ic_bsschan = chan;
+	ic->ic_prevchan = ic->ic_curchan;
+	ic->ic_curchan = chan;
+	ic->ic_set_channel(ic);
+	/* NB: do not need to reset ERP state 'cuz we're in sta mode */
+}
+
 void
 ieee80211_beacon_miss(struct ieee80211com *ic)
 {
@@ -827,8 +965,7 @@
 		/* XXX check ic_curchan != ic_bsschan? */
 		return;
 	}
-	IEEE80211_DPRINTF(ic,
-		IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
 		"%s\n", "beacon miss");
 
 	/*
@@ -853,7 +990,28 @@
 		return;
 	}
 	ic->ic_bmiss_count = 0;
-	ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+	if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
+		/*
+		 * If we receive a beacon miss interrupt when using
+		 * dynamic turbo, attempt to switch modes before
+		 * reassociating.
+		 */
+		if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP))
+			ieee80211_dturbo_switch(ic,
+			    ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
+		/*
+		 * Try to reassociate before scanning for a new ap.
+		 */
+		ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
+	} else {
+		/*
+		 * Somebody else is controlling state changes (e.g.
+		 * a user-mode app) don't do anything that would
+		 * confuse them; just drop into scan mode so they'll
+		 * notified of the state change and given control.
+		 */
+		ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+	}
 }
 
 /*
@@ -897,6 +1055,35 @@
 		IEEE80211_REASON_ASSOC_LEAVE);
 }
 
+/*
+ * Handle deauth with reason.  We retry only for
+ * the cases where we might succeed.  Otherwise
+ * we downgrade the ap and scan.
+ */
+static void
+sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason)
+{
+	switch (reason) {
+	case IEEE80211_STATUS_SUCCESS:
+	case IEEE80211_STATUS_TIMEOUT:
+	case IEEE80211_REASON_ASSOC_EXPIRE:
+	case IEEE80211_REASON_NOT_AUTHED:
+	case IEEE80211_REASON_NOT_ASSOCED:
+	case IEEE80211_REASON_ASSOC_LEAVE:
+	case IEEE80211_REASON_ASSOC_NOT_AUTHED:
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
+		break;
+	default:
+		ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason);
+		if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+			ieee80211_check_scan(ic,
+				IEEE80211_SCAN_ACTIVE,
+				IEEE80211_SCAN_FOREVER,
+				ic->ic_des_nssid, ic->ic_des_ssid);
+		break;
+	}
+}
+
 static int
 ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
@@ -908,6 +1095,9 @@
 	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 */
+	callout_stop(&ic->ic_mgtsend);		/* XXX callout_drain */
+	if (ostate != IEEE80211_S_SCAN)
+		ieee80211_cancel_scan(ic);	/* background scan */
 	ni = ic->ic_bss;			/* NB: no reference held */
 	if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)
 		callout_stop(&ic->ic_swbmiss);
@@ -931,7 +1121,7 @@
 			default:
 				break;
 			}
-			goto reset;
+			break;
 		case IEEE80211_S_ASSOC:
 			switch (ic->ic_opmode) {
 			case IEEE80211_M_STA:
@@ -946,67 +1136,94 @@
 			default:
 				break;
 			}
-			goto reset;
+			break;
 		case IEEE80211_S_SCAN:
 			ieee80211_cancel_scan(ic);
-			goto reset;
+			break;
 		case IEEE80211_S_AUTH:
-		reset:
-			ic->ic_mgt_timer = 0;
-			IF_DRAIN(&ic->ic_mgtq);
-			ieee80211_reset_bss(ic);
+			break;
+		default:
 			break;
 		}
+		if (ostate != IEEE80211_S_INIT) {
+			/* NB: optimize INIT -> INIT case */
+			ieee80211_drain_ifq(&ic->ic_mgtq);
+			ieee80211_reset_bss(ic);
+			ieee80211_scan_flush(ic);
+		}
 		if (ic->ic_auth->ia_detach != NULL)
 			ic->ic_auth->ia_detach(ic);
 		break;
 	case IEEE80211_S_SCAN:
 		switch (ostate) {
 		case IEEE80211_S_INIT:
+		createibss:
 			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;
-				 * bypass the scan and startup immediately.
+				 * Already have a channel; bypass the
+				 * scan and startup immediately.  Because
+				 * of this explicitly sync the scanner state.
 				 */
+				ieee80211_scan_update(ic);
 				ieee80211_create_ibss(ic, ic->ic_des_chan);
 			} else {
-				ieee80211_begin_scan(ic, arg);
+				ieee80211_check_scan(ic,
+					IEEE80211_SCAN_ACTIVE |
+					IEEE80211_SCAN_FLUSH,
+					IEEE80211_SCAN_FOREVER,
+					ic->ic_des_nssid, ic->ic_des_ssid);
 			}
 			break;
 		case IEEE80211_S_SCAN:
+		case IEEE80211_S_AUTH:
+		case IEEE80211_S_ASSOC:
 			/*
-			 * Scan next. If doing an active scan probe
-			 * for the requested ap (if any).
+			 * These can happen either because of a timeout
+			 * on an assoc/auth response or because of a
+			 * change in state that requires a reset.  For
+			 * the former we're called with a non-zero arg
+			 * that is the cause for the failure; pass this
+			 * to the scan code so it can update state.
+			 * Otherwise trigger a new scan unless we're in
+			 * manual roaming mode in which case an application
+			 * must issue an explicit scan request.
 			 */
-			if (ic->ic_flags & IEEE80211_F_ASCAN)
-				ieee80211_probe_curchan(ic, 0);
+			if (arg != 0)
+				ieee80211_scan_assoc_fail(ic,
+					ic->ic_bss->ni_macaddr, arg);
+			if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+				ieee80211_check_scan(ic,
+					IEEE80211_SCAN_ACTIVE,
+					IEEE80211_SCAN_FOREVER,
+					ic->ic_des_nssid, ic->ic_des_ssid);
 			break;
-		case IEEE80211_S_RUN:
-			/* beacon miss */
-			IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE,
-				"no recent beacons from %s; rescanning\n",
-				ether_sprintf(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_scan,
-				ic->ic_bss->ni_macaddr);
-			if (ni != NULL) {
-				ni->ni_fails++;
-				ieee80211_unref_node(&ni);
+		case IEEE80211_S_RUN:		/* beacon miss */
+			if (ic->ic_opmode == IEEE80211_M_STA) {
+				ieee80211_sta_leave(ic, ni);
+				ic->ic_flags &= ~IEEE80211_F_SIBSS;	/* XXX */
+				if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+					ieee80211_check_scan(ic,
+						IEEE80211_SCAN_ACTIVE,
+						IEEE80211_SCAN_FOREVER,
+						ic->ic_des_nssid,
+						ic->ic_des_ssid);
+			} else {
+				ieee80211_iterate_nodes(&ic->ic_sta,
+					sta_disassoc, ic);
+				goto createibss;
 			}
-			if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
-				ieee80211_begin_scan(ic, arg);
+			break;
+		default:
 			break;
 		}
 		break;
 	case IEEE80211_S_AUTH:
+		KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+			("switch to %s state when operating in mode %u",
+			 ieee80211_state_name[nstate], ic->ic_opmode));
 		switch (ostate) {
 		case IEEE80211_S_INIT:
 		case IEEE80211_S_SCAN:
@@ -1015,19 +1232,19 @@
 			break;
 		case IEEE80211_S_AUTH:
 		case IEEE80211_S_ASSOC:
-			switch (arg) {
+			switch (arg & 0xff) {
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				/* ??? */
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
 				break;
 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
-				/* ignore and retry scan on timeout */
+				sta_authretry(ic, ni, arg>>8);
 				break;
 			}
 			break;
 		case IEEE80211_S_RUN:
-			switch (arg) {
+			switch (arg & 0xff) {
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
@@ -1043,27 +1260,35 @@
 				break;
 			}
 			break;
+		default:
+			break;
 		}
 		break;
 	case IEEE80211_S_ASSOC:
+		KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+			("switch to %s state when operating in mode %u",
+			 ieee80211_state_name[nstate], ic->ic_opmode));
 		switch (ostate) {
 		case IEEE80211_S_INIT:
 		case IEEE80211_S_SCAN:
-		case IEEE80211_S_ASSOC:
 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
 				"%s: invalid transition\n", __func__);
 			break;
 		case IEEE80211_S_AUTH:
+		case IEEE80211_S_ASSOC:
 			IEEE80211_SEND_MGMT(ic, ni,
 			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
 			break;
 		case IEEE80211_S_RUN:
 			ieee80211_sta_leave(ic, ni);
 			if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
-				IEEE80211_SEND_MGMT(ic, ni,
-				    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+				IEEE80211_SEND_MGMT(ic, ni, arg ?
+				    IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
+				    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
 			}
 			break;
+		default:
+			break;
 		}
 		break;
 	case IEEE80211_S_RUN:
@@ -1072,8 +1297,20 @@
 		}
 		switch (ostate) {
 		case IEEE80211_S_INIT:
-			if (ic->ic_opmode == IEEE80211_M_MONITOR)
+			if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+			    ic->ic_opmode == IEEE80211_M_WDS ||
+			    ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				/*
+				 * Already have a channel; bypass the
+				 * scan and startup immediately.  Because
+				 * of this explicitly sync the scanner state.
+				 */
+				ieee80211_scan_update(ic);
+				ieee80211_create_ibss(ic,
+				    ieee80211_ht_adjust_channel(ic,
+					ic->ic_curchan, ic->ic_flags_ext));
 				break;
+			}
 			/* fall thru... */
 		case IEEE80211_S_AUTH:
 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
@@ -1101,12 +1338,16 @@
 					IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
 			}
 #endif
-			ic->ic_mgt_timer = 0;
-			if (ic->ic_opmode == IEEE80211_M_STA)
+			if (ic->ic_opmode == IEEE80211_M_STA) {
+				ieee80211_scan_assoc_success(ic,
+					ni->ni_macaddr);
 				ieee80211_notify_node_join(ic, ni, 
 					arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+			}
 			if_start(ifp);		/* XXX not authorized yet */
 			break;
+		default:
+			break;
 		}
 		if (ostate != IEEE80211_S_RUN &&
 		    ic->ic_opmode == IEEE80211_M_STA &&
@@ -1144,8 +1385,10 @@
 		 * Enable inactivity processing.
 		 * XXX
 		 */
-		ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
-		ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT;
+		callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
+			ieee80211_node_timeout, ic);
+		break;
+	default:
 		break;
 	}
 	return 0;
Index: ieee80211_freebsd.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_freebsd.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_freebsd.c -L sys/net80211/ieee80211_freebsd.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_freebsd.c
+++ sys/net80211/ieee80211_freebsd.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -10,8 +10,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -26,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_freebsd.c,v 1.7.2.2 2005/12/22 19:22:51 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_freebsd.c,v 1.16.2.1 2007/11/11 17:44:35 sam Exp $");
 
 /*
  * IEEE 802.11 support (FreeBSD-specific code)
@@ -56,6 +54,27 @@
 SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
 	    0, "debugging printfs");
 #endif
+extern int ieee80211_recv_bar_ena;
+SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
+	    0, "BAR frame processing (ena/dis)");
+
+#ifdef IEEE80211_AMPDU_AGE
+static int
+ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS)
+{
+	extern int ieee80211_ampdu_age;
+	int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age);
+	int error;
+
+	error = sysctl_handle_int(oidp, &ampdu_age, 0, req);
+	if (error || !req->newptr)
+		return error;
+	ieee80211_ampdu_age = msecs_to_ticks(ampdu_age);
+	return 0;
+}
+SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0,
+	ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)");
+#endif
 
 static int
 ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
@@ -138,6 +157,7 @@
 
 	if (ic->ic_sysctl != NULL) {
 		sysctl_ctx_free(ic->ic_sysctl);
+		FREE(ic->ic_sysctl, M_DEVBUF);
 		ic->ic_sysctl = NULL;
 	}
 }
@@ -150,6 +170,35 @@
 	return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
 }
 
+void
+ieee80211_drain_ifq(struct ifqueue *ifq)
+{
+	struct ieee80211_node *ni;
+	struct mbuf *m;
+
+	for (;;) {
+		IF_DEQUEUE(ifq, m);
+		if (m == NULL)
+			break;
+
+		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+		KASSERT(ni != NULL, ("frame w/o node"));
+		ieee80211_free_node(ni);
+		m->m_pkthdr.rcvif = NULL;
+
+		m_freem(m);
+	}
+}
+
+/*
+ * As above, for mbufs allocated with m_gethdr/MGETHDR
+ * or initialized by M_COPY_PKTHDR.
+ */
+#define	MC_ALIGN(m, len)						\
+do {									\
+	(m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1);	\
+} while (/* CONSTCOND */ 0)
+
 /*
  * Allocate and setup a management frame of the specified
  * size.  We return the mbuf and a pointer to the start
@@ -160,7 +209,7 @@
  * can use this interface too.
  */
 struct mbuf *
-ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
+ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen)
 {
 	struct mbuf *m;
 	u_int len;
@@ -169,11 +218,10 @@
 	 * NB: we know the mbuf routines will align the data area
 	 *     so we don't need to do anything special.
 	 */
-	/* XXX 4-address frame? */
-	len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
+	len = roundup2(headroom + pktlen, 4);
 	KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
 	if (len < MINCLSIZE) {
-		m = m_gethdr(M_NOWAIT, MT_HEADER);
+		m = m_gethdr(M_NOWAIT, MT_DATA);
 		/*
 		 * Align the data in case additional headers are added.
 		 * This should only happen when a WEP header is added
@@ -182,27 +230,63 @@
 		 */
 		if (m != NULL)
 			MH_ALIGN(m, len);
-	} else
-		m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR);
+	} else {
+		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+		if (m != NULL)
+			MC_ALIGN(m, len);
+	}
 	if (m != NULL) {
-		m->m_data += sizeof(struct ieee80211_frame);
+		m->m_data += headroom;
 		*frm = m->m_data;
 	}
 	return m;
 }
 
+int
+ieee80211_add_callback(struct mbuf *m,
+	void (*func)(struct ieee80211_node *, void *, int), void *arg)
+{
+	struct m_tag *mtag;
+	struct ieee80211_cb *cb;
+
+	mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK,
+			sizeof(struct ieee80211_cb), M_NOWAIT);
+	if (mtag == NULL)
+		return 0;
+
+	cb = (struct ieee80211_cb *)(mtag+1);
+	cb->func = func;
+	cb->arg = arg;
+	m_tag_prepend(m, mtag);
+	m->m_flags |= M_TXCB;
+	return 1;
+}
+
+void
+ieee80211_process_callback(struct ieee80211_node *ni,
+	struct mbuf *m, int status)
+{
+	struct m_tag *mtag;
+
+	mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL);
+	if (mtag != NULL) {
+		struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1);
+		cb->func(ni, cb->arg, status);
+	}
+}
+
 #include <sys/libkern.h>
 
 void
 get_random_bytes(void *p, size_t n)
 {
-	u_int8_t *dp = p;
+	uint8_t *dp = p;
 
 	while (n > 0) {
-		u_int32_t v = arc4random();
-		size_t nb = n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n;
-		bcopy(&v, dp, n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n);
-		dp += sizeof(u_int32_t), n -= nb;
+		uint32_t v = arc4random();
+		size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n;
+		bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n);
+		dp += sizeof(uint32_t), n -= nb;
 	}
 }
 
@@ -249,8 +333,7 @@
 {
 	struct ifnet *ifp = ic->ic_ifp;
 
-	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
-		"%s: notify scan done\n", ic->ic_ifp->if_xname);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
 
 	/* dispatch wireless event indicating scan completed */
 	rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
@@ -310,14 +393,9 @@
 void
 ieee80211_load_module(const char *modname)
 {
-#ifdef notyet
-	struct thread *td = curthread;
 
-	if (suser(td) == 0 && securelevel_gt(td->td_ucred, 0) == 0) {
-		mtx_lock(&Giant);
-		(void) linker_load_module(modname, NULL, NULL, NULL, NULL);
-		mtx_unlock(&Giant);
-	}
+#ifdef notyet
+	(void)kern_kldload(curthread, modname, NULL);
 #else
 	printf("%s: load the %s module by hand for now.\n", __func__, modname);
 #endif
Index: ieee80211_crypto_wep.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_wep.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_wep.c -L sys/net80211/ieee80211_crypto_wep.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_crypto_wep.c
+++ sys/net80211/ieee80211_crypto_wep.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -30,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_wep.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_wep.c,v 1.10 2007/06/11 03:36:54 sam Exp $");
 
 /*
  * IEEE 802.11 WEP crypto support.
@@ -54,7 +48,7 @@
 static	void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
 static	void wep_detach(struct ieee80211_key *);
 static	int wep_setkey(struct ieee80211_key *);
-static	int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid);
+static	int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid);
 static	int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen);
 static	int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
 static	int wep_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -79,7 +73,7 @@
 
 struct wep_ctx {
 	struct ieee80211com *wc_ic;	/* for diagnostics */
-	u_int32_t	wc_iv;		/* initial vector for crypto */
+	uint32_t	wc_iv;		/* initial vector for crypto */
 };
 
 /* number of references from net80211 layer */
@@ -123,12 +117,12 @@
  * Add privacy headers appropriate for the specified key.
  */
 static int
-wep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+wep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
 {
 	struct wep_ctx *ctx = k->wk_private;
 	struct ieee80211com *ic = ctx->wc_ic;
-	u_int32_t iv;
-	u_int8_t *ivp;
+	uint32_t iv;
+	uint8_t *ivp;
 	int hdrlen;
 
 	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
@@ -139,7 +133,7 @@
 	M_PREPEND(m, wep.ic_header, M_NOWAIT);
 	if (m == NULL)
 		return 0;
-	ivp = mtod(m, u_int8_t *);
+	ivp = mtod(m, uint8_t *);
 	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
 	ivp += hdrlen;
 
@@ -235,7 +229,7 @@
 	/*
 	 * Copy up 802.11 header and strip crypto bits.
 	 */
-	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
+	ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen);
 	m_adj(m, wep.ic_header);
 	m_adj(m, -wep.ic_trailer);
 
@@ -312,7 +306,7 @@
 #define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
 	struct wep_ctx *ctx = key->wk_private;
 	struct mbuf *m = m0;
-	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+	uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
 	uint8_t icv[IEEE80211_WEP_CRCLEN];
 	uint32_t i, j, k, crc;
 	size_t buflen, data_len;
@@ -323,7 +317,7 @@
 	ctx->wc_ic->ic_stats.is_crypto_wep++;
 
 	/* NB: this assumes the header was pulled up */
-	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
+	memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
 	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
 
 	/* Setup RC4 state */
@@ -394,7 +388,7 @@
 #define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
 	struct wep_ctx *ctx = key->wk_private;
 	struct mbuf *m = m0;
-	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+	uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
 	uint8_t icv[IEEE80211_WEP_CRCLEN];
 	uint32_t i, j, k, crc;
 	size_t buflen, data_len;
@@ -405,7 +399,7 @@
 	ctx->wc_ic->ic_stats.is_crypto_wep++;
 
 	/* NB: this assumes the header was pulled up */
-	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
+	memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
 	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
 
 	/* Setup RC4 state */
@@ -479,32 +473,4 @@
 /*
  * Module glue.
  */
-static int
-wep_modevent(module_t mod, int type, void *unused)
-{
-	switch (type) {
-	case MOD_LOAD:
-		ieee80211_crypto_register(&wep);
-		return 0;
-	case MOD_UNLOAD:
-	case MOD_QUIESCE:
-		if (nrefs) {
-			printf("wlan_wep: still in use (%u dynamic refs)\n",
-				nrefs);
-			return EBUSY;
-		}
-		if (type == MOD_UNLOAD)
-			ieee80211_crypto_unregister(&wep);
-		return 0;
-	}
-	return EINVAL;
-}
-
-static moduledata_t wep_mod = {
-	"wlan_wep",
-	wep_modevent,
-	0
-};
-DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_wep, 1);
-MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
+IEEE80211_CRYPTO_MODULE(wep, 1);
Index: ieee80211_node.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_node.h -L sys/net80211/ieee80211_node.h -u -r1.2 -r1.3
--- sys/net80211/ieee80211_node.h
+++ sys/net80211/ieee80211_node.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,12 +23,13 @@
  * (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.17.2.5 2006/03/13 03:05:47 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.29.2.1 2007/11/11 17:44:36 sam Exp $
  */
 #ifndef _NET80211_IEEE80211_NODE_H_
 #define _NET80211_IEEE80211_NODE_H_
 
 #include <net80211/ieee80211_ioctl.h>		/* for ieee80211_nodestats */
+#include <net80211/ieee80211_ht.h>		/* for aggregation state */
 
 /*
  * Each ieee80211com instance has a single timer that fires once a
@@ -58,23 +53,26 @@
 #define	IEEE80211_INACT_PROBE	(30/IEEE80211_INACT_WAIT)	/* probe */
 #define	IEEE80211_INACT_SCAN	(300/IEEE80211_INACT_WAIT)	/* scanned */
 
-#define	IEEE80211_TRANS_WAIT 	5		/* mgt frame tx timer (secs) */
+#define	IEEE80211_TRANS_WAIT 	2		/* mgt frame tx timer (secs) */
+
+/* threshold for aging overlapping non-ERP bss */
+#define	IEEE80211_NONERP_PRESENT_AGE	msecs_to_ticks(60*1000)
 
 #define	IEEE80211_NODE_HASHSIZE	32
 /* simple hash is enough for variation of macaddr */
 #define	IEEE80211_NODE_HASH(addr)	\
-	(((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
+	(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
 		IEEE80211_NODE_HASHSIZE)
 
 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 */
+	uint8_t		rsn_mcastcipher;	/* mcast/group cipher */
+	uint8_t		rsn_mcastkeylen;	/* mcast key length */
+	uint8_t		rsn_ucastcipherset;	/* unicast cipher set */
+	uint8_t		rsn_ucastcipher;	/* selected unicast cipher */
+	uint8_t		rsn_ucastkeylen;	/* unicast key length */
+	uint8_t		rsn_keymgmtset;		/* key mangement algorithms */
+	uint8_t		rsn_keymgmt;		/* selected key mgmt algo */
+	uint16_t	rsn_caps;		/* capabilities */
 };
 
 struct ieee80211_node_table;
@@ -93,52 +91,87 @@
 	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 */
+	uint8_t			ni_authmode;	/* authentication algorithm */
+	uint8_t			ni_ath_flags;	/* Atheros feature flags */
+	/* NB: These must have the same values as IEEE80211_ATHC_* */
+#define IEEE80211_NODE_TURBOP	0x0001		/* Turbo prime enable */
+#define IEEE80211_NODE_COMP	0x0002		/* Compresssion enable */
+#define IEEE80211_NODE_FF	0x0004          /* Fast Frame capable */
+#define IEEE80211_NODE_XR	0x0008		/* Atheros WME enable */
+#define IEEE80211_NODE_AR	0x0010		/* AR capable */
+#define IEEE80211_NODE_BOOST	0x0080 
+#define IEEE80211_NODE_PSUPDATE	0x0200		/* power save state changed */ 
+#define	IEEE80211_NODE_CHWUPDATE 0x0400		/* 11n channel width change */
+	uint16_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 */
 #define	IEEE80211_NODE_AREF	0x0020		/* authentication ref held */
-	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 */
+#define	IEEE80211_NODE_HT	0x0040		/* HT enabled */
+#define	IEEE80211_NODE_HTCOMPAT	0x0080		/* HT setup w/ vendor OUI's */
+#define	IEEE80211_NODE_AMPDU_RX	0x0400		/* AMPDU rx enabled */
+#define	IEEE80211_NODE_AMPDU_TX	0x0800		/* AMPDU tx enabled */
+	uint16_t		ni_ath_defkeyix;/* Atheros def key index */
+	uint16_t		ni_associd;	/* assoc response */
+	uint16_t		ni_txpower;	/* current transmit power */
+	uint16_t		ni_vlan;	/* vlan tag */
+	uint32_t		ni_jointime;	/* time of join (secs) */
+	uint32_t		*ni_challenge;	/* shared-key challenge */
+	uint8_t			*ni_wpa_ie;	/* captured WPA ie */
+	uint8_t			*ni_rsn_ie;	/* captured RSN ie */
+	uint8_t			*ni_wme_ie;	/* captured WME ie */
+	uint8_t			*ni_ath_ie;	/* captured Atheros ie */
+						/* tx seq per-tid */
+	uint16_t		ni_txseqs[IEEE80211_TID_SIZE];
+						/* rx seq previous per-tid*/
+	uint16_t		ni_rxseqs[IEEE80211_TID_SIZE];
+	uint32_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 */
-	u_int32_t		ni_rstamp;	/* recv timestamp */
-	u_int8_t		ni_rssi;	/* recv ssi */
+	uint32_t		ni_rstamp;	/* recv timestamp */
+	int8_t			ni_rssi;	/* recv ssi */
+	int8_t			ni_noise;	/* noise floor */
 
 	/* header */
-	u_int8_t		ni_macaddr[IEEE80211_ADDR_LEN];
-	u_int8_t		ni_bssid[IEEE80211_ADDR_LEN];
+	uint8_t			ni_macaddr[IEEE80211_ADDR_LEN];
+	uint8_t			ni_bssid[IEEE80211_ADDR_LEN];
 
 	/* beacon, probe response */
 	union {
-		u_int8_t	data[8];
-		u_int64_t	tsf;
+		uint8_t		data[8];
+		uint64_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];
+	uint16_t		ni_intval;	/* beacon interval */
+	uint16_t		ni_capinfo;	/* capabilities */
+	uint8_t			ni_esslen;
+	uint8_t			ni_essid[IEEE80211_NWID_LEN];
 	struct ieee80211_rateset ni_rates;	/* negotiated rate set */
-	struct ieee80211_channel *ni_chan;	/* XXX multiple uses */
-	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 */
-	u_int8_t		ni_dtim_period;	/* DTIM period */
-	u_int8_t		ni_dtim_count;	/* DTIM count for last bcn */
+	struct ieee80211_channel *ni_chan;
+	uint16_t		ni_fhdwell;	/* FH only */
+	uint8_t			ni_fhindex;	/* FH only */
+	uint8_t			ni_erp;		/* ERP from beacon/probe resp */
+	uint16_t		ni_timoff;	/* byte offset to TIM ie */
+	uint8_t			ni_dtim_period;	/* DTIM period */
+	uint8_t			ni_dtim_count;	/* DTIM count for last bcn */
+
+	/* 11n state */
+	uint8_t			*ni_htcap_ie;	/* captured HTCAP ie */
+	uint16_t		ni_htcap;	/* HT capabilities */
+	uint8_t			ni_htparam;	/* HT params */
+	uint8_t			ni_htctlchan;	/* HT control channel */
+	uint8_t			ni_ht2ndchan;	/* HT 2nd channel */
+	uint8_t			ni_htopmode;	/* HT operating mode */
+	uint8_t			ni_htstbc;	/* HT */
+	uint8_t			ni_reqcw;	/* requested tx channel width */
+	uint8_t			ni_chw;		/* negotiated channel width */
+	struct ieee80211_htrateset ni_htrates;	/* negotiated ht rate set */
+	struct ieee80211_tx_ampdu ni_tx_ampdu[WME_NUM_AC];
+	struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
 
 	/* others */
 	int			ni_fails;	/* failure count to associate */
@@ -147,10 +180,13 @@
 	int			ni_txrate;	/* index to ni_rates[] */
 	struct	ifqueue		ni_savedq;	/* ps-poll queue */
 	struct ieee80211_nodestats ni_stats;	/* per-node statistics */
-	u_int32_t		ni_pad[8];	/* future expansion */
 };
 MALLOC_DECLARE(M_80211_NODE);
 
+#define	IEEE80211_NODE_ATH	(IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP)
+#define	IEEE80211_NODE_AMPDU \
+	(IEEE80211_NODE_AMPDU_RX | IEEE80211_NODE_AMPDU_TX)
+
 #define	IEEE80211_NODE_AID(ni)	IEEE80211_AID(ni->ni_associd)
 
 #define	IEEE80211_NODE_STAT(ni,stat)	(ni->ni_stats.ns_##stat++)
@@ -186,15 +222,14 @@
 void	ieee80211_node_authorize(struct ieee80211_node *);
 void	ieee80211_node_unauthorize(struct ieee80211_node *);
 
-void	ieee80211_begin_scan(struct ieee80211com *, int);
-int	ieee80211_next_scan(struct ieee80211com *);
 void	ieee80211_probe_curchan(struct ieee80211com *, int);
 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 *);
+void	ieee80211_setbsschan(struct ieee80211com *, struct ieee80211_channel *);
 int	ieee80211_ibss_merge(struct ieee80211_node *);
-int	ieee80211_sta_join(struct ieee80211com *, struct ieee80211_node *);
+struct ieee80211_scan_entry;
+int	ieee80211_sta_join(struct ieee80211com *,
+		const struct ieee80211_scan_entry *);
 void	ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *);
 
 /*
@@ -208,46 +243,43 @@
 	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];
+	struct ieee80211_node	**nt_keyixmap;	/* key ix -> node map */
+	int			nt_keyixmax;	/* keyixmap size */
 	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 */
-	struct ieee80211_node	**nt_keyixmap;	/* key ix -> node map */
-	int			nt_keyixmax;	/* keyixmap size */
-
-	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_table *, const uint8_t *);
 struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *,
-		const u_int8_t *macaddr);
+		const uint8_t *macaddr);
 struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
-		const u_int8_t *);
+		const uint8_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 *,
+struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
+		const uint8_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_rxnode_debug(
-		struct ieee80211com *, const struct ieee80211_frame_min *,
+struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
+		struct ieee80211com *,
+		const struct ieee80211_frame_min *, uint16_t keyix,
 		const char *func, int line);
 struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
 		struct ieee80211com *,
-		const struct ieee80211_frame_min *, u_int16_t keyix,
+		const struct ieee80211_frame_min *, uint16_t keyix,
 		const char *func, int line);
-struct ieee80211_node *ieee80211_find_txnode_debug(
-		struct ieee80211com *, const u_int8_t *,
+struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211com *,
+		const uint8_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,
+		struct ieee80211_node_table *, const uint8_t *macaddr,
+		u_int ssidlen, const uint8_t *ssid,
 		const char *func, int line);
 #define	ieee80211_free_node(ni) \
 	ieee80211_free_node_debug(ni, __func__, __LINE__)
@@ -259,28 +291,24 @@
 	ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __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_node(struct ieee80211_node_table *,
+		const uint8_t *);
+struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *,
+		const struct ieee80211_frame_min *);
 struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
-		const struct ieee80211_frame_min *, u_int16_t keyix);
-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 *);
+		const struct ieee80211_frame_min *, uint16_t keyix);
+struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
+		const uint8_t *);
 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);
+		struct ieee80211_node_table *, const uint8_t *macaddr,
+		u_int ssidlen, const uint8_t *ssid);
 #endif
 int	ieee80211_node_delucastkey(struct ieee80211_node *);
+void	ieee80211_node_timeout(void *arg);
 
 typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
 void	ieee80211_iterate_nodes(struct ieee80211_node_table *,
@@ -290,46 +318,19 @@
 		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);
+void	ieee80211_notify_erp(struct ieee80211com *);
 
-/*
- * Parameters supplied when adding/updating an entry in a
- * scan cache.  Pointer variables should be set to NULL
- * if no data is available.  Pointer references can be to
- * local data; any information that is saved will be copied.
- * All multi-byte values must be in host byte order.
- */
-struct ieee80211_scanparams {
-	u_int16_t	capinfo;	/* 802.11 capabilities */
-	u_int16_t	fhdwell;	/* FHSS dwell interval */
-	u_int8_t	chan;		/* */
-	u_int8_t	bchan;
-	u_int8_t	fhindex;
-	u_int8_t	erp;
-	u_int16_t	bintval;
-	u_int8_t	timoff;
-	u_int8_t	*tim;
-	u_int8_t	*tstamp;
-	u_int8_t	*country;
-	u_int8_t	*ssid;
-	u_int8_t	*rates;
-	u_int8_t	*xrates;
-	u_int8_t	*wpa;
-	u_int8_t	*wme;
-};
-
-void	ieee80211_add_scan(struct ieee80211com *,
-		const struct ieee80211_scanparams *,
-		const struct ieee80211_frame *,
-		int subtype, int rssi, int rstamp);
+struct ieee80211_node *ieee80211_fakeup_adhoc_node(
+		struct ieee80211_node_table *, const uint8_t macaddr[]);
+struct ieee80211_scanparams;
 void	ieee80211_init_neighbor(struct ieee80211_node *,
 		const struct ieee80211_frame *,
 		const struct ieee80211_scanparams *);
 struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *,
 		const struct ieee80211_frame *,
 		const struct ieee80211_scanparams *);
+void	ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
+void	ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
+int8_t	ieee80211_getrssi(struct ieee80211com *);
+void	ieee80211_getsignal(struct ieee80211com *, int8_t *, int8_t *);
 #endif /* _NET80211_IEEE80211_NODE_H_ */
Index: ieee80211_crypto.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto.c -L sys/net80211/ieee80211_crypto.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_crypto.c
+++ sys/net80211/ieee80211_crypto.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.10.2.2 2005/09/03 22:40:02 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.16 2007/06/11 03:36:54 sam Exp $");
 
 /*
  * IEEE 802.11 generic crypto support.
@@ -90,7 +84,7 @@
 }
 static 	int
 null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
-	     const u_int8_t mac[IEEE80211_ADDR_LEN])
+	const uint8_t mac[IEEE80211_ADDR_LEN])
 {
 	return 1;
 }
@@ -131,7 +125,7 @@
 
 static __inline int
 dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
-	const u_int8_t mac[IEEE80211_ADDR_LEN])
+	const uint8_t mac[IEEE80211_ADDR_LEN])
 {
 	return ic->ic_crypto.cs_key_set(ic, key, mac);
 }
@@ -467,7 +461,7 @@
  */
 int
 ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
-		const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+		const uint8_t macaddr[IEEE80211_ADDR_LEN])
 {
 	const struct ieee80211_cipher *cip = key->wk_cipher;
 
@@ -511,7 +505,7 @@
 	struct ieee80211_key *k;
 	struct ieee80211_frame *wh;
 	const struct ieee80211_cipher *cip;
-	u_int8_t keyid;
+	uint8_t keyid;
 
 	/*
 	 * Multicast traffic always uses the multicast key.
@@ -521,7 +515,7 @@
 	 */
 	wh = mtod(m, struct ieee80211_frame *);
 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
-	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+	    IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
 		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
 			    "[%s] no default transmit key (%s) deftxkey %u\n",
@@ -555,8 +549,8 @@
 	struct ieee80211_key *k;
 	struct ieee80211_frame *wh;
 	const struct ieee80211_cipher *cip;
-	const u_int8_t *ivp;
-	u_int8_t keyid;
+	const uint8_t *ivp;
+	uint8_t keyid;
 
 	/* NB: this minimum size data frame could be bigger */
 	if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
@@ -574,10 +568,10 @@
 	 * the key id in the header is meaningless (typically 0).
 	 */
 	wh = mtod(m, struct ieee80211_frame *);
-	ivp = mtod(m, const u_int8_t *) + hdrlen;	/* XXX contig */
+	ivp = mtod(m, const uint8_t *) + hdrlen;	/* XXX contig */
 	keyid = ivp[IEEE80211_WEP_IVLEN];
 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
-	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none)
+	    IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey))
 		k = &ic->ic_nw_keys[keyid >> 6];
 	else
 		k = &ni->ni_ucastkey;
@@ -592,7 +586,7 @@
 		    "[%s] unable to pullup %s header\n",
 		    ether_sprintf(wh->i_addr2), cip->ic_name);
 		ic->ic_stats.is_rx_wepfail++;	/* XXX */
-		return 0;
+		return NULL;
 	}
 
 	return (cip->ic_decap(k, m, hdrlen) ? k : NULL);
Index: ieee80211.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211.c -L sys/net80211/ieee80211.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211.c
+++ sys/net80211/ieee80211.c
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.19.2.7 2006/03/11 19:25:23 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.43.2.2 2007/12/07 05:46:08 kmacy Exp $");
 
 /*
  * IEEE 802.11 generic handler
@@ -59,13 +53,36 @@
 	"FH",		/* IEEE80211_MODE_FH */
 	"turboA",	/* IEEE80211_MODE_TURBO_A */
 	"turboG",	/* IEEE80211_MODE_TURBO_G */
+	"sturboA",	/* IEEE80211_MODE_STURBO_A */
+	"11na",		/* IEEE80211_MODE_11NA */
+	"11ng",		/* IEEE80211_MODE_11NG */
 };
 
+/*
+ * Default supported rates for 802.11 operation (in IEEE .5Mb units).
+ */
+#define	B(r)	((r) | IEEE80211_RATE_BASIC)
+static const struct ieee80211_rateset ieee80211_rateset_11a =
+	{ 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } };
+static const struct ieee80211_rateset ieee80211_rateset_half =
+	{ 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } };
+static const struct ieee80211_rateset ieee80211_rateset_quarter =
+	{ 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } };
+static const struct ieee80211_rateset ieee80211_rateset_11b =
+	{ 4, { B(2), B(4), B(11), B(22) } };
+/* NB: OFDM rates are handled specially based on mode */
+static const struct ieee80211_rateset ieee80211_rateset_11g =
+	{ 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
+#undef B
+
+static	int media_status(enum ieee80211_opmode ,
+		const struct ieee80211_channel *);
+
 /* 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 uint8_t ieee80211_vapmap[32];		/* enough for 256 */
 static struct mtx ieee80211_vap_mtx;
 MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
 
@@ -74,7 +91,7 @@
 {
 #define	N(a)	(sizeof(a)/sizeof(a[0]))
 	int i;
-	u_int8_t b;
+	uint8_t b;
 
 	mtx_lock(&ieee80211_vap_mtx);
 	ic->ic_vap = 0;
@@ -118,86 +135,134 @@
 	return ENETRESET;
 }
 
+/*
+ * Fill in 802.11 available channel set, mark
+ * all available channels as active, and pick
+ * a default channel if not already specified.
+ */
+static void
+ieee80211_chan_init(struct ieee80211com *ic)
+{
+#define	DEFAULTRATES(m, def) do { \
+	if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \
+		ic->ic_sup_rates[m] = def; \
+} while (0)
+	struct ieee80211_channel *c;
+	int i;
+
+	KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
+		("invalid number of channels specified: %u", ic->ic_nchans));
+	memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+	setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
+	for (i = 0; i < ic->ic_nchans; i++) {
+		c = &ic->ic_channels[i];
+		KASSERT(c->ic_flags != 0, ("channel with no flags"));
+		KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX,
+			("channel with bogus ieee number %u", c->ic_ieee));
+		setbit(ic->ic_chan_avail, c->ic_ieee);
+		/*
+		 * Identify mode capabilities.
+		 */
+		if (IEEE80211_IS_CHAN_A(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_11A);
+		if (IEEE80211_IS_CHAN_B(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_11B);
+		if (IEEE80211_IS_CHAN_ANYG(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_11G);
+		if (IEEE80211_IS_CHAN_FHSS(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_FH);
+		if (IEEE80211_IS_CHAN_108A(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A);
+		if (IEEE80211_IS_CHAN_108G(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G);
+		if (IEEE80211_IS_CHAN_ST(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A);
+		if (IEEE80211_IS_CHAN_HTA(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_11NA);
+		if (IEEE80211_IS_CHAN_HTG(c))
+			setbit(ic->ic_modecaps, IEEE80211_MODE_11NG);
+	}
+	/* initialize candidate channels to all available */
+	memcpy(ic->ic_chan_active, ic->ic_chan_avail,
+		sizeof(ic->ic_chan_avail));
+
+	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
+	ic->ic_bsschan = IEEE80211_CHAN_ANYC;
+	ic->ic_prevchan = NULL;
+	/* arbitrarily pick the first channel */
+	ic->ic_curchan = &ic->ic_channels[0];
+
+	/* fillin well-known rate sets if driver has not specified */
+	DEFAULTRATES(IEEE80211_MODE_11B,	 ieee80211_rateset_11b);
+	DEFAULTRATES(IEEE80211_MODE_11G,	 ieee80211_rateset_11g);
+	DEFAULTRATES(IEEE80211_MODE_11A,	 ieee80211_rateset_11a);
+	DEFAULTRATES(IEEE80211_MODE_TURBO_A,	 ieee80211_rateset_11a);
+	DEFAULTRATES(IEEE80211_MODE_TURBO_G,	 ieee80211_rateset_11g);
+
+	/*
+	 * Set auto mode to reset active channel state and any desired channel.
+	 */
+	(void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO);
+#undef DEFAULTRATES
+}
+
 void
 ieee80211_ifattach(struct ieee80211com *ic)
 {
 	struct ifnet *ifp = ic->ic_ifp;
-	struct ieee80211_channel *c;
-	int i;
 
 	ether_ifattach(ifp, ic->ic_myaddr);
+	ifp->if_output = ieee80211_output;
+
 	bpfattach2(ifp, DLT_IEEE802_11,
 	    sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
 
-	ieee80211_crypto_attach(ic);
+	/* override the 802.3 setting */
+	ifp->if_hdrlen = ic->ic_headroom
+		+ sizeof(struct ieee80211_qosframe_addr4)
+		+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+		+ IEEE80211_WEP_EXTIVLEN;
+	/* XXX no way to recalculate on ifdetach */
+	if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
+		/* XXX sanity check... */
+		max_linkhdr = ALIGN(ifp->if_hdrlen);
+		max_hdr = max_linkhdr + max_protohdr;
+		max_datalen = MHLEN - max_hdr;
+	}
 
 	/*
-	 * Fill in 802.11 available channel set, mark
-	 * all available channels as active, and pick
-	 * a default channel if not already specified.
+	 * Fill in 802.11 available channel set, mark all
+	 * available channels as active, and pick a default
+	 * channel if not already specified.
 	 */
-	memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
-	ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO;
-	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
-		c = &ic->ic_channels[i];
-		if (c->ic_flags) {
-			/*
-			 * Verify driver passed us valid data.
-			 */
-			if (i != ieee80211_chan2ieee(ic, c)) {
-				if_printf(ifp, "bad channel ignored; "
-					"freq %u flags %x number %u\n",
-					c->ic_freq, c->ic_flags, i);
-				c->ic_flags = 0;	/* NB: remove */
-				continue;
-			}
-			setbit(ic->ic_chan_avail, i);
-			/*
-			 * Identify mode capabilities.
-			 */
-			if (IEEE80211_IS_CHAN_A(c))
-				ic->ic_modecaps |= 1<<IEEE80211_MODE_11A;
-			if (IEEE80211_IS_CHAN_B(c))
-				ic->ic_modecaps |= 1<<IEEE80211_MODE_11B;
-			if (IEEE80211_IS_CHAN_PUREG(c))
-				ic->ic_modecaps |= 1<<IEEE80211_MODE_11G;
-			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_A;
-			if (IEEE80211_IS_CHAN_108G(c))
-				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G;
-			if (ic->ic_curchan == NULL) {
-				/* arbitrarily pick the first channel */
-				ic->ic_curchan = &ic->ic_channels[i];
-			}
-		}
-	}
-	/* 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 */
+	ieee80211_chan_init(ic);
+
+	if (ic->ic_caps & IEEE80211_C_BGSCAN)	/* enable if capable */
+		ic->ic_flags |= IEEE80211_F_BGSCAN;
 #if 0
-	/*
-	 * Enable WME by default if we're capable.
-	 */
-	if (ic->ic_caps & IEEE80211_C_WME)
+	/* XXX not until WME+WPA issues resolved */
+	if (ic->ic_caps & IEEE80211_C_WME)	/* enable if capable */
 		ic->ic_flags |= IEEE80211_F_WME;
 #endif
 	if (ic->ic_caps & IEEE80211_C_BURST)
 		ic->ic_flags |= IEEE80211_F_BURST;
-	(void) ieee80211_setmode(ic, ic->ic_curmode);
+	ic->ic_flags |= IEEE80211_F_DOTH;	/* XXX out of caps, just ena */
 
 	ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
 	ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
 	ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
+	IEEE80211_LOCK_INIT(ic, "ieee80211com");
 	IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
 
 	ic->ic_lintval = ic->ic_bintval;
 	ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
 
+	ieee80211_crypto_attach(ic);
 	ieee80211_node_attach(ic);
+	ieee80211_power_attach(ic);
 	ieee80211_proto_attach(ic);
+	ieee80211_ht_attach(ic);
+	ieee80211_scan_attach(ic);
 
 	ieee80211_add_vap(ic);
 
@@ -209,6 +274,9 @@
 	 */
 	if (ic->ic_reset == NULL)
 		ic->ic_reset = ieee80211_default_reset;
+
+	KASSERT(ifp->if_llsoftc == NULL, ("oops, hosed"));
+	ifp->if_llsoftc = ic;
 }
 
 void
@@ -219,61 +287,98 @@
 	ieee80211_remove_vap(ic);
 
 	ieee80211_sysctl_detach(ic);
+	ieee80211_scan_detach(ic);
+	ieee80211_ht_detach(ic);
+	/* NB: must be called before ieee80211_node_detach */
 	ieee80211_proto_detach(ic);
 	ieee80211_crypto_detach(ic);
+	ieee80211_power_detach(ic);
 	ieee80211_node_detach(ic);
 	ifmedia_removeall(&ic->ic_media);
 
+	IEEE80211_LOCK_DESTROY(ic);
 	IEEE80211_BEACON_LOCK_DESTROY(ic);
 
 	bpfdetach(ifp);
 	ether_ifdetach(ifp);
 }
 
+static __inline int
+mapgsm(u_int freq, u_int flags)
+{
+	freq *= 10;
+	if (flags & IEEE80211_CHAN_QUARTER)
+		freq += 5;
+	else if (flags & IEEE80211_CHAN_HALF)
+		freq += 10;
+	else
+		freq += 20;
+	/* NB: there is no 907/20 wide but leave room */
+	return (freq - 906*10) / 5;
+}
+
+static __inline int
+mappsb(u_int freq, u_int flags)
+{
+	return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5;
+}
+
 /*
  * Convert MHz frequency to IEEE channel number.
  */
-u_int
+int
 ieee80211_mhz2ieee(u_int freq, u_int flags)
 {
+#define	IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990)
+	if (flags & IEEE80211_CHAN_GSM)
+		return mapgsm(freq, flags);
 	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
 		if (freq == 2484)
 			return 14;
 		if (freq < 2484)
-			return (freq - 2407) / 5;
+			return ((int) freq - 2407) / 5;
 		else
 			return 15 + ((freq - 2512) / 20);
 	} else if (flags & IEEE80211_CHAN_5GHZ) {	/* 5Ghz band */
-		return (freq - 5000) / 5;
+		if (freq <= 5000) {
+			/* XXX check regdomain? */
+			if (IS_FREQ_IN_PSB(freq))
+				return mappsb(freq, flags);
+			return (freq - 4000) / 5;
+		} else
+			return (freq - 5000) / 5;
 	} else {				/* either, guess */
 		if (freq == 2484)
 			return 14;
-		if (freq < 2484)
-			return (freq - 2407) / 5;
-		if (freq < 5000)
-			return 15 + ((freq - 2512) / 20);
+		if (freq < 2484) {
+			if (907 <= freq && freq <= 922)
+				return mapgsm(freq, flags);
+			return ((int) freq - 2407) / 5;
+		}
+		if (freq < 5000) {
+			if (IS_FREQ_IN_PSB(freq))
+				return mappsb(freq, flags);
+			else if (freq > 4900)
+				return (freq - 4000) / 5;
+			else
+				return 15 + ((freq - 2512) / 20);
+		}
 		return (freq - 5000) / 5;
 	}
+#undef IS_FREQ_IN_PSB
 }
 
 /*
  * Convert channel to IEEE channel number.
  */
-u_int
-ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c)
+int
+ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c)
 {
-	if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX])
-		return c - ic->ic_channels;
-	else if (c == IEEE80211_CHAN_ANYC)
-		return IEEE80211_CHAN_ANY;
-	else if (c != NULL) {
-		if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n",
-			c->ic_freq, c->ic_flags);
-		return 0;		/* XXX */
-	} else {
+	if (c == NULL) {
 		if_printf(ic->ic_ifp, "invalid channel (NULL)\n");
 		return 0;		/* XXX */
 	}
+	return (c == IEEE80211_CHAN_ANYC ?  IEEE80211_CHAN_ANY : c->ic_ieee);
 }
 
 /*
@@ -282,6 +387,8 @@
 u_int
 ieee80211_ieee2mhz(u_int chan, u_int flags)
 {
+	if (flags & IEEE80211_CHAN_GSM)
+		return 907 + 5 * (chan / 10);
 	if (flags & IEEE80211_CHAN_2GHZ) {	/* 2GHz band */
 		if (chan == 14)
 			return 2484;
@@ -290,8 +397,13 @@
 		else
 			return 2512 + ((chan-15)*20);
 	} else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */
+		if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) {
+			chan -= 37;
+			return 4940 + chan*5 + (chan % 5 ? 2 : 0);
+		}
 		return 5000 + (chan*5);
 	} else {				/* either, guess */
+		/* XXX can't distinguish PSB+GSM channels */
 		if (chan == 14)
 			return 2484;
 		if (chan < 14)			/* 0-13 */
@@ -303,6 +415,97 @@
 }
 
 /*
+ * Locate a channel given a frequency+flags.  We cache
+ * the previous lookup to optimize swithing between two
+ * channels--as happens with dynamic turbo.
+ */
+struct ieee80211_channel *
+ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags)
+{
+	struct ieee80211_channel *c;
+	int i;
+
+	flags &= IEEE80211_CHAN_ALLTURBO;
+	c = ic->ic_prevchan;
+	if (c != NULL && c->ic_freq == freq &&
+	    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+		return c;
+	/* brute force search */
+	for (i = 0; i < ic->ic_nchans; i++) {
+		c = &ic->ic_channels[i];
+		if (c->ic_freq == freq &&
+		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+			return c;
+	}
+	return NULL;
+}
+
+/*
+ * Locate a channel given a channel number+flags.  We cache
+ * the previous lookup to optimize switching between two
+ * channels--as happens with dynamic turbo.
+ */
+struct ieee80211_channel *
+ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags)
+{
+	struct ieee80211_channel *c;
+	int i;
+
+	flags &= IEEE80211_CHAN_ALLTURBO;
+	c = ic->ic_prevchan;
+	if (c != NULL && c->ic_ieee == ieee &&
+	    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+		return c;
+	/* brute force search */
+	for (i = 0; i < ic->ic_nchans; i++) {
+		c = &ic->ic_channels[i];
+		if (c->ic_ieee == ieee &&
+		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+			return c;
+	}
+	return NULL;
+}
+
+static void
+addmedia(struct ieee80211com *ic, int mode, int mword)
+{
+#define	TURBO(m)	((m) | IFM_IEEE80211_TURBO)
+#define	ADD(_ic, _s, _o) \
+	ifmedia_add(&(_ic)->ic_media, \
+		IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
+	static const u_int mopts[IEEE80211_MODE_MAX] = { 
+		IFM_AUTO,			/* IEEE80211_MODE_AUTO */
+		IFM_IEEE80211_11A,		/* IEEE80211_MODE_11A */
+		IFM_IEEE80211_11B,		/* IEEE80211_MODE_11B */
+		IFM_IEEE80211_11G,		/* IEEE80211_MODE_11G */
+		IFM_IEEE80211_FH,		/* IEEE80211_MODE_FH */
+		TURBO(IFM_IEEE80211_11A),	/* IEEE80211_MODE_TURBO_A */
+		TURBO(IFM_IEEE80211_11G),	/* IEEE80211_MODE_TURBO_G */
+		TURBO(IFM_IEEE80211_11A),	/* IEEE80211_MODE_STURBO_A */
+		IFM_IEEE80211_11NA,		/* IEEE80211_MODE_11NA */
+		IFM_IEEE80211_11NG,		/* IEEE80211_MODE_11NG */
+	};
+	u_int mopt;
+
+	KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
+	mopt = mopts[mode];
+	KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO,
+	    ("no media mapping for mode %u", mode));
+
+	ADD(ic, mword, mopt);	/* e.g. 11a auto */
+	if (ic->ic_caps & IEEE80211_C_IBSS)
+		ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
+	if (ic->ic_caps & IEEE80211_C_HOSTAP)
+		ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
+	if (ic->ic_caps & IEEE80211_C_AHDEMO)
+		ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
+	if (ic->ic_caps & IEEE80211_C_MONITOR)
+		ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+#undef ADD
+#undef TURBO
+}
+
+/*
  * Setup the media data structures according to the channel and
  * rate tables.  This must be called by the driver after
  * ieee80211_attach and before most anything else.
@@ -311,49 +514,42 @@
 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 ifnet *ifp = ic->ic_ifp;
-	struct ifmediareq imr;
-	int i, j, mode, rate, maxrate, mword, mopt, r;
-	struct ieee80211_rateset *rs;
+	int i, j, mode, rate, maxrate, mword, r;
+	const struct ieee80211_rateset *rs;
 	struct ieee80211_rateset allrates;
 
-	/*
-	 * Do late attach work that must wait for any subclass
-	 * (i.e. driver) work such as overriding methods.
-	 */
-	ieee80211_node_lateattach(ic);
+	/* NB: this works because the structure is initialized to zero */
+	if (LIST_EMPTY(&ic->ic_media.ifm_list)) {
+		/*
+		 * Do late attach work that must wait for any subclass
+		 * (i.e. driver) work such as overriding methods.
+		 */
+		ieee80211_node_lateattach(ic);
+	} else {
+		/*
+		 * We are re-initializing the channel list; clear
+		 * the existing media state as the media routines
+		 * don't suppress duplicates.
+		 */
+		ifmedia_removeall(&ic->ic_media);
+		ieee80211_chan_init(ic);
+	}
+	ieee80211_power_lateattach(ic);
 
 	/*
 	 * Fill in media characteristics.
 	 */
 	ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
 	maxrate = 0;
+	/*
+	 * Add media for legacy operating modes.
+	 */
 	memset(&allrates, 0, sizeof(allrates));
-	for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
-		static const u_int mopts[] = { 
-			IFM_AUTO,
-			IFM_IEEE80211_11A,
-			IFM_IEEE80211_11B,
-			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)
+	for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
+		if (isclr(ic->ic_modecaps, mode))
 			continue;
-		mopt = mopts[mode];
-		ADD(ic, IFM_AUTO, mopt);	/* e.g. 11a auto */
-		if (ic->ic_caps & IEEE80211_C_IBSS)
-			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC);
-		if (ic->ic_caps & IEEE80211_C_HOSTAP)
-			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP);
-		if (ic->ic_caps & IEEE80211_C_AHDEMO)
-			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
-		if (ic->ic_caps & IEEE80211_C_MONITOR)
-			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
+		addmedia(ic, mode, IFM_AUTO);
 		if (mode == IEEE80211_MODE_AUTO)
 			continue;
 		rs = &ic->ic_sup_rates[mode];
@@ -362,17 +558,9 @@
 			mword = ieee80211_rate2media(ic, rate, mode);
 			if (mword == 0)
 				continue;
-			ADD(ic, mword, mopt);
-			if (ic->ic_caps & IEEE80211_C_IBSS)
-				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
-			if (ic->ic_caps & IEEE80211_C_HOSTAP)
-				ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
-			if (ic->ic_caps & IEEE80211_C_AHDEMO)
-				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
-			if (ic->ic_caps & IEEE80211_C_MONITOR)
-				ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+			addmedia(ic, mode, mword);
 			/*
-			 * Add rate to the collection of all rates.
+			 * Add legacy rate to the collection of all rates.
 			 */
 			r = rate & IEEE80211_RATE_VAL;
 			for (j = 0; j < allrates.rs_nrates; j++)
@@ -393,23 +581,52 @@
 				IEEE80211_MODE_AUTO);
 		if (mword == 0)
 			continue;
-		mword = IFM_SUBTYPE(mword);	/* remove media options */
-		ADD(ic, mword, 0);
-		if (ic->ic_caps & IEEE80211_C_IBSS)
-			ADD(ic, mword, IFM_IEEE80211_ADHOC);
-		if (ic->ic_caps & IEEE80211_C_HOSTAP)
-			ADD(ic, mword, IFM_IEEE80211_HOSTAP);
-		if (ic->ic_caps & IEEE80211_C_AHDEMO)
-			ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
-		if (ic->ic_caps & IEEE80211_C_MONITOR)
-			ADD(ic, mword, IFM_IEEE80211_MONITOR);
+		/* NB: remove media options from mword */
+		addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
+	}
+	/*
+	 * Add HT/11n media.  Note that we do not have enough
+	 * bits in the media subtype to express the MCS so we
+	 * use a "placeholder" media subtype and any fixed MCS
+	 * must be specified with a different mechanism.
+	 */
+	for (; mode < IEEE80211_MODE_MAX; mode++) {
+		if (isclr(ic->ic_modecaps, mode))
+			continue;
+		addmedia(ic, mode, IFM_AUTO);
+		addmedia(ic, mode, IFM_IEEE80211_MCS);
 	}
-	ieee80211_media_status(ifp, &imr);
-	ifmedia_set(&ic->ic_media, imr.ifm_active);
+	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
+	    isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
+		addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
+		/* XXX could walk htrates */
+		/* XXX known array size */
+		if (ieee80211_htrates[15] > maxrate)
+			maxrate = ieee80211_htrates[15];
+	}
+
+	/* NB: strip explicit mode; we're actually in autoselect */
+	ifmedia_set(&ic->ic_media,
+		media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK);
 
 	if (maxrate)
 		ifp->if_baudrate = IF_Mbps(maxrate);
-#undef ADD
+}
+
+const struct ieee80211_rateset *
+ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c)
+{
+	if (IEEE80211_IS_CHAN_HALF(c))
+		return &ieee80211_rateset_half;
+	if (IEEE80211_IS_CHAN_QUARTER(c))
+		return &ieee80211_rateset_quarter;
+	if (IEEE80211_IS_CHAN_HTA(c))
+		return &ic->ic_sup_rates[IEEE80211_MODE_11A];
+	if (IEEE80211_IS_CHAN_HTG(c)) {
+		/* XXX does this work for basic rates? */
+		return &ic->ic_sup_rates[IEEE80211_MODE_11G];
+	}
+	return &ic->ic_sup_rates[ieee80211_chan2mode(c)];
 }
 
 void
@@ -417,44 +634,78 @@
 {
 	struct ifnet *ifp = ic->ic_ifp;
 	int i, mode, rate, mword;
-	struct ieee80211_rateset *rs;
+	const struct ieee80211_rateset *rs;
 
-	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
-		if ((ic->ic_modecaps & (1<<mode)) == 0)
+	/* NB: skip AUTO since it has no rates */
+	for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) {
+		if (isclr(ic->ic_modecaps, mode))
 			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);
+			mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode);
 			if (mword == 0)
 				continue;
+			rate = ieee80211_media2rate(mword);
 			printf("%s%d%sMbps", (i != 0 ? " " : ""),
-			    (rate & IEEE80211_RATE_VAL) / 2,
-			    ((rate & 0x1) != 0 ? ".5" : ""));
+			    rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
 		}
 		printf("\n");
 	}
+	ieee80211_ht_announce(ic);
 }
 
-static int
-findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+void
+ieee80211_announce_channels(struct ieee80211com *ic)
 {
-#define	IEEERATE(_ic,_m,_i) \
-	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
-	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
-	for (i = 0; i < nrates; i++)
-		if (IEEERATE(ic, mode, i) == rate)
-			return i;
-	return -1;
-#undef IEEERATE
+	const struct ieee80211_channel *c;
+	char type;
+	int i, cw;
+
+	printf("Chan  Freq  CW  RegPwr  MinPwr  MaxPwr\n");
+	for (i = 0; i < ic->ic_nchans; i++) {
+		c = &ic->ic_channels[i];
+		if (IEEE80211_IS_CHAN_ST(c))
+			type = 'S';
+		else if (IEEE80211_IS_CHAN_108A(c))
+			type = 'T';
+		else if (IEEE80211_IS_CHAN_108G(c))
+			type = 'G';
+		else if (IEEE80211_IS_CHAN_HT(c))
+			type = 'n';
+		else if (IEEE80211_IS_CHAN_A(c))
+			type = 'a';
+		else if (IEEE80211_IS_CHAN_ANYG(c))
+			type = 'g';
+		else if (IEEE80211_IS_CHAN_B(c))
+			type = 'b';
+		else
+			type = 'f';
+		if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c))
+			cw = 40;
+		else if (IEEE80211_IS_CHAN_HALF(c))
+			cw = 10;
+		else if (IEEE80211_IS_CHAN_QUARTER(c))
+			cw = 5;
+		else
+			cw = 20;
+		printf("%4d  %4d%c %2d%c %6d  %4d.%d  %4d.%d\n"
+			, c->ic_ieee, c->ic_freq, type
+			, cw
+			, IEEE80211_IS_CHAN_HT40U(c) ? '+' :
+			  IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' '
+			, c->ic_maxregpower
+			, c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0
+			, c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0
+		);
+	}
 }
 
 /*
  * Find an instance by it's mac address.
  */
 struct ieee80211com *
-ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN])
+ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN])
 {
 	struct ieee80211com *ic;
 
@@ -478,6 +729,50 @@
 	return NULL;
 }
 
+static int
+findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+{
+#define	IEEERATE(_ic,_m,_i) \
+	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
+	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
+	for (i = 0; i < nrates; i++)
+		if (IEEERATE(ic, mode, i) == rate)
+			return i;
+	return -1;
+#undef IEEERATE
+}
+
+/*
+ * Convert a media specification to a rate index and possibly a mode
+ * (if the rate is fixed and the mode is specified as ``auto'' then
+ * we need to lock down the mode so the index is meanginful).
+ */
+static int
+checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+{
+
+	/*
+	 * Check the rate table for the specified/current phy.
+	 */
+	if (mode == IEEE80211_MODE_AUTO) {
+		int i;
+		/*
+		 * In autoselect mode search for the rate.
+		 */
+		for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
+			if (isset(ic->ic_modecaps, i) &&
+			    findrate(ic, i, rate) != -1)
+				return 1;
+		}
+		return 0;
+	} else {
+		/*
+		 * Mode is fixed, check for rate.
+		 */
+		return (findrate(ic, mode, rate) != -1);
+	}
+}
+
 /*
  * Handle a media change request.
  */
@@ -488,7 +783,7 @@
 	struct ifmedia_entry *ime;
 	enum ieee80211_opmode newopmode;
 	enum ieee80211_phymode newphymode;
-	int i, j, newrate, error = 0;
+	int newrate, error = 0;
 
 	ic = ieee80211_find_instance(ifp);
 	if (!ic) {
@@ -512,6 +807,12 @@
 	case IFM_IEEE80211_FH:
 		newphymode = IEEE80211_MODE_FH;
 		break;
+	case IFM_IEEE80211_11NA:
+		newphymode = IEEE80211_MODE_11NA;
+		break;
+	case IFM_IEEE80211_11NG:
+		newphymode = IEEE80211_MODE_11NG;
+		break;
 	case IFM_AUTO:
 		newphymode = IEEE80211_MODE_AUTO;
 		break;
@@ -523,55 +824,30 @@
 	 * XXX does not apply to AUTO
 	 */
 	if (ime->ifm_media & IFM_IEEE80211_TURBO) {
-		if (newphymode == IEEE80211_MODE_11A)
-			newphymode = IEEE80211_MODE_TURBO_A;
-		else if (newphymode == IEEE80211_MODE_11G)
+		if (newphymode == IEEE80211_MODE_11A) {
+			if (ic->ic_flags & IEEE80211_F_TURBOP)
+				newphymode = IEEE80211_MODE_TURBO_A;
+			else
+				newphymode = IEEE80211_MODE_STURBO_A;
+		} else if (newphymode == IEEE80211_MODE_11G)
 			newphymode = IEEE80211_MODE_TURBO_G;
 		else
 			return EINVAL;
 	}
-	/*
-	 * Validate requested mode is available.
-	 */
-	if ((ic->ic_modecaps & (1<<newphymode)) == 0)
-		return EINVAL;
-
+	/* XXX HT40 +/- */
 	/*
 	 * Next, the fixed/variable rate.
 	 */
-	i = -1;
+	newrate = ic->ic_fixed_rate;
 	if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
 		/*
 		 * Convert media subtype to rate.
 		 */
 		newrate = ieee80211_media2rate(ime->ifm_media);
-		if (newrate == 0)
+		if (newrate == 0 || !checkrate(ic, newphymode, newrate))
 			return EINVAL;
-		/*
-		 * Check the rate table for the specified/current phy.
-		 */
-		if (newphymode == IEEE80211_MODE_AUTO) {
-			/*
-			 * In autoselect mode search for the rate.
-			 */
-			for (j = IEEE80211_MODE_11A;
-			     j < IEEE80211_MODE_MAX; j++) {
-				if ((ic->ic_modecaps & (1<<j)) == 0)
-					continue;
-				i = findrate(ic, j, newrate);
-				if (i != -1) {
-					/* lock mode too */
-					newphymode = j;
-					break;
-				}
-			}
-		} else {
-			i = findrate(ic, newphymode, newrate);
-		}
-		if (i == -1)			/* mode/rate mismatch */
-			return EINVAL;
-	}
-	/* NB: defer rate setting to later */
+	} else
+		newrate = IEEE80211_FIXED_RATE_NONE;
 
 	/*
 	 * Deduce new operating mode but don't install it just yet.
@@ -589,35 +865,18 @@
 		newopmode = IEEE80211_M_STA;
 
 	/*
-	 * Autoselect doesn't make sense when operating as an AP.
-	 * If no phy mode has been selected, pick one and lock it
-	 * down so rate tables can be used in forming beacon frames
-	 * and the like.
-	 */
-	if (newopmode == IEEE80211_M_HOSTAP &&
-	    newphymode == IEEE80211_MODE_AUTO) {
-		for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++)
-			if (ic->ic_modecaps & (1<<j)) {
-				newphymode = j;
-				break;
-			}
-	}
-
-	/*
 	 * Handle phy mode change.
 	 */
-	if (ic->ic_curmode != newphymode) {		/* change phy mode */
-		error = ieee80211_setmode(ic, newphymode);
-		if (error != 0)
-			return error;
+	if (ic->ic_des_mode != newphymode) {		/* change phy mode */
+		ic->ic_des_mode = newphymode;
 		error = ENETRESET;
 	}
 
 	/*
 	 * Committed to changes, install the rate setting.
 	 */
-	if (ic->ic_fixed_rate != i) {
-		ic->ic_fixed_rate = i;			/* set fixed tx rate */
+	if (ic->ic_fixed_rate != newrate) {
+		ic->ic_fixed_rate = newrate;		/* set fixed tx rate */
 		error = ENETRESET;
 	}
 
@@ -631,6 +890,7 @@
 		case IEEE80211_M_HOSTAP:
 		case IEEE80211_M_STA:
 		case IEEE80211_M_MONITOR:
+		case IEEE80211_M_WDS:
 			ic->ic_flags &= ~IEEE80211_F_IBSSON;
 			break;
 		case IEEE80211_M_IBSS:
@@ -653,11 +913,62 @@
 	return error;
 }
 
+/*
+ * Common code to calculate the media status word
+ * from the operating mode and channel state.
+ */
+static int
+media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
+{
+	int status;
+
+	status = IFM_IEEE80211;
+	switch (opmode) {
+	case IEEE80211_M_STA:
+		break;
+	case IEEE80211_M_IBSS:
+		status |= IFM_IEEE80211_ADHOC;
+		break;
+	case IEEE80211_M_HOSTAP:
+		status |= IFM_IEEE80211_HOSTAP;
+		break;
+	case IEEE80211_M_MONITOR:
+		status |= IFM_IEEE80211_MONITOR;
+		break;
+	case IEEE80211_M_AHDEMO:
+		status |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
+		break;
+	case IEEE80211_M_WDS:
+		/* should not come here */
+		break;
+	}
+	if (IEEE80211_IS_CHAN_HTA(chan)) {
+		status |= IFM_IEEE80211_11NA;
+	} else if (IEEE80211_IS_CHAN_HTG(chan)) {
+		status |= IFM_IEEE80211_11NG;
+	} else if (IEEE80211_IS_CHAN_A(chan)) {
+		status |= IFM_IEEE80211_11A;
+	} else if (IEEE80211_IS_CHAN_B(chan)) {
+		status |= IFM_IEEE80211_11B;
+	} else if (IEEE80211_IS_CHAN_ANYG(chan)) {
+		status |= IFM_IEEE80211_11G;
+	} else if (IEEE80211_IS_CHAN_FHSS(chan)) {
+		status |= IFM_IEEE80211_FH;
+	}
+	/* XXX else complain? */
+
+	if (IEEE80211_IS_CHAN_TURBO(chan))
+		status |= IFM_IEEE80211_TURBO;
+
+	return status;
+}
+
 void
 ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
 {
 	struct ieee80211com *ic;
-	struct ieee80211_rateset *rs;
+	enum ieee80211_phymode mode;
+	const struct ieee80211_rateset *rs;
 
 	ic = ieee80211_find_instance(ifp);
 	if (!ic) {
@@ -665,9 +976,17 @@
 		return;
 	}
 	imr->ifm_status = IFM_AVALID;
-	imr->ifm_active = IFM_IEEE80211;
-	if (ic->ic_state == IEEE80211_S_RUN)
+	/*
+	 * NB: use the current channel's mode to lock down a xmit
+	 * rate only when running; otherwise we may have a mismatch
+	 * in which case the rate will not be convertible.
+	 */
+	if (ic->ic_state == IEEE80211_S_RUN) {
 		imr->ifm_status |= IFM_ACTIVE;
+		mode = ieee80211_chan2mode(ic->ic_curchan);
+	} else
+		mode = IEEE80211_MODE_AUTO;
+	imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
 	/*
 	 * Calculate a current rate if possible.
 	 */
@@ -675,82 +994,18 @@
 		/*
 		 * 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);
+			ic->ic_fixed_rate, mode);
 	} else if (ic->ic_opmode == IEEE80211_M_STA) {
 		/*
 		 * In station mode report the current transmit rate.
+		 * XXX HT rate
 		 */
 		rs = &ic->ic_bss->ni_rates;
 		imr->ifm_active |= ieee80211_rate2media(ic,
-			rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode);
+			rs->rs_rates[ic->ic_bss->ni_txrate], mode);
 	} else
 		imr->ifm_active |= IFM_AUTO;
-	switch (ic->ic_opmode) {
-	case IEEE80211_M_STA:
-		break;
-	case IEEE80211_M_IBSS:
-		imr->ifm_active |= IFM_IEEE80211_ADHOC;
-		break;
-	case IEEE80211_M_AHDEMO:
-		/* should not come here */
-		break;
-	case IEEE80211_M_HOSTAP:
-		imr->ifm_active |= IFM_IEEE80211_HOSTAP;
-		break;
-	case IEEE80211_M_MONITOR:
-		imr->ifm_active |= IFM_IEEE80211_MONITOR;
-		break;
-	}
-	switch (ic->ic_curmode) {
-	case IEEE80211_MODE_11A:
-		imr->ifm_active |= IFM_IEEE80211_11A;
-		break;
-	case IEEE80211_MODE_11B:
-		imr->ifm_active |= IFM_IEEE80211_11B;
-		break;
-	case IEEE80211_MODE_11G:
-		imr->ifm_active |= IFM_IEEE80211_11G;
-		break;
-	case IEEE80211_MODE_FH:
-		imr->ifm_active |= IFM_IEEE80211_FH;
-		break;
-	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;
-	}
-}
-
-void
-ieee80211_watchdog(struct ieee80211com *ic)
-{
-	struct ieee80211_node_table *nt;
-	int need_inact_timer = 0;
-
-	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;
 }
 
 /*
@@ -762,170 +1017,80 @@
 int
 ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
-	static const u_int chanflags[] = {
-		0,			/* IEEE80211_MODE_AUTO */
-		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
-		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
-		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
-		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
-		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO_A */
-		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
-	};
-	struct ieee80211_channel *c;
-	u_int modeflags;
-	int i;
-
-	/* validate new mode */
-	if ((ic->ic_modecaps & (1<<mode)) == 0) {
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
-			"%s: mode %u not supported (caps 0x%x)\n",
-			__func__, mode, ic->ic_modecaps);
-		return EINVAL;
-	}
-
 	/*
-	 * Verify at least one channel is present in the available
-	 * channel list before committing to the new mode.
+	 * Adjust basic rates in 11b/11g supported rate set.
+	 * Note that if operating on a hal/quarter rate channel
+	 * this is a noop as those rates sets are different
+	 * and used instead.
 	 */
-	KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
-	modeflags = chanflags[mode];
-	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
-		c = &ic->ic_channels[i];
-		if (c->ic_flags == 0)
-			continue;
-		if (mode == IEEE80211_MODE_AUTO) {
-			/* ignore static turbo channels for autoselect */
-			if (!IEEE80211_IS_CHAN_T(c))
-				break;
-		} else {
-			if ((c->ic_flags & modeflags) == modeflags)
-				break;
-		}
-	}
-	if (i > IEEE80211_CHAN_MAX) {
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
-			"%s: no channels found for mode %u\n", __func__, mode);
-		return EINVAL;
-	}
-
-	/*
-	 * Calculate the active channel set.
-	 */
-	memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active));
-	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
-		c = &ic->ic_channels[i];
-		if (c->ic_flags == 0)
-			continue;
-		if (mode == IEEE80211_MODE_AUTO) {
-			/* take anything but static turbo channels */
-			if (!IEEE80211_IS_CHAN_T(c))
-				setbit(ic->ic_chan_active, i);
-		} else {
-			if ((c->ic_flags & modeflags) == modeflags)
-				setbit(ic->ic_chan_active, i);
-		}
-	}
-	/*
-	 * If no current/default channel is setup or the current
-	 * channel is wrong for the mode then pick the first
-	 * available channel from the active list.  This is likely
-	 * not the right one.
-	 */
-	if (ic->ic_ibss_chan == NULL ||
-	    isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
-		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
-			if (isset(ic->ic_chan_active, i)) {
-				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;
-
-	/*
-	 * Do mode-specific rate setup.
-	 */
-	if (mode == IEEE80211_MODE_11G) {
-		/*
-		 * Use a mixed 11b/11g rate set.
-		 */
-		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
-			IEEE80211_MODE_11G);
-	} 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];
+	if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
+		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 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 for channels
- * where multiple operating modes are possible (e.g. 11g+11b).
- * In those cases we defer to the current operating mode when set.
+ * Return the phy mode for with the specified channel.
  */
 enum ieee80211_phymode
-ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
+ieee80211_chan2mode(const struct ieee80211_channel *chan)
 {
-	if (IEEE80211_IS_CHAN_T(chan)) {
+
+	if (IEEE80211_IS_CHAN_HTA(chan))
+		return IEEE80211_MODE_11NA;
+	else if (IEEE80211_IS_CHAN_HTG(chan))
+		return IEEE80211_MODE_11NG;
+	else if (IEEE80211_IS_CHAN_108G(chan))
+		return IEEE80211_MODE_TURBO_G;
+	else if (IEEE80211_IS_CHAN_ST(chan))
+		return IEEE80211_MODE_STURBO_A;
+	else if (IEEE80211_IS_CHAN_TURBO(chan))
 		return IEEE80211_MODE_TURBO_A;
-	} else if (IEEE80211_IS_CHAN_5GHZ(chan)) {
+	else if (IEEE80211_IS_CHAN_A(chan))
 		return IEEE80211_MODE_11A;
-	} else if (IEEE80211_IS_CHAN_FHSS(chan))
-		return IEEE80211_MODE_FH;
-	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;
+	else if (IEEE80211_IS_CHAN_ANYG(chan))
 		return IEEE80211_MODE_11G;
-	} else
+	else if (IEEE80211_IS_CHAN_B(chan))
 		return IEEE80211_MODE_11B;
+	else if (IEEE80211_IS_CHAN_FHSS(chan))
+		return IEEE80211_MODE_FH;
+
+	/* NB: should not get here */
+	printf("%s: cannot map channel to mode; freq %u flags 0x%x\n",
+		__func__, chan->ic_freq, chan->ic_flags);
+	return IEEE80211_MODE_11B;
+}
+
+struct ratemedia {
+	u_int	match;	/* rate + mode */
+	u_int	media;	/* if_media rate */
+};
+
+static int
+findmedia(const struct ratemedia rates[], int n, u_int match)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		if (rates[i].match == match)
+			return rates[i].media;
+	return IFM_AUTO;
 }
 
 /*
- * convert IEEE80211 rate value to ifmedia subtype.
- * ieee80211 rate is in unit of 0.5Mbps.
+ * Convert IEEE80211 rate value to ifmedia subtype.
+ * Rate is either a legacy rate in units of 0.5Mbps
+ * or an MCS index.
  */
 int
 ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
 {
 #define	N(a)	(sizeof(a) / sizeof(a[0]))
-	static const struct {
-		u_int	m;	/* rate + mode */
-		u_int	r;	/* if_media rate */
-	} rates[] = {
+	static const struct ratemedia rates[] = {
 		{   2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
 		{   4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
 		{   2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 },
@@ -953,38 +1118,73 @@
 		{  72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 },
 		{  96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 },
 		{ 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 },
+		{   6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 },
+		{   9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 },
+		{  54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 },
 		/* NB: OFDM72 doesn't realy exist so we don't handle it */
 	};
-	u_int mask, i;
+	static const struct ratemedia htrates[] = {
+		{   0, IFM_IEEE80211_MCS },
+		{   1, IFM_IEEE80211_MCS },
+		{   2, IFM_IEEE80211_MCS },
+		{   3, IFM_IEEE80211_MCS },
+		{   4, IFM_IEEE80211_MCS },
+		{   5, IFM_IEEE80211_MCS },
+		{   6, IFM_IEEE80211_MCS },
+		{   7, IFM_IEEE80211_MCS },
+		{   8, IFM_IEEE80211_MCS },
+		{   9, IFM_IEEE80211_MCS },
+		{  10, IFM_IEEE80211_MCS },
+		{  11, IFM_IEEE80211_MCS },
+		{  12, IFM_IEEE80211_MCS },
+		{  13, IFM_IEEE80211_MCS },
+		{  14, IFM_IEEE80211_MCS },
+		{  15, IFM_IEEE80211_MCS },
+	};
+	int m;
 
-	mask = rate & IEEE80211_RATE_VAL;
+	/*
+	 * Check 11n rates first for match as an MCS.
+	 */
+	if (mode == IEEE80211_MODE_11NA) {
+		if (rate & IEEE80211_RATE_MCS) {
+			rate &= ~IEEE80211_RATE_MCS;
+			m = findmedia(htrates, N(htrates), rate);
+			if (m != IFM_AUTO)
+				return m | IFM_IEEE80211_11NA;
+		}
+	} else if (mode == IEEE80211_MODE_11NG) {
+		/* NB: 12 is ambiguous, it will be treated as an MCS */
+		if (rate & IEEE80211_RATE_MCS) {
+			rate &= ~IEEE80211_RATE_MCS;
+			m = findmedia(htrates, N(htrates), rate);
+			if (m != IFM_AUTO)
+				return m | IFM_IEEE80211_11NG;
+		}
+	}
+	rate &= IEEE80211_RATE_VAL;
 	switch (mode) {
 	case IEEE80211_MODE_11A:
+	case IEEE80211_MODE_11NA:
 	case IEEE80211_MODE_TURBO_A:
-		mask |= IFM_IEEE80211_11A;
-		break;
+	case IEEE80211_MODE_STURBO_A:
+		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A);
 	case IEEE80211_MODE_11B:
-		mask |= IFM_IEEE80211_11B;
-		break;
+		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B);
 	case IEEE80211_MODE_FH:
-		mask |= IFM_IEEE80211_FH;
-		break;
+		return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH);
 	case IEEE80211_MODE_AUTO:
 		/* NB: ic may be NULL for some drivers */
-		if (ic && ic->ic_phytype == IEEE80211_T_FH) {
-			mask |= IFM_IEEE80211_FH;
-			break;
-		}
+		if (ic && ic->ic_phytype == IEEE80211_T_FH)
+			return findmedia(rates, N(rates),
+			    rate | IFM_IEEE80211_FH);
 		/* NB: hack, 11g matches both 11b+11a rates */
 		/* fall thru... */
 	case IEEE80211_MODE_11G:
+	case IEEE80211_MODE_11NG:
 	case IEEE80211_MODE_TURBO_G:
-		mask |= IFM_IEEE80211_11G;
-		break;
+		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G);
 	}
-	for (i = 0; i < N(rates); i++)
-		if (rates[i].m == mask)
-			return rates[i].r;
 	return IFM_AUTO;
 #undef N
 }
@@ -1013,6 +1213,12 @@
 		96,		/* IFM_IEEE80211_OFDM48 */
 		108,		/* IFM_IEEE80211_OFDM54 */
 		144,		/* IFM_IEEE80211_OFDM72 */
+		0,		/* IFM_IEEE80211_DS354k */
+		0,		/* IFM_IEEE80211_DS512k */
+		6,		/* IFM_IEEE80211_OFDM3 */
+		9,		/* IFM_IEEE80211_OFDM4 */
+		54,		/* IFM_IEEE80211_OFDM27 */
+		-1,		/* IFM_IEEE80211_MCS */
 	};
 	return IFM_SUBTYPE(mword) < N(ieeerates) ?
 		ieeerates[IFM_SUBTYPE(mword)] : 0;
Index: ieee80211_crypto_tkip.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_tkip.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_tkip.c -L sys/net80211/ieee80211_crypto_tkip.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_crypto_tkip.c
+++ sys/net80211/ieee80211_crypto_tkip.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -30,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_tkip.c,v 1.9.2.2 2005/12/22 19:02:08 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_tkip.c,v 1.13 2007/06/11 03:36:54 sam Exp $");
 
 /*
  * IEEE 802.11i TKIP crypto support.
@@ -58,7 +52,7 @@
 static	void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
 static	void tkip_detach(struct ieee80211_key *);
 static	int tkip_setkey(struct ieee80211_key *);
-static	int tkip_encap(struct ieee80211_key *, struct mbuf *m, u_int8_t keyid);
+static	int tkip_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid);
 static	int tkip_enmic(struct ieee80211_key *, struct mbuf *, int);
 static	int tkip_decap(struct ieee80211_key *, struct mbuf *, int);
 static	int tkip_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -156,11 +150,11 @@
  * Add privacy headers and do any s/w encryption required.
  */
 static int
-tkip_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
 {
 	struct tkip_ctx *ctx = k->wk_private;
 	struct ieee80211com *ic = ctx->tc_ic;
-	u_int8_t *ivp;
+	uint8_t *ivp;
 	int hdrlen;
 
 	/*
@@ -185,7 +179,7 @@
 	M_PREPEND(m, tkip.ic_header, M_NOWAIT);
 	if (m == NULL)
 		return 0;
-	ivp = mtod(m, u_int8_t *);
+	ivp = mtod(m, uint8_t *);
 	memmove(ivp, ivp + tkip.ic_header, hdrlen);
 	ivp += hdrlen;
 
@@ -976,32 +970,4 @@
 /*
  * Module glue.
  */
-static int
-tkip_modevent(module_t mod, int type, void *unused)
-{
-	switch (type) {
-	case MOD_LOAD:
-		ieee80211_crypto_register(&tkip);
-		return 0;
-	case MOD_UNLOAD:
-	case MOD_QUIESCE:
-		if (nrefs) {
-			printf("wlan_tkip: still in use (%u dynamic refs)\n",
-				nrefs);
-			return EBUSY;
-		}
-		if (type == MOD_UNLOAD)
-			ieee80211_crypto_unregister(&tkip);
-		return 0;
-	}
-	return EINVAL;
-}
-
-static moduledata_t tkip_mod = {
-	"wlan_tkip",
-	tkip_modevent,
-	0
-};
-DECLARE_MODULE(wlan_tkip, tkip_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_tkip, 1);
-MODULE_DEPEND(wlan_tkip, wlan, 1, 1, 1);
+IEEE80211_CRYPTO_MODULE(tkip, 1);
--- /dev/null
+++ sys/net80211/ieee80211_regdomain.c
@@ -0,0 +1,339 @@
+/*-
+ * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_regdomain.c,v 1.3 2007/09/17 03:48:32 sam Exp $");
+
+/*
+ * IEEE 802.11 regdomain support.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/kernel.h>
+ 
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+
+void
+ieee80211_regdomain_attach(struct ieee80211com *ic)
+{
+	ic->ic_regdomain = 0;			/* XXX */
+	ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */
+	ic->ic_location = 1+2;			/* both */
+}
+
+void
+ieee80211_regdomain_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+addchan(struct ieee80211com *ic, int ieee, int flags)
+{
+	struct ieee80211_channel *c;
+
+	c = &ic->ic_channels[ic->ic_nchans++];
+	c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
+	c->ic_ieee = ieee;
+	c->ic_flags = flags;
+}
+
+/*
+ * Setup the channel list for the specified regulatory domain,
+ * country code, and operating modes.  This interface is used
+ * when a driver does not obtain the channel list from another
+ * source (such as firmware).
+ */
+void
+ieee80211_init_channels(struct ieee80211com *ic,
+	int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm)
+{
+	int i;
+
+	/* XXX just do something for now */
+	ic->ic_nchans = 0;
+	if (isset(&bands, IEEE80211_MODE_11B) ||
+	    isset(&bands, IEEE80211_MODE_11G)) {
+		for (i = 1; i <= (ecm ? 14 : 11); i++) {
+			if (isset(&bands, IEEE80211_MODE_11B))
+				addchan(ic, i, IEEE80211_CHAN_B);
+			if (isset(&bands, IEEE80211_MODE_11G))
+				addchan(ic, i, IEEE80211_CHAN_G);
+		}
+	}
+	if (isset(&bands, IEEE80211_MODE_11A)) {
+		for (i = 36; i <= 64; i += 4)
+			addchan(ic, i, IEEE80211_CHAN_A);
+		for (i = 100; i <= 140; i += 4)
+			addchan(ic, i, IEEE80211_CHAN_A);
+		for (i = 149; i <= 161; i += 4)
+			addchan(ic, i, IEEE80211_CHAN_A);
+	}
+	ic->ic_regdomain = rd;
+	ic->ic_countrycode = cc;
+	ic->ic_location = outdoor;
+}
+
+/*
+ * Add Country Information IE.
+ */
+uint8_t *
+ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
+	enum ISOCountryCode cc, int location)
+{
+#define	CHAN_UNINTERESTING \
+    (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
+     IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)
+	/* XXX what about auto? */
+	/* flag set of channels to be excluded */
+	static const int skipflags[IEEE80211_MODE_MAX] = {
+	    CHAN_UNINTERESTING,				/* MODE_AUTO */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_11A */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11B */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11G */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_OFDM |	/* MODE_FH */
+	        IEEE80211_CHAN_CCK | IEEE80211_CHAN_DYN,
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_TURBO_A */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_TURBO_G */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_STURBO_A */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_11NA */
+	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11NG */
+	};
+	struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm;
+	const char *iso_name;
+	uint8_t nextchan, chans[IEEE80211_CHAN_BYTES];
+	int i, skip;
+
+	ie->ie = IEEE80211_ELEMID_COUNTRY;
+	iso_name = ieee80211_cctoiso(cc);
+	if (iso_name == NULL) {
+		if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc);
+		iso_name = "  ";
+	}
+	ie->cc[0] = iso_name[0];
+	ie->cc[1] = iso_name[1];
+	/* 
+	 * Indoor/Outdoor portion of country string.
+	 * NB: this is not quite right, since we should have one of:
+	 *     'I' indoor only
+	 *     'O' outdoor only
+	 *     ' ' all enviroments
+	 */
+	ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O');
+
+	/* 
+	 * Run-length encoded channel+max tx power info.
+	 */
+	frm = (uint8_t *)&ie->band[0];
+	nextchan = 0;			/* NB: impossible channel # */
+	memset(chans, 0, sizeof(chans));
+	skip = skipflags[ic->ic_curmode];
+	for (i = 0; i < ic->ic_nchans; i++) {
+		const struct ieee80211_channel *c = &ic->ic_channels[i];
+
+		if (isset(chans, c->ic_ieee))		/* suppress dup's */
+			continue;
+		if (c->ic_flags & skip)			/* skip band, etc. */
+			continue;
+		setbit(chans, c->ic_ieee);
+		if (c->ic_ieee != nextchan ||
+		    c->ic_maxregpower != frm[-1]) {	/* new run */
+			/* XXX max of 83 runs */
+			frm[0] = c->ic_ieee;		/* starting channel # */
+			frm[1] = 1;			/* # channels in run */
+			frm[2] = c->ic_maxregpower;	/* tx power cap */
+			frm += 3;
+			nextchan = c->ic_ieee + 1;	/* overflow? */
+		} else {				/* extend run */
+			frm[-2]++;
+			nextchan++;
+		}
+	}
+	ie->len = frm - ie->cc;
+	if (ie->len & 1) {		/* Zero pad to multiple of 2 */
+		ie->len++;
+		*frm++ = 0;
+	}
+	return frm;
+#undef CHAN_UNINTERESTING
+}
+
+/*
+ * Country Code Table for code-to-string conversion.
+ */
+static const struct {
+	enum ISOCountryCode iso_code;	   
+	const char*	iso_name;
+} country_strings[] = {
+    { CTRY_DEBUG,	 	"DB" },		/* NB: nonstandard */
+    { CTRY_DEFAULT,	 	"NA" },		/* NB: nonstandard */
+    { CTRY_ALBANIA,		"AL" },
+    { CTRY_ALGERIA,		"DZ" },
+    { CTRY_ARGENTINA,		"AR" },
+    { CTRY_ARMENIA,		"AM" },
+    { CTRY_AUSTRALIA,		"AU" },
+    { CTRY_AUSTRIA,		"AT" },
+    { CTRY_AZERBAIJAN,		"AZ" },
+    { CTRY_BAHRAIN,		"BH" },
+    { CTRY_BELARUS,		"BY" },
+    { CTRY_BELGIUM,		"BE" },
+    { CTRY_BELIZE,		"BZ" },
+    { CTRY_BOLIVIA,		"BO" },
+    { CTRY_BRAZIL,		"BR" },
+    { CTRY_BRUNEI_DARUSSALAM,	"BN" },
+    { CTRY_BULGARIA,		"BG" },
+    { CTRY_CANADA,		"CA" },
+    { CTRY_CHILE,		"CL" },
+    { CTRY_CHINA,		"CN" },
+    { CTRY_COLOMBIA,		"CO" },
+    { CTRY_COSTA_RICA,		"CR" },
+    { CTRY_CROATIA,		"HR" },
+    { CTRY_CYPRUS,		"CY" },
+    { CTRY_CZECH,		"CZ" },
+    { CTRY_DENMARK,		"DK" },
+    { CTRY_DOMINICAN_REPUBLIC,	"DO" },
+    { CTRY_ECUADOR,		"EC" },
+    { CTRY_EGYPT,		"EG" },
+    { CTRY_EL_SALVADOR,		"SV" },    
+    { CTRY_ESTONIA,		"EE" },
+    { CTRY_FINLAND,		"FI" },
+    { CTRY_FRANCE,		"FR" },
+    { CTRY_FRANCE2,		"F2" },
+    { CTRY_GEORGIA,		"GE" },
+    { CTRY_GERMANY,		"DE" },
+    { CTRY_GREECE,		"GR" },
+    { CTRY_GUATEMALA,		"GT" },
+    { CTRY_HONDURAS,		"HN" },
+    { CTRY_HONG_KONG,		"HK" },
+    { CTRY_HUNGARY,		"HU" },
+    { CTRY_ICELAND,		"IS" },
+    { CTRY_INDIA,		"IN" },
+    { CTRY_INDONESIA,		"ID" },
+    { CTRY_IRAN,		"IR" },
+    { CTRY_IRELAND,		"IE" },
+    { CTRY_ISRAEL,		"IL" },
+    { CTRY_ITALY,		"IT" },
+    { CTRY_JAMAICA,		"JM" },
+    { CTRY_JAPAN,		"JP" },
+    { CTRY_JAPAN1,		"J1" },
+    { CTRY_JAPAN2,		"J2" },    
+    { CTRY_JAPAN3,		"J3" },
+    { CTRY_JAPAN4,		"J4" },
+    { CTRY_JAPAN5,		"J5" },    
+    { CTRY_JORDAN,		"JO" },
+    { CTRY_KAZAKHSTAN,		"KZ" },
+    { CTRY_KOREA_NORTH,		"KP" },
+    { CTRY_KOREA_ROC,		"KR" },
+    { CTRY_KOREA_ROC2,		"K2" },
+    { CTRY_KUWAIT,		"KW" },
+    { CTRY_LATVIA,		"LV" },
+    { CTRY_LEBANON,		"LB" },
+    { CTRY_LIECHTENSTEIN,	"LI" },
+    { CTRY_LITHUANIA,		"LT" },
+    { CTRY_LUXEMBOURG,		"LU" },
+    { CTRY_MACAU,		"MO" },
+    { CTRY_MACEDONIA,		"MK" },
+    { CTRY_MALAYSIA,		"MY" },
+    { CTRY_MEXICO,		"MX" },
+    { CTRY_MONACO,		"MC" },
+    { CTRY_MOROCCO,		"MA" },
+    { CTRY_NETHERLANDS,		"NL" },
+    { CTRY_NEW_ZEALAND,		"NZ" },
+    { CTRY_NORWAY,		"NO" },
+    { CTRY_OMAN,		"OM" },
+    { CTRY_PAKISTAN,		"PK" },
+    { CTRY_PANAMA,		"PA" },
+    { CTRY_PERU,		"PE" },
+    { CTRY_PHILIPPINES,		"PH" },
+    { CTRY_POLAND,		"PL" },
+    { CTRY_PORTUGAL,		"PT" },
+    { CTRY_PUERTO_RICO,		"PR" },
+    { CTRY_QATAR,		"QA" },
+    { CTRY_ROMANIA,		"RO" },
+    { CTRY_RUSSIA,		"RU" },
+    { CTRY_SAUDI_ARABIA,	"SA" },
+    { CTRY_SINGAPORE,		"SG" },
+    { CTRY_SLOVAKIA,		"SK" },
+    { CTRY_SLOVENIA,		"SI" },
+    { CTRY_SOUTH_AFRICA,	"ZA" },
+    { CTRY_SPAIN,		"ES" },
+    { CTRY_SWEDEN,		"SE" },
+    { CTRY_SWITZERLAND,		"CH" },
+    { CTRY_SYRIA,		"SY" },
+    { CTRY_TAIWAN,		"TW" },
+    { CTRY_THAILAND,		"TH" },
+    { CTRY_TRINIDAD_Y_TOBAGO,	"TT" },
+    { CTRY_TUNISIA,		"TN" },
+    { CTRY_TURKEY,		"TR" },
+    { CTRY_UKRAINE,		"UA" },
+    { CTRY_UAE,			"AE" },
+    { CTRY_UNITED_KINGDOM,	"GB" },
+    { CTRY_UNITED_STATES,	"US" },
+    { CTRY_URUGUAY,		"UY" },
+    { CTRY_UZBEKISTAN,		"UZ" },    
+    { CTRY_VENEZUELA,		"VE" },
+    { CTRY_VIET_NAM,		"VN" },
+    { CTRY_YEMEN,		"YE" },
+    { CTRY_ZIMBABWE,		"ZW" }    
+};
+
+const char *
+ieee80211_cctoiso(enum ISOCountryCode cc)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(country_strings); i++) {
+		if (country_strings[i].iso_code == cc)
+			return country_strings[i].iso_name;
+	}
+	return NULL;
+#undef N
+}
+
+int
+ieee80211_isotocc(const char iso[2])
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(country_strings); i++) {
+		if (country_strings[i].iso_name[0] == iso[0] &&
+		    country_strings[i].iso_name[1] == iso[1])
+			return country_strings[i].iso_code;
+	}
+	return -1;
+#undef N
+}
--- /dev/null
+++ sys/net80211/ieee80211_ht.h
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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_ht.h,v 1.2.2.1 2007/11/11 17:44:35 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_HT_H_
+#define _NET80211_IEEE80211_HT_H_
+
+/*
+ * 802.11n protocol implementation definitions.
+ */
+
+#define	IEEE80211_AGGR_BAWMAX	64	/* max block ack window size */
+/* threshold for aging overlapping non-HT bss */
+#define	IEEE80211_NONHT_PRESENT_AGE	msecs_to_ticks(60*1000)
+
+typedef uint16_t ieee80211_seq;
+
+struct ieee80211_tx_ampdu {
+	u_short		txa_flags;
+#define	IEEE80211_AGGR_IMMEDIATE	0x0001	/* BA policy */
+#define	IEEE80211_AGGR_XCHGPEND		0x0002	/* ADDBA response pending */
+#define	IEEE80211_AGGR_RUNNING		0x0004	/* ADDBA response received */
+#define	IEEE80211_AGGR_SETUP		0x0008	/* deferred state setup */
+#define	IEEE80211_AGGR_NAK		0x0010	/* peer NAK'd ADDBA request */
+	uint8_t		txa_ac;
+	uint8_t		txa_token;	/* dialog token */
+	int		txa_qbytes;	/* data queued (bytes) */
+	short		txa_qframes;	/* data queued (frames) */
+	ieee80211_seq	txa_seqstart;
+	ieee80211_seq	txa_start;
+	uint16_t	txa_wnd;	/* BA window size */
+	uint8_t		txa_attempts;	/* # setup attempts */
+	int		txa_lastrequest;/* time of last ADDBA request */
+	struct ifqueue	txa_q;		/* packet queue */
+	struct callout	txa_timer;
+};
+
+/* return non-zero if AMPDU tx for the TID is running */
+#define	IEEE80211_AMPDU_RUNNING(tap) \
+	(((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0)
+
+/* return non-zero if AMPDU tx for the TID is running or started */
+#define	IEEE80211_AMPDU_REQUESTED(tap) \
+	(((tap)->txa_flags & \
+	 (IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0)
+
+struct ieee80211_rx_ampdu {
+	int		rxa_flags;
+	int		rxa_qbytes;	/* data queued (bytes) */
+	short		rxa_qframes;	/* data queued (frames) */
+	ieee80211_seq	rxa_seqstart;
+	ieee80211_seq	rxa_start;	/* start of current BA window */
+	uint16_t	rxa_wnd;	/* BA window size */
+	int		rxa_age;	/* age of oldest frame in window */
+	int		rxa_nframes;	/* frames since ADDBA */
+	struct mbuf *rxa_m[IEEE80211_AGGR_BAWMAX];
+};
+
+void	ieee80211_ht_attach(struct ieee80211com *);
+void	ieee80211_ht_detach(struct ieee80211com *);
+
+void	ieee80211_ht_announce(struct ieee80211com *);
+
+extern const int ieee80211_htrates[16];
+const struct ieee80211_htrateset *ieee80211_get_suphtrates(
+		struct ieee80211com *, const struct ieee80211_channel *);
+
+struct ieee80211_node;
+int	ieee80211_setup_htrates(struct ieee80211_node *,
+		const uint8_t *htcap, int flags);
+void	ieee80211_setup_basic_htrates(struct ieee80211_node *,
+		const uint8_t *htinfo);
+struct mbuf *ieee80211_decap_amsdu(struct ieee80211_node *, struct mbuf *);
+int	ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *);
+void	ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *);
+void	ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *);
+void	ieee80211_ht_node_cleanup(struct ieee80211_node *);
+struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
+		struct ieee80211_channel *, int);
+void	ieee80211_ht_wds_init(struct ieee80211_node *);
+void	ieee80211_ht_node_join(struct ieee80211_node *);
+void	ieee80211_ht_node_leave(struct ieee80211_node *);
+void	ieee80211_htinfo_update(struct ieee80211com *, int protmode);
+void	ieee80211_ht_timeout(struct ieee80211com *);
+void	ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
+void	ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
+void	ieee80211_recv_action(struct ieee80211_node *,
+		const uint8_t *, const uint8_t *);
+int	ieee80211_ampdu_request(struct ieee80211_node *,
+		struct ieee80211_tx_ampdu *);
+void	ieee80211_ampdu_stop(struct ieee80211_node *,
+		struct ieee80211_tx_ampdu *);
+int	ieee80211_send_bar(struct ieee80211_node *,
+		const struct ieee80211_tx_ampdu *);
+int	ieee80211_send_action(struct ieee80211_node *,
+		int, int, uint16_t [4]);
+uint8_t	*ieee80211_add_htcap(uint8_t *, struct ieee80211_node *);
+uint8_t	*ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *);
+uint8_t	*ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *);
+uint8_t	*ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *);
+struct ieee80211_beacon_offsets;
+void	ieee80211_ht_update_beacon(struct ieee80211com *,
+		struct ieee80211_beacon_offsets *);
+#endif /* _NET80211_IEEE80211_HT_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_amrr.h
@@ -0,0 +1,64 @@
+/* $FreeBSD: src/sys/net80211/ieee80211_amrr.h,v 1.1 2006/11/26 19:55:26 sam Exp $ */
+/*	$OpenBSD: ieee80211_amrr.h,v 1.3 2006/06/17 19:34:31 damien Exp $	*/
+
+/*-
+ * Copyright (c) 2006
+ *	Damien Bergamini <damien.bergamini at free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _NET80211_IEEE80211_AMRR_H_
+#define _NET80211_IEEE80211_AMRR_H_
+
+/*-
+ * Naive implementation of the Adaptive Multi Rate Retry algorithm:
+ *
+ * "IEEE 802.11 Rate Adaptation: A Practical Approach"
+ *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
+ *  INRIA Sophia - Projet Planete
+ *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
+ */
+
+/*
+ * Rate control settings.
+ */
+struct ieee80211com;
+
+struct ieee80211_amrr {
+	u_int	amrr_min_success_threshold;
+	u_int	amrr_max_success_threshold;
+	struct ieee80211com *amrr_ic;
+};
+
+#define IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD	 1
+#define IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD	15
+
+/*
+ * Rate control state for a given node.
+ */
+struct ieee80211_amrr_node {
+	u_int	amn_success;
+	u_int	amn_recovery;
+	u_int	amn_success_threshold;
+	u_int	amn_txcnt;
+	u_int	amn_retrycnt;
+};
+
+void	ieee80211_amrr_init(struct ieee80211_amrr *,
+	    struct ieee80211com *ic, int, int);
+void	ieee80211_amrr_node_init(struct ieee80211_amrr *,
+	    struct ieee80211_amrr_node *);
+void	ieee80211_amrr_choose(struct ieee80211_amrr *, struct ieee80211_node *,
+	    struct ieee80211_amrr_node *);
+
+#endif /* _NET80211_IEEE80211_AMRR_H_ */
Index: ieee80211_proto.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_proto.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_proto.h -L sys/net80211/ieee80211_proto.h -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_proto.h
+++ sys/net80211/ieee80211_proto.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,7 +23,7 @@
  * (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.11.2.5 2006/02/12 19:00:39 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.26.2.1 2007/11/11 17:44:36 sam Exp $
  */
 #ifndef _NET80211_IEEE80211_PROTO_H_
 #define _NET80211_IEEE80211_PROTO_H_
@@ -43,13 +37,26 @@
 	IEEE80211_S_SCAN	= 1,	/* scanning */
 	IEEE80211_S_AUTH	= 2,	/* try to authenticate */
 	IEEE80211_S_ASSOC	= 3,	/* try to assoc */
-	IEEE80211_S_RUN		= 4,	/* associated */
+	IEEE80211_S_CAC		= 4,	/* doing channel availability check */
+	IEEE80211_S_RUN		= 5,	/* operational (e.g. associated) */
+	IEEE80211_S_CSA		= 6,	/* channel switch announce pending */
+	IEEE80211_S_SLEEP	= 7,	/* power save */
 };
-#define	IEEE80211_S_MAX		(IEEE80211_S_RUN+1)
+#define	IEEE80211_S_MAX		(IEEE80211_S_SLEEP+1)
 
 #define	IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
 	((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
 
+/*
+ * The formation of some management frames requires guidance to
+ * deal with legacy clients.  When the client is identified as
+ * "legacy 11b" this parameter can be passed in the arg param of a
+ * IEEE80211_SEND_MGMT call.
+ */
+#define	IEEE80211_SEND_LEGACY_11B	0x1	/* legacy 11b client */
+#define	IEEE80211_SEND_LEGACY_11	0x2	/* other legacy client */
+#define	IEEE80211_SEND_LEGACY		0x3	/* any legacy client */
+
 extern	const char *ieee80211_mgt_subtype_name[];
 extern	const char *ieee80211_phymode_name[];
 
@@ -58,27 +65,36 @@
 
 struct ieee80211_node;
 int	ieee80211_input(struct ieee80211com *, struct mbuf *,
-		struct ieee80211_node *, int, u_int32_t);
+		struct ieee80211_node *, int, int, uint32_t);
+void	ieee80211_deliver_data(struct ieee80211com *,
+		struct ieee80211_node *, struct mbuf *);
+struct mbuf *ieee80211_decap1(struct mbuf *, int *);
 int	ieee80211_setup_rates(struct ieee80211_node *ni,
-		const u_int8_t *rates, const u_int8_t *xrates, int flags);
-void	ieee80211_saveie(u_int8_t **, const u_int8_t *);
+		const uint8_t *rates, const uint8_t *xrates, int flags);
+void	ieee80211_saveie(uint8_t **, const uint8_t *);
+void	ieee80211_saveath(struct ieee80211_node *, uint8_t *);
 void	ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
-		struct ieee80211_node *, int, int, u_int32_t);
+		struct ieee80211_node *, int, int, int, uint32_t);
+int	ieee80211_mgmt_output(struct ieee80211com *, struct ieee80211_node *,
+		struct mbuf *, int type);
+struct ieee80211_bpf_params;
+int	ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
+		const struct ieee80211_bpf_params *);
+int	ieee80211_output(struct ifnet *, struct mbuf *,
+		struct sockaddr *, struct rtentry *);
 int	ieee80211_send_nulldata(struct ieee80211_node *);
-int	ieee80211_send_probereq(struct ieee80211_node *ni,
-		const u_int8_t sa[IEEE80211_ADDR_LEN],
-		const u_int8_t da[IEEE80211_ADDR_LEN],
-		const u_int8_t bssid[IEEE80211_ADDR_LEN],
-		const u_int8_t *ssid, size_t ssidlen,
-		const void *optie, size_t optielen);
 int	ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
 		int, int);
+int	ieee80211_send_probereq(struct ieee80211_node *ni,
+		const uint8_t sa[IEEE80211_ADDR_LEN],
+		const uint8_t da[IEEE80211_ADDR_LEN],
+		const uint8_t bssid[IEEE80211_ADDR_LEN],
+		const uint8_t *ssid, size_t ssidlen,
+		const void *optie, size_t optielen);
 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);
@@ -102,12 +118,12 @@
 	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);
+		size += sizeof(uint16_t);
 	return size;
 }
 
 /*
- * Return the size of the 802.11 header; handles any type of frame.
+ * Like ieee80211_hdrsize, but handles any type of frame.
  */
 static __inline int
 ieee80211_anyhdrsize(const void *data)
@@ -119,6 +135,8 @@
 		case IEEE80211_FC0_SUBTYPE_CTS:
 		case IEEE80211_FC0_SUBTYPE_ACK:
 			return sizeof(struct ieee80211_frame_ack);
+		case IEEE80211_FC0_SUBTYPE_BAR:
+			return sizeof(struct ieee80211_frame_bar);
 		}
 		return sizeof(struct ieee80211_frame_min);
 	} else
@@ -155,11 +173,11 @@
 	int	(*iac_attach)(struct ieee80211com *);
 	void	(*iac_detach)(struct ieee80211com *);
 	int	(*iac_check)(struct ieee80211com *,
-			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+			const uint8_t mac[IEEE80211_ADDR_LEN]);
 	int	(*iac_add)(struct ieee80211com *,
-			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+			const uint8_t mac[IEEE80211_ADDR_LEN]);
 	int	(*iac_remove)(struct ieee80211com *,
-			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+			const uint8_t mac[IEEE80211_ADDR_LEN]);
 	int	(*iac_flush)(struct ieee80211com *);
 	int	(*iac_setpolicy)(struct ieee80211com *, int);
 	int	(*iac_getpolicy)(struct ieee80211com *);
@@ -175,24 +193,27 @@
 #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 ieee80211_node *, int);
+#define	IEEE80211_F_DOBRS	0x00000010	/* check basic rate set */
+#define	IEEE80211_F_JOIN	0x00000020	/* sta joining our bss */
+int	ieee80211_fix_rate(struct ieee80211_node *,
+		struct ieee80211_rateset *, 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) */
+	uint8_t		wmep_acm;
+	uint8_t		wmep_aifsn;
+	uint8_t		wmep_logcwmin;		/* log2(cwmin) */
+	uint8_t		wmep_logcwmax;		/* log2(cwmax) */
+	uint8_t		wmep_txopLimit;
+	uint8_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 */
+	uint8_t		cap_info;		/* version of the current set */
 	struct wmeParams cap_wmeParams[WME_NUM_AC];
 };
 
@@ -218,10 +239,14 @@
 
 #define	ieee80211_new_state(_ic, _nstate, _arg) \
 	(((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
+int	ieee80211_init(struct ieee80211com *, int forcescan);
+void	ieee80211_dturbo_switch(struct ieee80211com *, int newflags);
 void	ieee80211_beacon_miss(struct ieee80211com *);
-void	ieee80211_print_essid(const u_int8_t *, int);
-void	ieee80211_dump_pkt(const u_int8_t *, int, int, int);
+void	ieee80211_print_essid(const uint8_t *, int);
+void	ieee80211_dump_pkt(struct ieee80211com *,
+		const uint8_t *, int, int, int);
 
+extern 	const char *ieee80211_opmode_name[];
 extern	const char *ieee80211_state_name[IEEE80211_S_MAX];
 extern	const char *ieee80211_wme_acnames[];
 
@@ -231,20 +256,48 @@
  * 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 */
-	u_int8_t	*bo_erp;	/* start of ERP element */
-	void		*bo_pad[8];	/* future expansion */
+	uint8_t		bo_flags[4];	/* update/state flags */
+	uint16_t	*bo_caps;	/* capabilities */
+	uint8_t		*bo_cfp;	/* start of CFParms element */
+	uint8_t		*bo_tim;	/* start of atim/dtim */
+	uint8_t		*bo_wme;	/* start of WME parameters */
+	uint8_t		*bo_tim_trailer;/* start of fixed-size trailer */
+	uint16_t	bo_tim_len;	/* atim/dtim length in bytes */
+	uint16_t	bo_tim_trailer_len;/* tim trailer length in bytes */
+	uint8_t		*bo_erp;	/* start of ERP element */
+	uint8_t		*bo_htinfo;	/* start of HT info element */
+	uint8_t		*bo_appie;	/* start of AppIE element */
+	uint16_t	bo_appie_len;	/* AppIE length in bytes */
+	uint16_t	bo_csa_trailer_len;;
+	uint8_t		*bo_csa;	/* start of CSA element */
+};
+struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
+		struct ieee80211_beacon_offsets *);
+
+/*
+ * Beacon frame updates are signaled through calls to ic_update_beacon
+ * with one of the IEEE80211_BEACON_* tokens defined below.  For devices
+ * that construct beacon frames on the host this can trigger a rebuild
+ * or defer the processing.  For devices that offload beacon frame
+ * handling this callback can be used to signal a rebuild.  The bo_flags
+ * array in the ieee80211_beacon_offsets structure is intended to record
+ * deferred processing requirements; ieee80211_beacon_update uses the
+ * state to optimize work.  Since this structure is owned by the driver
+ * and not visible to the 802.11 layer drivers must supply an ic_update_beacon
+ * callback that marks the flag bits and schedules (as necessary) an update.
+ */
+enum {
+	IEEE80211_BEACON_CAPS	= 0,	/* capabilities */
+	IEEE80211_BEACON_TIM	= 1,	/* DTIM/ATIM */
+	IEEE80211_BEACON_WME	= 2,
+	IEEE80211_BEACON_ERP	= 3,	/* Extended Rate Phy */
+	IEEE80211_BEACON_HTINFO	= 4,	/* HT Information */
+	IEEE80211_BEACON_APPIE	= 5,	/* Application IE's */
+	IEEE80211_BEACON_CFP	= 6,	/* CFParms */
+	IEEE80211_BEACON_CSA	= 7,	/* Channel Switch Announcement */
 };
-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);
+int	ieee80211_beacon_update(struct ieee80211_node *,
+		struct ieee80211_beacon_offsets *, struct mbuf *, int mcast);
 
 /*
  * Notification methods called from the 802.11 state machine.
--- /dev/null
+++ sys/net80211/ieee80211_regdomain.h
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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_regdomain.h,v 1.1 2007/06/11 03:36:55 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_REGDOMAIN_H_
+#define _NET80211_IEEE80211_REGDOMAIN_H_
+
+/*
+ * 802.11 regulatory domain definitions.
+ */
+
+/*
+ * ISO 3166 Country/Region Codes
+ * http://ftp.ics.uci.edu/pub/ietf/http/related/iso3166.txt
+ */
+enum ISOCountryCode {
+	CTRY_AFGHANISTAN	= 4,
+	CTRY_ALBANIA		= 8,	/* Albania */
+	CTRY_ALGERIA		= 12,	/* Algeria */
+	CTRY_AMERICAN_SAMOA	= 16,
+	CTRY_ANDORRA		= 20,
+	CTRY_ANGOLA		= 24,
+	CTRY_ANGUILLA		= 660,
+	/* XXX correct remainder */
+	CTRY_ARGENTINA		= 32,	/* Argentina */
+	CTRY_ARMENIA		= 51,	/* Armenia */
+	CTRY_AUSTRALIA		= 36,	/* Australia */
+	CTRY_AUSTRIA		= 40,	/* Austria */
+	CTRY_AZERBAIJAN		= 31,	/* Azerbaijan */
+	CTRY_BAHRAIN		= 48,	/* Bahrain */
+	CTRY_BELARUS		= 112,	/* Belarus */
+	CTRY_BELGIUM		= 56,	/* Belgium */
+	CTRY_BELIZE		= 84,	/* Belize */
+	CTRY_BOLIVIA		= 68,	/* Bolivia */
+	CTRY_BRAZIL		= 76,	/* Brazil */
+	CTRY_BRUNEI_DARUSSALAM	= 96,	/* Brunei Darussalam */
+	CTRY_BULGARIA		= 100,	/* Bulgaria */
+	CTRY_CANADA		= 124,	/* Canada */
+	CTRY_CHILE		= 152,	/* Chile */
+	CTRY_CHINA		= 156,	/* People's Republic of China */
+	CTRY_COLOMBIA		= 170,	/* Colombia */
+	CTRY_COSTA_RICA		= 188,	/* Costa Rica */
+	CTRY_CROATIA		= 191,	/* Croatia */
+	CTRY_CYPRUS		= 196,	/* Cyprus */
+	CTRY_CZECH		= 203,	/* Czech Republic */
+	CTRY_DENMARK		= 208,	/* Denmark */
+	CTRY_DOMINICAN_REPUBLIC	= 214,	/* Dominican Republic */
+	CTRY_ECUADOR		= 218,	/* Ecuador */
+	CTRY_EGYPT		= 818,	/* Egypt */
+	CTRY_EL_SALVADOR	= 222,	/* El Salvador */
+	CTRY_ESTONIA		= 233,	/* Estonia */
+	CTRY_FAEROE_ISLANDS	= 234,	/* Faeroe Islands */
+	CTRY_FINLAND		= 246,	/* Finland */
+	CTRY_FRANCE		= 250,	/* France */
+	CTRY_FRANCE2		= 255,	/* France2 */
+	CTRY_GEORGIA		= 268,	/* Georgia */
+	CTRY_GERMANY		= 276,	/* Germany */
+	CTRY_GREECE		= 300,	/* Greece */
+	CTRY_GUATEMALA		= 320,	/* Guatemala */
+	CTRY_HONDURAS		= 340,	/* Honduras */
+	CTRY_HONG_KONG		= 344,	/* Hong Kong S.A.R., P.R.C. */
+	CTRY_HUNGARY		= 348,	/* Hungary */
+	CTRY_ICELAND		= 352,	/* Iceland */
+	CTRY_INDIA		= 356,	/* India */
+	CTRY_INDONESIA		= 360,	/* Indonesia */
+	CTRY_IRAN		= 364,	/* Iran */
+	CTRY_IRAQ		= 368,	/* Iraq */
+	CTRY_IRELAND		= 372,	/* Ireland */
+	CTRY_ISRAEL		= 376,	/* Israel */
+	CTRY_ITALY		= 380,	/* Italy */
+	CTRY_JAMAICA		= 388,	/* Jamaica */
+	CTRY_JAPAN		= 392,	/* Japan */
+	CTRY_JAPAN1		= 393,	/* Japan (JP1) */
+	CTRY_JAPAN2		= 394,	/* Japan (JP0) */
+	CTRY_JAPAN3		= 395,	/* Japan (JP1-1) */
+	CTRY_JAPAN4		= 396,	/* Japan (JE1) */
+	CTRY_JAPAN5		= 397,	/* Japan (JE2) */
+	CTRY_JORDAN		= 400,	/* Jordan */
+	CTRY_KAZAKHSTAN		= 398,	/* Kazakhstan */
+	CTRY_KENYA		= 404,	/* Kenya */
+	CTRY_KOREA_NORTH	= 408,	/* North Korea */
+	CTRY_KOREA_ROC		= 410,	/* South Korea */
+	CTRY_KOREA_ROC2		= 411,	/* South Korea */
+	CTRY_KUWAIT		= 414,	/* Kuwait */
+	CTRY_LATVIA		= 428,	/* Latvia */
+	CTRY_LEBANON		= 422,	/* Lebanon */
+	CTRY_LIBYA		= 434,	/* Libya */
+	CTRY_LIECHTENSTEIN	= 438,	/* Liechtenstein */
+	CTRY_LITHUANIA		= 440,	/* Lithuania */
+	CTRY_LUXEMBOURG		= 442,	/* Luxembourg */
+	CTRY_MACAU		= 446,	/* Macau */
+	CTRY_MACEDONIA		= 807,	/* the Former Yugoslav Republic of Macedonia */
+	CTRY_MALAYSIA		= 458,	/* Malaysia */
+	CTRY_MEXICO		= 484,	/* Mexico */
+	CTRY_MONACO		= 492,	/* Principality of Monaco */
+	CTRY_MOROCCO		= 504,	/* Morocco */
+	CTRY_NETHERLANDS	= 528,	/* Netherlands */
+	CTRY_NEW_ZEALAND	= 554,	/* New Zealand */
+	CTRY_NICARAGUA		= 558,	/* Nicaragua */
+	CTRY_NORWAY		= 578,	/* Norway */
+	CTRY_OMAN		= 512,	/* Oman */
+	CTRY_PAKISTAN		= 586,	/* Islamic Republic of Pakistan */
+	CTRY_PANAMA		= 591,	/* Panama */
+	CTRY_PARAGUAY		= 600,	/* Paraguay */
+	CTRY_PERU		= 604,	/* Peru */
+	CTRY_PHILIPPINES	= 608,	/* Republic of the Philippines */
+	CTRY_POLAND		= 616,	/* Poland */
+	CTRY_PORTUGAL		= 620,	/* Portugal */
+	CTRY_PUERTO_RICO	= 630,	/* Puerto Rico */
+	CTRY_QATAR		= 634,	/* Qatar */
+	CTRY_ROMANIA		= 642,	/* Romania */
+	CTRY_RUSSIA		= 643,	/* Russia */
+	CTRY_SAUDI_ARABIA	= 682,	/* Saudi Arabia */
+	CTRY_SINGAPORE		= 702,	/* Singapore */
+	CTRY_SLOVAKIA		= 703,	/* Slovak Republic */
+	CTRY_SLOVENIA		= 705,	/* Slovenia */
+	CTRY_SOUTH_AFRICA	= 710,	/* South Africa */
+	CTRY_SPAIN		= 724,	/* Spain */
+	CTRY_SWEDEN		= 752,	/* Sweden */
+	CTRY_SWITZERLAND	= 756,	/* Switzerland */
+	CTRY_SYRIA		= 760,	/* Syria */
+	CTRY_TAIWAN		= 158,	/* Taiwan */
+	CTRY_THAILAND		= 764,	/* Thailand */
+	CTRY_TRINIDAD_Y_TOBAGO	= 780,	/* Trinidad y Tobago */
+	CTRY_TUNISIA		= 788,	/* Tunisia */
+	CTRY_TURKEY		= 792,	/* Turkey */
+	CTRY_UAE		= 784,	/* U.A.E. */
+	CTRY_UKRAINE		= 804,	/* Ukraine */
+	CTRY_UNITED_KINGDOM	= 826,	/* United Kingdom */
+	CTRY_UNITED_STATES	= 840,	/* United States */
+	CTRY_URUGUAY		= 858,	/* Uruguay */
+	CTRY_UZBEKISTAN		= 860,	/* Uzbekistan */
+	CTRY_VENEZUELA		= 862,	/* Venezuela */
+	CTRY_VIET_NAM		= 704,	/* Viet Nam */
+	CTRY_YEMEN		= 887,	/* Yemen */
+	CTRY_ZIMBABWE		= 716,	/* Zimbabwe */
+};
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+#define CTRY_DEBUG                0x1ff   /* debug */
+#define CTRY_DEFAULT              0       /* default */
+
+void	ieee80211_regdomain_attach(struct ieee80211com *);
+void	ieee80211_regdomain_detach(struct ieee80211com *);
+
+void	ieee80211_init_channels(struct ieee80211com *ic,
+	    int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm);
+uint8_t	*ieee80211_add_countryie(uint8_t *, struct ieee80211com *,
+	    enum ISOCountryCode cc, int location);
+const char *ieee80211_cctoiso(enum ISOCountryCode);
+int	ieee80211_isotocc(const char iso[2]);
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+#endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */
Index: ieee80211_crypto_ccmp.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_ccmp.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_ccmp.c -L sys/net80211/ieee80211_crypto_ccmp.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_crypto_ccmp.c
+++ sys/net80211/ieee80211_crypto_ccmp.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -30,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_ccmp.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_ccmp.c,v 1.10 2007/06/11 03:36:54 sam Exp $");
 
 /*
  * IEEE 802.11i AES-CCMP crypto support.
@@ -66,7 +60,7 @@
 static	void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
 static	void ccmp_detach(struct ieee80211_key *);
 static	int ccmp_setkey(struct ieee80211_key *);
-static	int ccmp_encap(struct ieee80211_key *k, struct mbuf *, u_int8_t keyid);
+static	int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid);
 static	int ccmp_decap(struct ieee80211_key *, struct mbuf *, int);
 static	int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int);
 static	int ccmp_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -140,11 +134,11 @@
  * Add privacy headers appropriate for the specified key.
  */
 static int
-ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
 {
 	struct ccmp_ctx *ctx = k->wk_private;
 	struct ieee80211com *ic = ctx->cc_ic;
-	u_int8_t *ivp;
+	uint8_t *ivp;
 	int hdrlen;
 
 	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
@@ -155,7 +149,7 @@
 	M_PREPEND(m, ccmp.ic_header, M_NOWAIT);
 	if (m == NULL)
 		return 0;
-	ivp = mtod(m, u_int8_t *);
+	ivp = mtod(m, uint8_t *);
 	ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen);
 	ivp += hdrlen;
 
@@ -250,7 +244,7 @@
 	/*
 	 * Copy up 802.11 header and strip crypto bits.
 	 */
-	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + ccmp.ic_header, hdrlen);
+	ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen);
 	m_adj(m, ccmp.ic_header);
 	m_adj(m, -ccmp.ic_trailer);
 
@@ -356,7 +350,7 @@
 			b0[1] = aad[30];
 			aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
 		} else {
-			*(u_int16_t *)&aad[30] = 0;
+			*(uint16_t *)&aad[30] = 0;
 			b0[1] = 0;
 			aad[1] = 22 + IEEE80211_ADDR_LEN;
 		}
@@ -369,12 +363,12 @@
 			b0[1] = aad[24];
 			aad[1] = 22 + 2;
 		} else {
-			*(u_int16_t *)&aad[24] = 0;
+			*(uint16_t *)&aad[24] = 0;
 			b0[1] = 0;
 			aad[1] = 22;
 		}
-		*(u_int16_t *)&aad[26] = 0;
-		*(u_int32_t *)&aad[28] = 0;
+		*(uint16_t *)&aad[26] = 0;
+		*(uint32_t *)&aad[28] = 0;
 	}
 
 	/* Start with the first block and AAD */
@@ -635,32 +629,4 @@
 /*
  * Module glue.
  */
-static int
-ccmp_modevent(module_t mod, int type, void *unused)
-{
-	switch (type) {
-	case MOD_LOAD:
-		ieee80211_crypto_register(&ccmp);
-		return 0;
-	case MOD_UNLOAD:
-	case MOD_QUIESCE:
-		if (nrefs) {
-			printf("wlan_ccmp: still in use (%u dynamic refs)\n",
-				nrefs);
-			return EBUSY;
-		}
-		if (type == MOD_UNLOAD)
-			ieee80211_crypto_unregister(&ccmp);
-		return 0;
-	}
-	return EINVAL;
-}
-
-static moduledata_t ccmp_mod = {
-	"wlan_ccmp",
-	ccmp_modevent,
-	0
-};
-DECLARE_MODULE(wlan_ccmp, ccmp_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_ccmp, 1);
-MODULE_DEPEND(wlan_ccmp, wlan, 1, 1, 1);
+IEEE80211_CRYPTO_MODULE(ccmp, 1);
--- /dev/null
+++ sys/net80211/ieee80211_scan_sta.c
@@ -0,0 +1,1479 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_scan_sta.c,v 1.4.2.3 2007/11/29 19:05:09 sam Exp $");
+
+/*
+ * IEEE 802.11 station scanning support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/kernel.h>
+#include <sys/module.h>
+ 
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+/*
+ * Parameters for managing cache entries:
+ *
+ * o a station with STA_FAILS_MAX failures is not considered
+ *   when picking a candidate
+ * o a station that hasn't had an update in STA_PURGE_SCANS
+ *   (background) scans is discarded
+ * o after STA_FAILS_AGE seconds we clear the failure count
+ */
+#define	STA_FAILS_MAX	2		/* assoc failures before ignored */
+#define	STA_FAILS_AGE	(2*60)		/* time before clearing fails (secs) */
+#define	STA_PURGE_SCANS	2		/* age for purging entries (scans) */
+
+/* XXX tunable */
+#define	STA_RSSI_MIN	8		/* min acceptable rssi */
+#define	STA_RSSI_MAX	40		/* max rssi for comparison */
+
+#define RSSI_LPF_LEN		10
+#define	RSSI_DUMMY_MARKER	0x127
+#define	RSSI_EP_MULTIPLIER	(1<<7)	/* pow2 to optimize out * and / */
+#define RSSI_IN(x)		((x) * RSSI_EP_MULTIPLIER)
+#define LPF_RSSI(x, y, len) \
+    ((x != RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define RSSI_LPF(x, y) do {						\
+    if ((y) >= -20)							\
+    	x = LPF_RSSI((x), RSSI_IN((y)), RSSI_LPF_LEN);			\
+} while (0)
+#define	EP_RND(x, mul) \
+	((((x)%(mul)) >= ((mul)/2)) ? howmany(x, mul) : (x)/(mul))
+#define	RSSI_GET(x)	EP_RND(x, RSSI_EP_MULTIPLIER)
+
+struct sta_entry {
+	struct ieee80211_scan_entry base;
+	TAILQ_ENTRY(sta_entry) se_list;
+	LIST_ENTRY(sta_entry) se_hash;
+	uint8_t		se_fails;		/* failure to associate count */
+	uint8_t		se_seen;		/* seen during current scan */
+	uint8_t		se_notseen;		/* not seen in previous scans */
+	uint8_t		se_flags;
+	uint32_t	se_avgrssi;		/* LPF rssi state */
+	unsigned long	se_lastupdate;		/* time of last update */
+	unsigned long	se_lastfail;		/* time of last failure */
+	unsigned long	se_lastassoc;		/* time of last association */
+	u_int		se_scangen;		/* iterator scan gen# */
+};
+
+#define	STA_HASHSIZE	32
+/* simple hash is enough for variation of macaddr */
+#define	STA_HASH(addr)	\
+	(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % STA_HASHSIZE)
+
+struct sta_table {
+	struct mtx	st_lock;		/* on scan table */
+	TAILQ_HEAD(, sta_entry) st_entry;	/* all entries */
+	LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE];
+	struct mtx	st_scanlock;		/* on st_scangen */
+	u_int		st_scangen;		/* gen# for iterator */
+	int		st_newscan;
+};
+
+static void sta_flush_table(struct sta_table *);
+/*
+ * match_bss returns a bitmask describing if an entry is suitable
+ * for use.  If non-zero the entry was deemed not suitable and it's
+ * contents explains why.  The following flags are or'd to to this
+ * mask and can be used to figure out why the entry was rejected.
+ */
+#define	MATCH_CHANNEL	0x001	/* channel mismatch */
+#define	MATCH_CAPINFO	0x002	/* capabilities mismatch, e.g. no ess */
+#define	MATCH_PRIVACY	0x004	/* privacy mismatch */
+#define	MATCH_RATE	0x008	/* rate set mismatch */
+#define	MATCH_SSID	0x010	/* ssid mismatch */
+#define	MATCH_BSSID	0x020	/* bssid mismatch */
+#define	MATCH_FAILS	0x040	/* too many failed auth attempts */
+#define	MATCH_NOTSEEN	0x080	/* not seen in recent scans */
+#define	MATCH_RSSI	0x100	/* rssi deemed too low to use */
+static int match_bss(struct ieee80211com *,
+	const struct ieee80211_scan_state *, struct sta_entry *, int);
+
+/* number of references from net80211 layer */
+static	int nrefs = 0;
+
+/*
+ * Attach prior to any scanning work.
+ */
+static int
+sta_attach(struct ieee80211_scan_state *ss)
+{
+	struct sta_table *st;
+
+	MALLOC(st, struct sta_table *, sizeof(struct sta_table),
+		M_80211_SCAN, M_NOWAIT | M_ZERO);
+	if (st == NULL)
+		return 0;
+	mtx_init(&st->st_lock, "scantable", "802.11 scan table", MTX_DEF);
+	mtx_init(&st->st_scanlock, "scangen", "802.11 scangen", MTX_DEF);
+	TAILQ_INIT(&st->st_entry);
+	ss->ss_priv = st;
+	nrefs++;			/* NB: we assume caller locking */
+	return 1;
+}
+
+/*
+ * Cleanup any private state.
+ */
+static int
+sta_detach(struct ieee80211_scan_state *ss)
+{
+	struct sta_table *st = ss->ss_priv;
+
+	if (st != NULL) {
+		sta_flush_table(st);
+		mtx_destroy(&st->st_lock);
+		mtx_destroy(&st->st_scanlock);
+		FREE(st, M_80211_SCAN);
+		KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+		nrefs--;		/* NB: we assume caller locking */
+	}
+	return 1;
+}
+
+/*
+ * Flush all per-scan state.
+ */
+static int
+sta_flush(struct ieee80211_scan_state *ss)
+{
+	struct sta_table *st = ss->ss_priv;
+
+	mtx_lock(&st->st_lock);
+	sta_flush_table(st);
+	mtx_unlock(&st->st_lock);
+	ss->ss_last = 0;
+	return 0;
+}
+
+/*
+ * Flush all entries in the scan cache.
+ */
+static void
+sta_flush_table(struct sta_table *st)
+{
+	struct sta_entry *se, *next;
+
+	TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
+		TAILQ_REMOVE(&st->st_entry, se, se_list);
+		LIST_REMOVE(se, se_hash);
+		FREE(se, M_80211_SCAN);
+	}
+}
+
+static void
+saveie(uint8_t **iep, const uint8_t *ie)
+{
+
+	if (ie == NULL)
+		*iep = NULL;
+	else
+		ieee80211_saveie(iep, ie);
+}
+
+/*
+ * Process a beacon or probe response frame; create an
+ * entry in the scan cache or update any previous entry.
+ */
+static int
+sta_add(struct ieee80211_scan_state *ss, 
+	const struct ieee80211_scanparams *sp,
+	const struct ieee80211_frame *wh,
+	int subtype, int rssi, int noise, int rstamp)
+{
+#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define	PICK1ST(_ss) \
+	((ss->ss_flags & (IEEE80211_SCAN_PICK1ST | IEEE80211_SCAN_GOTPICK)) == \
+	IEEE80211_SCAN_PICK1ST)
+	struct sta_table *st = ss->ss_priv;
+	const uint8_t *macaddr = wh->i_addr2;
+	struct ieee80211com *ic = ss->ss_ic;
+	struct sta_entry *se;
+	struct ieee80211_scan_entry *ise;
+	int hash, offchan;
+
+	hash = STA_HASH(macaddr);
+
+	mtx_lock(&st->st_lock);
+	LIST_FOREACH(se, &st->st_hash[hash], se_hash)
+		if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr))
+			goto found;
+	MALLOC(se, struct sta_entry *, sizeof(struct sta_entry),
+		M_80211_SCAN, M_NOWAIT | M_ZERO);
+	if (se == NULL) {
+		mtx_unlock(&st->st_lock);
+		return 0;
+	}
+	se->se_scangen = st->st_scangen-1;
+	se->se_avgrssi = RSSI_DUMMY_MARKER;
+	IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr);
+	TAILQ_INSERT_TAIL(&st->st_entry, se, se_list);
+	LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash);
+found:
+	ise = &se->base;
+	/* XXX ap beaconing multiple ssid w/ same bssid */
+	if (sp->ssid[1] != 0 &&
+	    (ISPROBE(subtype) || ise->se_ssid[1] == 0))
+		memcpy(ise->se_ssid, sp->ssid, 2+sp->ssid[1]);
+	KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
+		("rate set too large: %u", sp->rates[1]));
+	memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]);
+	if (sp->xrates != NULL) {
+		/* XXX validate xrates[1] */
+		KASSERT(sp->xrates[1] + sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
+			("xrate set too large: %u", sp->xrates[1]));
+		memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]);
+	} else
+		ise->se_xrates[1] = 0;
+	IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3);
+	offchan = (IEEE80211_CHAN2IEEE(sp->curchan) != sp->bchan &&
+	    ic->ic_phytype != IEEE80211_T_FH);
+	if (!offchan) {
+		/*
+		 * Record rssi data using extended precision LPF filter.
+		 *
+		 * NB: use only on-channel data to insure we get a good
+		 *     estimate of the signal we'll see when associated.
+		 */
+		RSSI_LPF(se->se_avgrssi, rssi);
+		ise->se_rssi = RSSI_GET(se->se_avgrssi);
+		ise->se_noise = noise;
+	}
+	ise->se_rstamp = rstamp;
+	memcpy(ise->se_tstamp.data, sp->tstamp, sizeof(ise->se_tstamp));
+	ise->se_intval = sp->bintval;
+	ise->se_capinfo = sp->capinfo;
+	/*
+	 * Beware of overriding se_chan for frames seen
+	 * off-channel; this can cause us to attempt an
+	 * assocation on the wrong channel.
+	 */
+	if (offchan) {
+		struct ieee80211_channel *c;
+		/*
+		 * Off-channel, locate the home/bss channel for the sta
+		 * using the value broadcast in the DSPARMS ie.
+		 */
+		c = ieee80211_find_channel_byieee(ic, sp->bchan,
+		    sp->curchan->ic_flags);
+		if (c != NULL) {
+			ise->se_chan = c;
+		} else if (ise->se_chan == NULL) {
+			/* should not happen, pick something */
+			ise->se_chan = sp->curchan;
+		}
+	} else
+		ise->se_chan = sp->curchan;
+	ise->se_fhdwell = sp->fhdwell;
+	ise->se_fhindex = sp->fhindex;
+	ise->se_erp = sp->erp;
+	ise->se_timoff = sp->timoff;
+	if (sp->tim != NULL) {
+		const struct ieee80211_tim_ie *tim =
+		    (const struct ieee80211_tim_ie *) sp->tim;
+		ise->se_dtimperiod = tim->tim_period;
+	}
+	saveie(&ise->se_wme_ie, sp->wme);
+	saveie(&ise->se_wpa_ie, sp->wpa);
+	saveie(&ise->se_rsn_ie, sp->rsn);
+	saveie(&ise->se_ath_ie, sp->ath);
+	saveie(&ise->se_htcap_ie, sp->htcap);
+	saveie(&ise->se_htinfo_ie, sp->htinfo);
+
+	/* clear failure count after STA_FAIL_AGE passes */
+	if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) {
+		se->se_fails = 0;
+		IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, macaddr,
+		    "%s: fails %u", __func__, se->se_fails);
+	}
+
+	se->se_lastupdate = ticks;		/* update time */
+	se->se_seen = 1;
+	se->se_notseen = 0;
+
+	mtx_unlock(&st->st_lock);
+
+	/*
+	 * If looking for a quick choice and nothing's
+	 * been found check here.
+	 */
+	if (PICK1ST(ss) && match_bss(ic, ss, se, IEEE80211_MSG_SCAN) == 0)
+		ss->ss_flags |= IEEE80211_SCAN_GOTPICK;
+
+	return 1;
+#undef PICK1ST
+#undef ISPROBE
+}
+
+/*
+ * Check if a channel is excluded by user request.
+ */
+static int
+isexcluded(struct ieee80211com *ic, const struct ieee80211_channel *c)
+{
+	return (isclr(ic->ic_chan_active, c->ic_ieee) ||
+	    (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+	     c->ic_freq != ic->ic_des_chan->ic_freq));
+}
+
+static struct ieee80211_channel *
+find11gchannel(struct ieee80211com *ic, int i, int freq)
+{
+	struct ieee80211_channel *c;
+	int j;
+
+	/*
+	 * The normal ordering in the channel list is b channel
+	 * immediately followed by g so optimize the search for
+	 * this.  We'll still do a full search just in case.
+	 */
+	for (j = i+1; j < ic->ic_nchans; j++) {
+		c = &ic->ic_channels[j];
+		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+			return c;
+	}
+	for (j = 0; j < i; j++) {
+		c = &ic->ic_channels[j];
+		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+			return c;
+	}
+	return NULL;
+}
+static const u_int chanflags[IEEE80211_MODE_MAX] = {
+	IEEE80211_CHAN_B,	/* IEEE80211_MODE_AUTO */
+	IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
+	IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
+	IEEE80211_CHAN_G,	/* IEEE80211_MODE_11G */
+	IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
+	IEEE80211_CHAN_A,	/* IEEE80211_MODE_TURBO_A (check base channel)*/
+	IEEE80211_CHAN_G,	/* IEEE80211_MODE_TURBO_G */
+	IEEE80211_CHAN_ST,	/* IEEE80211_MODE_STURBO_A */
+	IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA (check legacy) */
+	IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG (check legacy) */
+};
+
+static void
+add_channels(struct ieee80211com *ic,
+	struct ieee80211_scan_state *ss,
+	enum ieee80211_phymode mode, const uint16_t freq[], int nfreq)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	struct ieee80211_channel *c, *cg;
+	u_int modeflags;
+	int i;
+
+	KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
+	modeflags = chanflags[mode];
+	for (i = 0; i < nfreq; i++) {
+		if (ss->ss_last >= IEEE80211_SCAN_MAX)
+			break;
+
+		c = ieee80211_find_channel(ic, freq[i], modeflags);
+		if (c != NULL && isexcluded(ic, c))
+			continue;
+		if (mode == IEEE80211_MODE_AUTO) {
+			/*
+			 * XXX special-case 11b/g channels so we select
+			 *     the g channel if both are present or there
+			 *     are only g channels.
+			 */
+			if (c == NULL || IEEE80211_IS_CHAN_B(c)) {
+				cg = find11gchannel(ic, i, freq[i]);
+				if (cg != NULL)
+					c = cg;
+			}
+		}
+		if (c == NULL)
+			continue;
+
+		ss->ss_chans[ss->ss_last++] = c;
+	}
+#undef N
+}
+
+static const uint16_t rcl1[] =		/* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
+{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
+static const uint16_t rcl2[] =		/* 4 MKK channels: 34, 38, 42, 46 */
+{ 5170, 5190, 5210, 5230 };
+static const uint16_t rcl3[] =		/* 2.4Ghz ch: 1,6,11,7,13 */
+{ 2412, 2437, 2462, 2442, 2472 };
+static const uint16_t rcl4[] =		/* 5 FCC channel: 149, 153, 161, 165 */
+{ 5745, 5765, 5785, 5805, 5825 };
+static const uint16_t rcl7[] =		/* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
+{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
+static const uint16_t rcl8[] =		/* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
+{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
+static const uint16_t rcl9[] =		/* 2.4Ghz ch: 14 */
+{ 2484 };
+static const uint16_t rcl10[] =		/* Added Korean channels 2312-2372 */
+{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
+static const uint16_t rcl11[] =		/* Added Japan channels in 4.9/5.0 spectrum */
+{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
+#ifdef ATH_TURBO_SCAN
+static const uint16_t rcl5[] =		/* 3 static turbo channels */
+{ 5210, 5250, 5290 };
+static const uint16_t rcl6[] =		/* 2 static turbo channels */
+{ 5760, 5800 };
+static const uint16_t rcl6x[] =		/* 4 FCC3 turbo channels */
+{ 5540, 5580, 5620, 5660 };
+static const uint16_t rcl12[] =		/* 2.4Ghz Turbo channel 6 */
+{ 2437 };
+static const uint16_t rcl13[] =		/* dynamic Turbo channels */
+{ 5200, 5240, 5280, 5765, 5805 };
+#endif /* ATH_TURBO_SCAN */
+
+struct scanlist {
+	uint16_t	mode;
+	uint16_t	count;
+	const uint16_t	*list;
+};
+
+#define	X(a)	.count = sizeof(a)/sizeof(a[0]), .list = a
+
+static const struct scanlist staScanTable[] = {
+	{ IEEE80211_MODE_11B,   	X(rcl3) },
+	{ IEEE80211_MODE_11A,   	X(rcl1) },
+	{ IEEE80211_MODE_11A,   	X(rcl2) },
+	{ IEEE80211_MODE_11B,   	X(rcl8) },
+	{ IEEE80211_MODE_11B,   	X(rcl9) },
+	{ IEEE80211_MODE_11A,   	X(rcl4) },
+#ifdef ATH_TURBO_SCAN
+	{ IEEE80211_MODE_STURBO_A,	X(rcl5) },
+	{ IEEE80211_MODE_STURBO_A,	X(rcl6) },
+	{ IEEE80211_MODE_TURBO_A,	X(rcl6x) },
+	{ IEEE80211_MODE_TURBO_A,	X(rcl13) },
+#endif /* ATH_TURBO_SCAN */
+	{ IEEE80211_MODE_11A,		X(rcl7) },
+	{ IEEE80211_MODE_11B,		X(rcl10) },
+	{ IEEE80211_MODE_11A,		X(rcl11) },
+#ifdef ATH_TURBO_SCAN
+	{ IEEE80211_MODE_TURBO_G,	X(rcl12) },
+#endif /* ATH_TURBO_SCAN */
+	{ .list = NULL }
+};
+
+static int
+checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
+{
+	int i;
+
+	for (; scan->list != NULL; scan++) {
+		for (i = 0; i < scan->count; i++)
+			if (scan->list[i] == c->ic_freq) 
+				return 1;
+	}
+	return 0;
+}
+
+/*
+ * Start a station-mode scan by populating the channel list.
+ */
+static int
+sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct sta_table *st = ss->ss_priv;
+	const struct scanlist *scan;
+	enum ieee80211_phymode mode;
+	struct ieee80211_channel *c;
+	int i;
+
+	ss->ss_last = 0;
+	/*
+	 * Use the table of ordered channels to construct the list
+	 * of channels for scanning.  Any channels in the ordered
+	 * list not in the master list will be discarded.
+	 */
+	for (scan = staScanTable; scan->list != NULL; scan++) {
+		mode = scan->mode;
+		if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
+			/*
+			 * If a desired mode was specified, scan only 
+			 * channels that satisfy that constraint.
+			 */
+			if (ic->ic_des_mode != mode) {
+				/*
+				 * The scan table marks 2.4Ghz channels as b
+				 * so if the desired mode is 11g, then use
+				 * the 11b channel list but upgrade the mode.
+				 */
+				if (ic->ic_des_mode != IEEE80211_MODE_11G ||
+				    mode != IEEE80211_MODE_11B)
+					continue;
+				mode = IEEE80211_MODE_11G;	/* upgrade */
+			}
+		} else {
+			/*
+			 * This lets add_channels upgrade an 11b channel
+			 * to 11g if available.
+			 */
+			if (mode == IEEE80211_MODE_11B)
+				mode = IEEE80211_MODE_AUTO;
+		}
+#ifdef IEEE80211_F_XR
+		/* XR does not operate on turbo channels */
+		if ((ic->ic_flags & IEEE80211_F_XR) &&
+		    (mode == IEEE80211_MODE_TURBO_A ||
+		     mode == IEEE80211_MODE_TURBO_G ||
+		     mode == IEEE80211_MODE_STURBO_A))
+			continue;
+#endif
+		/*
+		 * Add the list of the channels; any that are not
+		 * in the master channel list will be discarded.
+		 */
+		add_channels(ic, ss, mode, scan->list, scan->count);
+	}
+
+	/*
+	 * Add the channels from the ic (from HAL) that are not present
+	 * in the staScanTable.
+	 */
+	for (i = 0; i < ic->ic_nchans; i++) {
+		if (ss->ss_last >= IEEE80211_SCAN_MAX)
+			break;
+
+		c = &ic->ic_channels[i];
+		/*
+		 * Ignore dynamic turbo channels; we scan them
+		 * in normal mode (i.e. not boosted).  Likewise
+		 * for HT channels, they get scanned using
+		 * legacy rates.
+		 */
+		if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c))
+			continue;
+
+		/*
+		 * If a desired mode was specified, scan only 
+		 * channels that satisfy that constraint.
+		 */
+		if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
+		    ic->ic_des_mode != ieee80211_chan2mode(c))
+			continue;
+
+		/*
+		 * Skip channels excluded by user request.
+		 */
+		if (isexcluded(ic, c))
+			continue;
+
+		/*
+		 * Add the channel unless it is listed in the
+		 * fixed scan order tables.  This insures we
+		 * don't sweep back in channels we filtered out
+		 * above.
+		 */
+		if (checktable(staScanTable, c))
+			continue;
+
+		/* Add channel to scanning list. */
+		ss->ss_chans[ss->ss_last++] = c;
+	}
+
+	ss->ss_next = 0;
+	/* XXX tunables */
+	ss->ss_mindwell = msecs_to_ticks(20);		/* 20ms */
+	ss->ss_maxdwell = msecs_to_ticks(200);		/* 200ms */
+
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic)) {
+		if_printf(ic->ic_ifp, "scan set ");
+		ieee80211_scan_dump_channels(ss);
+		printf(" dwell min %ld max %ld\n",
+			ss->ss_mindwell, ss->ss_maxdwell);
+	}
+#endif /* IEEE80211_DEBUG */
+
+	st->st_newscan = 1;
+
+	return 0;
+#undef N
+}
+
+/*
+ * Restart a bg scan.
+ */
+static int
+sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	struct sta_table *st = ss->ss_priv;
+
+	st->st_newscan = 1;
+	return 0;
+}
+
+/*
+ * Cancel an ongoing scan.
+ */
+static int
+sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	return 0;
+}
+
+static uint8_t
+maxrate(const struct ieee80211_scan_entry *se)
+{
+	uint8_t rmax, r;
+	int i;
+
+	rmax = 0;
+	for (i = 0; i < se->se_rates[1]; i++) {
+		r = se->se_rates[2+i] & IEEE80211_RATE_VAL;
+		if (r > rmax)
+			rmax = r;
+	}
+	for (i = 0; i < se->se_xrates[1]; i++) {
+		r = se->se_xrates[2+i] & IEEE80211_RATE_VAL;
+		if (r > rmax)
+			rmax = r;
+	}
+	return rmax;
+}
+
+/*
+ * Compare the capabilities of two entries 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
+sta_compare(const struct sta_entry *a, const struct sta_entry *b)
+{
+#define	PREFER(_a,_b,_what) do {			\
+	if (((_a) ^ (_b)) & (_what))			\
+		return ((_a) & (_what)) ? 1 : -1;	\
+} while (0)
+	uint8_t maxa, maxb;
+	int8_t rssia, rssib;
+	int weight;
+
+	/* privacy support */
+	PREFER(a->base.se_capinfo, b->base.se_capinfo,
+		IEEE80211_CAPINFO_PRIVACY);
+
+	/* compare count of previous failures */
+	weight = b->se_fails - a->se_fails;
+	if (abs(weight) > 1)
+		return weight;
+
+	/*
+	 * Compare rssi.  If the two are considered equivalent
+	 * then fallback to other criteria.  We threshold the
+	 * comparisons to avoid selecting an ap purely by rssi
+	 * when both values may be good but one ap is otherwise
+	 * more desirable (e.g. an 11b-only ap with stronger
+	 * signal than an 11g ap).
+	 */
+	rssia = MIN(a->base.se_rssi, STA_RSSI_MAX);
+	rssib = MIN(b->base.se_rssi, STA_RSSI_MAX);
+	if (abs(rssib - rssia) < 5) {
+		/* best/max rate preferred if signal level close enough XXX */
+		maxa = maxrate(&a->base);
+		maxb = maxrate(&b->base);
+		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->base.se_chan) &&
+		   !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
+			return 1;
+		if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
+		     IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
+			return -1;
+	}
+	/* all things being equal, use signal level */
+	return a->base.se_rssi - b->base.se_rssi;
+#undef PREFER
+}
+
+/*
+ * Check rate set suitability and return the best supported rate.
+ */
+static int
+check_rate(struct ieee80211com *ic, const struct ieee80211_scan_entry *se)
+{
+#define	RV(v)	((v) & IEEE80211_RATE_VAL)
+	const struct ieee80211_rateset *srs;
+	int i, j, nrs, r, okrate, badrate, fixedrate;
+	const uint8_t *rs;
+
+	okrate = badrate = fixedrate = 0;
+
+	srs = ieee80211_get_suprates(ic, se->se_chan);
+	nrs = se->se_rates[1];
+	rs = se->se_rates+2;
+	fixedrate = IEEE80211_FIXED_RATE_NONE;
+again:
+	for (i = 0; i < nrs; i++) {
+		r = RV(rs[i]);
+		badrate = r;
+		/*
+		 * Check any fixed rate is included. 
+		 */
+		if (r == ic->ic_fixed_rate)
+			fixedrate = r;
+		/*
+		 * Check against our supported rates.
+		 */
+		for (j = 0; j < srs->rs_nrates; j++)
+			if (r == RV(srs->rs_rates[j])) {
+				if (r > okrate)		/* NB: track max */
+					okrate = r;
+				break;
+			}
+
+		if (j == srs->rs_nrates && (rs[i] & IEEE80211_RATE_BASIC)) {
+			/*
+			 * Don't try joining a BSS, if we don't support
+			 * one of its basic rates.
+			 */
+			okrate = 0;
+			goto back;
+		}
+	}
+	if (rs == se->se_rates+2) {
+		/* scan xrates too; sort of an algol68-style for loop */
+		nrs = se->se_xrates[1];
+		rs = se->se_xrates+2;
+		goto again;
+	}
+
+back:
+	if (okrate == 0 || ic->ic_fixed_rate != fixedrate)
+		return badrate | IEEE80211_RATE_BASIC;
+	else
+		return RV(okrate);
+#undef RV
+}
+
+static int
+match_ssid(const uint8_t *ie,
+	int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+	int i;
+
+	for (i = 0; i < nssid; i++) {
+		if (ie[1] == ssids[i].len &&
+		     memcmp(ie+2, ssids[i].ssid, ie[1]) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Test a scan candidate for suitability/compatibility.
+ */
+static int
+match_bss(struct ieee80211com *ic,
+	const struct ieee80211_scan_state *ss, struct sta_entry *se0,
+	int debug)
+{
+	struct ieee80211_scan_entry *se = &se0->base;
+	uint8_t rate;
+	int fail;
+
+	fail = 0;
+	if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan)))
+		fail |= MATCH_CHANNEL;
+	/*
+	 * NB: normally the desired mode is used to construct
+	 * the channel list, but it's possible for the scan
+	 * cache to include entries for stations outside this
+	 * list so we check the desired mode here to weed them
+	 * out.
+	 */
+	if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
+	    (se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
+	    chanflags[ic->ic_des_mode])
+		fail |= MATCH_CHANNEL;
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+			fail |= MATCH_CAPINFO;
+	} else {
+		if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0)
+			fail |= MATCH_CAPINFO;
+	}
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+			fail |= MATCH_PRIVACY;
+	} else {
+		/* XXX does this mean privacy is supported or required? */
+		if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY)
+			fail |= MATCH_PRIVACY;
+	}
+	rate = check_rate(ic, se);
+	if (rate & IEEE80211_RATE_BASIC)
+		fail |= MATCH_RATE;
+	if (ss->ss_nssid != 0 &&
+	    !match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid))
+		fail |= MATCH_SSID;
+	if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, se->se_bssid))
+		fail |=  MATCH_BSSID;
+	if (se0->se_fails >= STA_FAILS_MAX)
+		fail |= MATCH_FAILS;
+	/* NB: entries may be present awaiting purge, skip */
+	if (se0->se_notseen >= STA_PURGE_SCANS)
+		fail |= MATCH_NOTSEEN;
+	if (se->se_rssi < STA_RSSI_MIN)
+		fail |= MATCH_RSSI;
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg(ic, debug)) {
+		printf(" %c %s",
+		    fail & MATCH_FAILS ? '=' :
+		    fail & MATCH_NOTSEEN ? '^' :
+		    fail ? '-' : '+', ether_sprintf(se->se_macaddr));
+		printf(" %s%c", ether_sprintf(se->se_bssid),
+		    fail & MATCH_BSSID ? '!' : ' ');
+		printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan),
+			fail & MATCH_CHANNEL ? '!' : ' ');
+		printf(" %+4d%c", se->se_rssi, fail & MATCH_RSSI ? '!' : ' ');
+		printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
+		    fail & MATCH_RATE ? '!' : ' ');
+		printf(" %4s%c",
+		    (se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
+		    (se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
+		    "????",
+		    fail & MATCH_CAPINFO ? '!' : ' ');
+		printf(" %3s%c ",
+		    (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
+		    "wep" : "no",
+		    fail & MATCH_PRIVACY ? '!' : ' ');
+		ieee80211_print_essid(se->se_ssid+2, se->se_ssid[1]);
+		printf("%s\n", fail & MATCH_SSID ? "!" : "");
+	}
+#endif
+	return fail;
+}
+
+static void
+sta_update_notseen(struct sta_table *st)
+{
+	struct sta_entry *se;
+
+	mtx_lock(&st->st_lock);
+	TAILQ_FOREACH(se, &st->st_entry, se_list) {
+		/*
+		 * If seen the reset and don't bump the count;
+		 * otherwise bump the ``not seen'' count.  Note
+		 * that this insures that stations for which we
+		 * see frames while not scanning but not during
+		 * this scan will not be penalized.
+		 */
+		if (se->se_seen)
+			se->se_seen = 0;
+		else
+			se->se_notseen++;
+	}
+	mtx_unlock(&st->st_lock);
+}
+
+static void
+sta_dec_fails(struct sta_table *st)
+{
+	struct sta_entry *se;
+
+	mtx_lock(&st->st_lock);
+	TAILQ_FOREACH(se, &st->st_entry, se_list)
+		if (se->se_fails)
+			se->se_fails--;
+	mtx_unlock(&st->st_lock);
+}
+
+static struct sta_entry *
+select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se, *selbs = NULL;
+
+	IEEE80211_DPRINTF(ic, debug, " %s\n",
+	    "macaddr          bssid         chan  rssi  rate flag  wep  essid");
+	mtx_lock(&st->st_lock);
+	TAILQ_FOREACH(se, &st->st_entry, se_list) {
+		if (match_bss(ic, ss, se, debug) == 0) {
+			if (selbs == NULL)
+				selbs = se;
+			else if (sta_compare(se, selbs) > 0)
+				selbs = se;
+		}
+	}
+	mtx_unlock(&st->st_lock);
+
+	return selbs;
+}
+
+/*
+ * Pick an ap or ibss network to join or find a channel
+ * to use to start an ibss network.
+ */
+static int
+sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *selbs;
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+		("wrong mode %u", ic->ic_opmode));
+
+	if (st->st_newscan) {
+		sta_update_notseen(st);
+		st->st_newscan = 0;
+	}
+	if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
+		/*
+		 * Manual/background scan, don't select+join the
+		 * bss, just return.  The scanning framework will
+		 * handle notification that this has completed.
+		 */
+		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+		return 1;
+	}
+	/*
+	 * Automatic sequencing; look for a candidate and
+	 * if found join the network.
+	 */
+	/* NB: unlocked read should be ok */
+	if (TAILQ_FIRST(&st->st_entry) == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			"%s: no scan candidate\n", __func__);
+notfound:
+		/*
+		 * If nothing suitable was found decrement
+		 * the failure counts so entries will be
+		 * reconsidered the next time around.  We
+		 * really want to do this only for sta's
+		 * where we've previously had some success.
+		 */
+		sta_dec_fails(st);
+		st->st_newscan = 1;
+		return 0;			/* restart scan */
+	}
+	selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
+	if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+		goto notfound;
+	return 1;				/* terminate scan */
+}
+
+/*
+ * Lookup an entry in the scan cache.  We assume we're
+ * called from the bottom half or such that we don't need
+ * to block the bottom half so that it's safe to return
+ * a reference to an entry w/o holding the lock on the table.
+ */
+static struct sta_entry *
+sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+	struct sta_entry *se;
+	int hash = STA_HASH(macaddr);
+
+	mtx_lock(&st->st_lock);
+	LIST_FOREACH(se, &st->st_hash[hash], se_hash)
+		if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr))
+			break;
+	mtx_unlock(&st->st_lock);
+
+	return se;		/* NB: unlocked */
+}
+
+static void
+sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni = ic->ic_bss;
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se, *selbs;
+	uint8_t roamRate, curRate;
+	int8_t roamRssi, curRssi;
+
+	se = sta_lookup(st, ni->ni_macaddr);
+	if (se == NULL) {
+		/* XXX something is wrong */
+		return;
+	}
+
+	/* XXX do we need 11g too? */
+	if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
+		roamRate = ic->ic_roam.rate11b;
+		roamRssi = ic->ic_roam.rssi11b;
+	} else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) {
+		roamRate = ic->ic_roam.rate11bOnly;
+		roamRssi = ic->ic_roam.rssi11bOnly;
+	} else {
+		roamRate = ic->ic_roam.rate11a;
+		roamRssi = ic->ic_roam.rssi11a;
+	}
+	/* NB: the most up to date rssi is in the node, not the scan cache */
+	curRssi = ic->ic_node_getrssi(ni);
+	if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+		curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+		    "%s: currssi %d currate %u roamrssi %d roamrate %u\n",
+		    __func__, curRssi, curRate, roamRssi, roamRate);
+	} else {
+		curRate = roamRate;	/* NB: insure compare below fails */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+		    "%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi);
+	}
+	/*
+	 * Check if a new ap should be used and switch.
+	 * XXX deauth current ap
+	 */
+	if (curRate < roamRate || curRssi < roamRssi) {
+		if (time_after(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+			/*
+			 * Scan cache contents are too old; force a scan now
+			 * if possible so we have current state to make a
+			 * decision with.  We don't kick off a bg scan if
+			 * we're using dynamic turbo and boosted or if the
+			 * channel is busy.
+			 * XXX force immediate switch on scan complete
+			 */
+			if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
+			    time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle))
+				ieee80211_bg_scan(ic);
+			return;
+		}
+		se->base.se_rssi = curRssi;
+		selbs = select_bss(ss, ic, IEEE80211_MSG_ROAM);
+		if (selbs != NULL && selbs != se) {
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG,
+			    "%s: ROAM: curRate %u, roamRate %u, "
+			    "curRssi %d, roamRssi %d\n", __func__,
+			    curRate, roamRate, curRssi, roamRssi);
+			ieee80211_sta_join(ic, &selbs->base);
+		}
+	}
+}
+
+/*
+ * Age entries in the scan cache.
+ * XXX also do roaming since it's convenient
+ */
+static void
+sta_age(struct ieee80211_scan_state *ss)
+{
+	struct ieee80211com *ic = ss->ss_ic;
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se, *next;
+
+	mtx_lock(&st->st_lock);
+	TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
+		if (se->se_notseen > STA_PURGE_SCANS) {
+			TAILQ_REMOVE(&st->st_entry, se, se_list);
+			LIST_REMOVE(se, se_hash);
+			FREE(se, M_80211_SCAN);
+		}
+	}
+	mtx_unlock(&st->st_lock);
+	/*
+	 * If rate control is enabled check periodically to see if
+	 * we should roam from our current connection to one that
+	 * might be better.  This only applies when we're operating
+	 * in sta mode and automatic roaming is set.
+	 * XXX defer if busy
+	 * XXX repeater station
+	 * XXX do when !bgscan?
+	 */
+	KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+		("wrong mode %u", ic->ic_opmode));
+	if (ic->ic_roaming == IEEE80211_ROAMING_AUTO &&
+	    (ic->ic_flags & IEEE80211_F_BGSCAN) &&
+	    ic->ic_state >= IEEE80211_S_RUN)
+		/* XXX vap is implicit */
+		sta_roam_check(ss, ic);
+}
+
+/*
+ * Iterate over the entries in the scan cache, invoking
+ * the callback function on each one.
+ */
+static void
+sta_iterate(struct ieee80211_scan_state *ss, 
+	ieee80211_scan_iter_func *f, void *arg)
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se;
+	u_int gen;
+
+	mtx_lock(&st->st_scanlock);
+	gen = st->st_scangen++;
+restart:
+	mtx_lock(&st->st_lock);
+	TAILQ_FOREACH(se, &st->st_entry, se_list) {
+		if (se->se_scangen != gen) {
+			se->se_scangen = gen;
+			/* update public state */
+			se->base.se_age = ticks - se->se_lastupdate;
+			mtx_unlock(&st->st_lock);
+			(*f)(arg, &se->base);
+			goto restart;
+		}
+	}
+	mtx_unlock(&st->st_lock);
+
+	mtx_unlock(&st->st_scanlock);
+}
+
+static void
+sta_assoc_fail(struct ieee80211_scan_state *ss,
+	const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se;
+
+	se = sta_lookup(st, macaddr);
+	if (se != NULL) {
+		se->se_fails++;
+		se->se_lastfail = ticks;
+		IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+		    macaddr, "%s: reason %u fails %u",
+		    __func__, reason, se->se_fails);
+	}
+}
+
+static void
+sta_assoc_success(struct ieee80211_scan_state *ss,
+	const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se;
+
+	se = sta_lookup(st, macaddr);
+	if (se != NULL) {
+#if 0
+		se->se_fails = 0;
+		IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+		    macaddr, "%s: fails %u",
+		    __func__, se->se_fails);
+#endif
+		se->se_lastassoc = ticks;
+	}
+}
+
+static const struct ieee80211_scanner sta_default = {
+	.scan_name		= "default",
+	.scan_attach		= sta_attach,
+	.scan_detach		= sta_detach,
+	.scan_start		= sta_start,
+	.scan_restart		= sta_restart,
+	.scan_cancel		= sta_cancel,
+	.scan_end		= sta_pick_bss,
+	.scan_flush		= sta_flush,
+	.scan_add		= sta_add,
+	.scan_age		= sta_age,
+	.scan_iterate		= sta_iterate,
+	.scan_assoc_fail	= sta_assoc_fail,
+	.scan_assoc_success	= sta_assoc_success,
+};
+
+/*
+ * Adhoc mode-specific support.
+ */
+
+static const uint16_t adhocWorld[] =		/* 36, 40, 44, 48 */
+{ 5180, 5200, 5220, 5240 };
+static const uint16_t adhocFcc3[] =		/* 36, 40, 44, 48 145, 149, 153, 157, 161, 165 */
+{ 5180, 5200, 5220, 5240, 5725, 5745, 5765, 5785, 5805, 5825 };
+static const uint16_t adhocMkk[] =		/* 34, 38, 42, 46 */
+{ 5170, 5190, 5210, 5230 };
+static const uint16_t adhoc11b[] =		/* 10, 11 */
+{ 2457, 2462 };
+
+static const struct scanlist adhocScanTable[] = {
+	{ IEEE80211_MODE_11B,   	X(adhoc11b) },
+	{ IEEE80211_MODE_11A,   	X(adhocWorld) },
+	{ IEEE80211_MODE_11A,   	X(adhocFcc3) },
+	{ IEEE80211_MODE_11B,   	X(adhocMkk) },
+	{ .list = NULL }
+};
+#undef X
+
+/*
+ * Start an adhoc-mode scan by populating the channel list.
+ */
+static int
+adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct sta_table *st = ss->ss_priv;
+	const struct scanlist *scan;
+	enum ieee80211_phymode mode;
+	
+	ss->ss_last = 0;
+	/*
+	 * Use the table of ordered channels to construct the list
+	 * of channels for scanning.  Any channels in the ordered
+	 * list not in the master list will be discarded.
+	 */
+	for (scan = adhocScanTable; scan->list != NULL; scan++) {
+		mode = scan->mode;
+		if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
+			/*
+			 * If a desired mode was specified, scan only 
+			 * channels that satisfy that constraint.
+			 */
+			if (ic->ic_des_mode != mode) {
+				/*
+				 * The scan table marks 2.4Ghz channels as b
+				 * so if the desired mode is 11g, then use
+				 * the 11b channel list but upgrade the mode.
+				 */
+				if (ic->ic_des_mode != IEEE80211_MODE_11G ||
+				    mode != IEEE80211_MODE_11B)
+					continue;
+				mode = IEEE80211_MODE_11G;	/* upgrade */
+			}
+		} else {
+			/*
+			 * This lets add_channels upgrade an 11b channel
+			 * to 11g if available.
+			 */
+			if (mode == IEEE80211_MODE_11B)
+				mode = IEEE80211_MODE_AUTO;
+		}
+#ifdef IEEE80211_F_XR
+		/* XR does not operate on turbo channels */
+		if ((ic->ic_flags & IEEE80211_F_XR) &&
+		    (mode == IEEE80211_MODE_TURBO_A ||
+		     mode == IEEE80211_MODE_TURBO_G))
+			continue;
+#endif
+		/*
+		 * Add the list of the channels; any that are not
+		 * in the master channel list will be discarded.
+		 */
+		add_channels(ic, ss, mode, scan->list, scan->count);
+	}
+	ss->ss_next = 0;
+	/* XXX tunables */
+	ss->ss_mindwell = msecs_to_ticks(200);		/* 200ms */
+	ss->ss_maxdwell = msecs_to_ticks(200);		/* 200ms */
+
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic)) {
+		if_printf(ic->ic_ifp, "scan set ");
+		ieee80211_scan_dump_channels(ss);
+		printf(" dwell min %ld max %ld\n",
+			ss->ss_mindwell, ss->ss_maxdwell);
+	}
+#endif /* IEEE80211_DEBUG */
+
+	st->st_newscan = 1;
+
+	return 0;
+#undef N
+}
+
+/*
+ * Select a channel to start an adhoc network on.
+ * The channel list was populated with appropriate
+ * channels so select one that looks least occupied.
+ * XXX need regulatory domain constraints
+ */
+static struct ieee80211_channel *
+adhoc_pick_channel(struct ieee80211_scan_state *ss)
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se;
+	struct ieee80211_channel *c, *bestchan;
+	int i, bestrssi, maxrssi;
+
+	bestchan = NULL;
+	bestrssi = -1;
+
+	mtx_lock(&st->st_lock);
+	for (i = 0; i < ss->ss_last; i++) {
+		c = ss->ss_chans[i];
+		maxrssi = 0;
+		TAILQ_FOREACH(se, &st->st_entry, se_list) {
+			if (se->base.se_chan != c)
+				continue;
+			if (se->base.se_rssi > maxrssi)
+				maxrssi = se->base.se_rssi;
+		}
+		if (bestchan == NULL || maxrssi < bestrssi)
+			bestchan = c;
+	}
+	mtx_unlock(&st->st_lock);
+
+	return bestchan;
+}
+
+/*
+ * Pick an ibss network to join or find a channel
+ * to use to start an ibss network.
+ */
+static int
+adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *selbs;
+	struct ieee80211_channel *chan;
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_IBSS ||
+		ic->ic_opmode == IEEE80211_M_AHDEMO,
+		("wrong opmode %u", ic->ic_opmode));
+
+	if (st->st_newscan) {
+		sta_update_notseen(st);
+		st->st_newscan = 0;
+	}
+	if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
+		/*
+		 * Manual/background scan, don't select+join the
+		 * bss, just return.  The scanning framework will
+		 * handle notification that this has completed.
+		 */
+		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+		return 1;
+	}
+	/*
+	 * Automatic sequencing; look for a candidate and
+	 * if found join the network.
+	 */
+	/* NB: unlocked read should be ok */
+	if (TAILQ_FIRST(&st->st_entry) == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			"%s: no scan candidate\n", __func__);
+notfound:
+		if (ic->ic_des_nssid) {
+			/*
+			 * No existing adhoc network to join and we have
+			 * an ssid; start one up.  If no channel was
+			 * specified, try to select a channel.
+			 */
+			if (ic->ic_des_chan == IEEE80211_CHAN_ANYC)
+				chan = ieee80211_ht_adjust_channel(ic,
+				    adhoc_pick_channel(ss), ic->ic_flags_ext);
+			else
+				chan = ic->ic_des_chan;
+			if (chan != NULL) {
+				ieee80211_create_ibss(ic, chan);
+				return 1;
+			}
+		}
+		/*
+		 * If nothing suitable was found decrement
+		 * the failure counts so entries will be
+		 * reconsidered the next time around.  We
+		 * really want to do this only for sta's
+		 * where we've previously had some success.
+		 */
+		sta_dec_fails(st);
+		st->st_newscan = 1;
+		return 0;			/* restart scan */
+	}
+	selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
+	if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+		goto notfound;
+	return 1;				/* terminate scan */
+}
+
+/*
+ * Age entries in the scan cache.
+ */
+static void
+adhoc_age(struct ieee80211_scan_state *ss)
+{
+	struct sta_table *st = ss->ss_priv;
+	struct sta_entry *se, *next;
+
+	mtx_lock(&st->st_lock);
+	TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
+		if (se->se_notseen > STA_PURGE_SCANS) {
+			TAILQ_REMOVE(&st->st_entry, se, se_list);
+			LIST_REMOVE(se, se_hash);
+			FREE(se, M_80211_SCAN);
+		}
+	}
+	mtx_unlock(&st->st_lock);
+}
+
+static const struct ieee80211_scanner adhoc_default = {
+	.scan_name		= "default",
+	.scan_attach		= sta_attach,
+	.scan_detach		= sta_detach,
+	.scan_start		= adhoc_start,
+	.scan_restart		= sta_restart,
+	.scan_cancel		= sta_cancel,
+	.scan_end		= adhoc_pick_bss,
+	.scan_flush		= sta_flush,
+	.scan_add		= sta_add,
+	.scan_age		= adhoc_age,
+	.scan_iterate		= sta_iterate,
+	.scan_assoc_fail	= sta_assoc_fail,
+	.scan_assoc_success	= sta_assoc_success,
+};
+
+/*
+ * Module glue.
+ */
+static int
+wlan_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		ieee80211_scanner_register(IEEE80211_M_STA, &sta_default);
+		ieee80211_scanner_register(IEEE80211_M_IBSS, &adhoc_default);
+		ieee80211_scanner_register(IEEE80211_M_AHDEMO, &adhoc_default);
+		return 0;
+	case MOD_UNLOAD:
+	case MOD_QUIESCE:
+		if (nrefs) {
+			printf("wlan_scan_sta: still in use (%u dynamic refs)\n",
+				nrefs);
+			return EBUSY;
+		}
+		if (type == MOD_UNLOAD) {
+			ieee80211_scanner_unregister_all(&sta_default);
+			ieee80211_scanner_unregister_all(&adhoc_default);
+		}
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t wlan_mod = {
+	"wlan_scan_sta",
+	wlan_modevent,
+	0
+};
+DECLARE_MODULE(wlan_scan_sta, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_scan_sta, 1);
+MODULE_DEPEND(wlan_scan_sta, wlan, 1, 1, 1);
Index: ieee80211_crypto.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto.h -L sys/net80211/ieee80211_crypto.h -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_crypto.h
+++ sys/net80211/ieee80211_crypto.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,7 +23,7 @@
  * (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.9.2.1 2005/09/03 22:40:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.13 2007/06/11 03:36:54 sam Exp $
  */
 #ifndef _NET80211_IEEE80211_CRYPTO_H_
 #define _NET80211_IEEE80211_CRYPTO_H_
@@ -45,7 +39,7 @@
  */
 struct ieee80211_wepkey {
 	u_int		wk_len;		/* key length in bytes */
-	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE];
+	uint8_t		wk_key[IEEE80211_KEYBUF_SIZE];
 };
 
 struct ieee80211_cipher;
@@ -66,12 +60,12 @@
  * Ciphers such as TKIP may also support mixed hardware/software
  * encrypt/decrypt and MIC processing.
  */
-typedef u_int16_t ieee80211_keyix;	/* h/w key index */
+typedef uint16_t ieee80211_keyix;	/* h/w key index */
 
 struct ieee80211_key {
-	u_int8_t	wk_keylen;	/* key length in bytes */
-	u_int8_t	wk_pad;
-	u_int16_t	wk_flags;
+	uint8_t		wk_keylen;	/* key length in bytes */
+	uint8_t		wk_pad;
+	uint16_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 */
@@ -79,11 +73,11 @@
 #define	IEEE80211_KEY_SWMIC	0x20	/* host-based enmic/demic */
 	ieee80211_keyix	wk_keyix;	/* h/w key index */
 	ieee80211_keyix	wk_rxkeyix;	/* optional h/w rx key index */
-	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+	uint8_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 */
+	uint64_t	wk_keyrsc;	/* key receive sequence counter */
+	uint64_t	wk_keytsc;	/* key transmit sequence counter */
 	const struct ieee80211_cipher *wk_cipher;
 	void		*wk_private;	/* private cipher state */
 };
@@ -122,7 +116,7 @@
 struct ieee80211_crypto_state {
 	struct ieee80211_key	cs_nw_keys[IEEE80211_WEP_NKID];
 	ieee80211_keyix		cs_def_txkey;	/* default/group tx key index */
-	u_int16_t		cs_max_keyix;	/* max h/w key index */
+	uint16_t		cs_max_keyix;	/* max h/w key index */
 
 	int			(*cs_key_alloc)(struct ieee80211com *,
 					const struct ieee80211_key *,
@@ -131,7 +125,7 @@
 					const struct ieee80211_key *);
 	int			(*cs_key_set)(struct ieee80211com *,
 					const struct ieee80211_key *,
-					const u_int8_t mac[IEEE80211_ADDR_LEN]);
+					const uint8_t mac[IEEE80211_ADDR_LEN]);
 	void			(*cs_key_update_begin)(struct ieee80211com *);
 	void			(*cs_key_update_end)(struct ieee80211com *);
 };
@@ -143,7 +137,7 @@
 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]);
+		struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]);
 void	ieee80211_crypto_delglobalkeys(struct ieee80211com *);
 
 /*
@@ -162,13 +156,16 @@
 	void	(*ic_detach)(struct ieee80211_key *);
 	int	(*ic_setkey)(struct ieee80211_key *);
 	int	(*ic_encap)(struct ieee80211_key *, struct mbuf *,
-			u_int8_t keyid);
+			uint8_t keyid);
 	int	(*ic_decap)(struct ieee80211_key *, struct mbuf *, int);
 	int	(*ic_enmic)(struct ieee80211_key *, struct mbuf *, int);
 	int	(*ic_demic)(struct ieee80211_key *, struct mbuf *, int);
 };
 extern	const struct ieee80211_cipher ieee80211_cipher_none;
 
+#define	IEEE80211_KEY_UNDEFINED(k) \
+	((k)->wk_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);
Index: ieee80211.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211.h -L sys/net80211/ieee80211.h -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211.h
+++ sys/net80211/ieee80211.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,7 +23,7 @@
  * (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.9.2.1 2005/07/29 23:31:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.15.2.3 2007/11/28 06:12:30 sam Exp $
  */
 #ifndef _NET80211_IEEE80211_H_
 #define _NET80211_IEEE80211_H_
@@ -44,11 +38,11 @@
 
 /* IEEE 802.11 PLCP header */
 struct ieee80211_plcp_hdr {
-	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;
+	uint16_t	i_sfd;
+	uint8_t		i_signal;
+	uint8_t		i_service;
+	uint16_t	i_length;
+	uint16_t	i_crc;
 } __packed;
 
 #define IEEE80211_PLCP_SFD      0xF3A0 
@@ -58,52 +52,52 @@
  * generic definitions for IEEE 802.11 frames
  */
 struct ieee80211_frame {
-	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];
+	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];
 	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
 	/* see below */
 } __packed;
 
 struct ieee80211_qosframe {
-	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];
+	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];
 	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
 	/* see below */
 } __packed;
 
 struct ieee80211_qoscntl {
-	u_int8_t	i_qos[2];
+	uint8_t		i_qos[2];
 };
 
 struct ieee80211_frame_addr4 {
-	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];
+	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];
 } __packed;
 
 
 struct ieee80211_qosframe_addr4 {
-	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];
+	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];
 } __packed;
 
 #define	IEEE80211_FC0_VERSION_MASK		0x03
@@ -129,7 +123,9 @@
 #define	IEEE80211_FC0_SUBTYPE_DISASSOC		0xa0
 #define	IEEE80211_FC0_SUBTYPE_AUTH		0xb0
 #define	IEEE80211_FC0_SUBTYPE_DEAUTH		0xc0
+#define	IEEE80211_FC0_SUBTYPE_ACTION		0xd0
 /* for TYPE_CTL */
+#define	IEEE80211_FC0_SUBTYPE_BAR		0x80
 #define	IEEE80211_FC0_SUBTYPE_PS_POLL		0xa0
 #define	IEEE80211_FC0_SUBTYPE_RTS		0xb0
 #define	IEEE80211_FC0_SUBTYPE_CTS		0xc0
@@ -165,13 +161,28 @@
 #define	IEEE80211_SEQ_FRAG_SHIFT		0
 #define	IEEE80211_SEQ_SEQ_MASK			0xfff0
 #define	IEEE80211_SEQ_SEQ_SHIFT			4
+#define	IEEE80211_SEQ_RANGE			4096
+
+#define	IEEE80211_SEQ_ADD(seq, incr) \
+	(((seq) + (incr)) & (IEEE80211_SEQ_RANGE-1))
+#define	IEEE80211_SEQ_INC(seq)	IEEE80211_SEQ_ADD(seq,1)
+#define	IEEE80211_SEQ_SUB(a, b) \
+	(((a) + IEEE80211_SEQ_RANGE - (b)) & (IEEE80211_SEQ_RANGE-1))
+
+#define	IEEE80211_SEQ_BA_RANGE			2048	/* 2^11 */
+#define	IEEE80211_SEQ_BA_BEFORE(a, b) \
+	(IEEE80211_SEQ_SUB(b, a+1) < IEEE80211_SEQ_BA_RANGE-1)
 
 #define	IEEE80211_NWID_LEN			32
 
 #define	IEEE80211_QOS_TXOP			0x00ff
 /* bit 8 is reserved */
+#define	IEEE80211_QOS_AMSDU			0x80
+#define	IEEE80211_QOS_AMSDU_S			7
 #define	IEEE80211_QOS_ACKPOLICY			0x60
 #define	IEEE80211_QOS_ACKPOLICY_S		5
+#define	IEEE80211_QOS_ACKPOLICY_NOACK		0x20	/* No ACK required */
+#define	IEEE80211_QOS_ACKPOLICY_BA		0x60	/* Block ACK */
 #define	IEEE80211_QOS_ESOP			0x10
 #define	IEEE80211_QOS_ESOP_S			4
 #define	IEEE80211_QOS_TID			0x0f
@@ -186,53 +197,54 @@
  * 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 */
+	uint8_t		wme_id;		/* IEEE80211_ELEMID_VENDOR */
+	uint8_t		wme_len;	/* length in bytes */
+	uint8_t		wme_oui[3];	/* 0x00, 0x50, 0xf2 */
+	uint8_t		wme_type;	/* OUI type */
+	uint8_t		wme_subtype;	/* OUI subtype */
+	uint8_t		wme_version;	/* spec revision */
+	uint8_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];
+	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];
 } __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;
+	uint8_t		acp_aci_aifsn;
+	uint8_t		acp_logcwminmax;
+	uint16_t	acp_txop;
 } __packed;
 
 #define WME_NUM_AC		4	/* 4 AC categories */
+#define	WME_NUM_TID		16	/* 16 tids */
 
 #define WME_PARAM_ACI		0x60	/* Mask for ACI field */
 #define WME_PARAM_ACI_S		5	/* Shift for ACI field */
@@ -252,7 +264,7 @@
 	0)
 
 #define TID_TO_WME_AC(_tid) (      \
-	((_tid) < 1) ? WME_AC_BE : \
+	((_tid) == 0 || (_tid) == 3) ? WME_AC_BE : \
 	((_tid) < 3) ? WME_AC_BK : \
 	((_tid) < 6) ? WME_AC_VI : \
 	WME_AC_VO)
@@ -261,15 +273,15 @@
  * 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;
+	uint8_t		param_id;
+	uint8_t		param_len;
+	uint8_t		param_oui[3];
+	uint8_t		param_oui_type;
+	uint8_t		param_oui_subtype;
+	uint8_t		param_version;
+	uint8_t		param_qosInfo;
 #define	WME_QOSINFO_COUNT	0x0f	/* Mask for param count field */
-	u_int8_t	param_reserved;
+	uint8_t		param_reserved;
 	struct ieee80211_wme_acparams	params_acParams[WME_NUM_AC];
 } __packed;
 
@@ -277,61 +289,174 @@
  * 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;
+	uint8_t		mnf_category;
+	uint8_t		mnf_action;
+	uint8_t		mnf_dialog;
+	uint8_t		mnf_status;
 } __packed;
 #define	MNF_SETUP_REQ	0
 #define	MNF_SETUP_RESP	1
 #define	MNF_TEARDOWN	2
 
+/* 
+ * 802.11n Management Action Frames 
+ */
+/* generic frame format */
+struct ieee80211_action {
+	uint8_t		ia_category;
+	uint8_t		ia_action;
+} __packed;
+
+#define	IEEE80211_ACTION_CAT_QOS	0	/* QoS */
+#define	IEEE80211_ACTION_CAT_BA		3	/* BA */
+#define	IEEE80211_ACTION_CAT_HT		7	/* HT */
+
+#define	IEEE80211_ACTION_HT_TXCHWIDTH	0	/* recommended xmit chan width*/
+#define	IEEE80211_ACTION_HT_MIMOPWRSAVE	1	/* MIMO power save */
+
+/* HT - recommended transmission channel width */
+struct ieee80211_action_ht_txchwidth {
+	struct ieee80211_action	at_header;
+	uint8_t		at_chwidth;	
+} __packed;
+
+#define	IEEE80211_A_HT_TXCHWIDTH_20	0
+#define	IEEE80211_A_HT_TXCHWIDTH_2040	1
+
+/* HT - MIMO Power Save (NB: D2.04) */
+struct ieee80211_action_ht_mimopowersave {
+	struct ieee80211_action am_header;
+	uint8_t		am_control;
+} __packed;
+
+#define	IEEE80211_A_HT_MIMOPWRSAVE_ENA		0x01	/* PS enabled */
+#define	IEEE80211_A_HT_MIMOPWRSAVE_MODE		0x02
+#define	IEEE80211_A_HT_MIMOPWRSAVE_MODE_S	1
+#define	IEEE80211_A_HT_MIMOPWRSAVE_DYNAMIC	0x02	/* Dynamic Mode */
+#define	IEEE80211_A_HT_MIMOPWRSAVE_STATIC	0x00	/* no SM packets */
+/* bits 2-7 reserved */
+
+/* Block Ack actions */
+#define IEEE80211_ACTION_BA_ADDBA_REQUEST       0   /* ADDBA request */
+#define IEEE80211_ACTION_BA_ADDBA_RESPONSE      1   /* ADDBA response */
+#define IEEE80211_ACTION_BA_DELBA	        2   /* DELBA */
+
+/* Block Ack Parameter Set */
+#define	IEEE80211_BAPS_BUFSIZ	0xffc0		/* buffer size */
+#define	IEEE80211_BAPS_BUFSIZ_S	6
+#define	IEEE80211_BAPS_TID	0x003c		/* TID */
+#define	IEEE80211_BAPS_TID_S	2
+#define	IEEE80211_BAPS_POLICY	0x0002		/* block ack policy */
+#define	IEEE80211_BAPS_POLICY_S	1
+
+#define	IEEE80211_BAPS_POLICY_DELAYED	(0<<IEEE80211_BAPS_POLICY_S)
+#define	IEEE80211_BAPS_POLICY_IMMEDIATE	(1<<IEEE80211_BAPS_POLICY_S)
+
+/* Block Ack Sequence Control */
+#define	IEEE80211_BASEQ_START	0xfff0		/* starting seqnum */
+#define	IEEE80211_BASEQ_START_S	4
+#define	IEEE80211_BASEQ_FRAG	0x000f		/* fragment number */
+#define	IEEE80211_BASEQ_FRAG_S	0
+
+/* Delayed Block Ack Parameter Set */
+#define	IEEE80211_DELBAPS_TID	0xf000		/* TID */
+#define	IEEE80211_DELBAPS_TID_S	12
+#define	IEEE80211_DELBAPS_INIT	0x0800		/* initiator */
+#define	IEEE80211_DELBAPS_INIT_S 11
+
+/* BA - ADDBA request */
+struct ieee80211_action_ba_addbarequest {
+	struct ieee80211_action rq_header;
+	uint8_t		rq_dialogtoken;
+	uint16_t	rq_baparamset;
+	uint16_t	rq_batimeout;		/* in TUs */
+	uint16_t	rq_baseqctl;
+} __packed;
+
+/* BA - ADDBA response */
+struct ieee80211_action_ba_addbaresponse {
+	struct ieee80211_action rs_header;
+	uint8_t		rs_dialogtoken;
+	uint16_t	rs_statuscode;
+	uint16_t	rs_baparamset; 
+	uint16_t	rs_batimeout;		/* in TUs */
+} __packed;
+
+/* BA - DELBA */
+struct ieee80211_action_ba_delba {
+	struct ieee80211_action dl_header;
+	uint16_t	dl_baparamset;
+	uint16_t	dl_reasoncode;
+} __packed;
+
+/* BAR Control */
+#define	IEEE80211_BAR_TID	0xf000		/* TID */
+#define	IEEE80211_BAR_TID_S	12
+#define	IEEE80211_BAR_COMP	0x0004		/* compressed */
+#define	IEEE80211_BAR_MTID	0x0002
+#define	IEEE80211_BAR_NOACK	0x0001		/* no-ack policy */
+
+struct ieee80211_ba_request {
+	uint16_t	rq_barctl;
+	uint16_t	rq_barseqctl;
+} __packed;
+
 /*
  * Control frames.
  */
 struct ieee80211_frame_min {
-	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];
+	uint8_t		i_fc[2];
+	uint8_t		i_dur[2];
+	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
+	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
 	/* FCS */
 } __packed;
 
 struct ieee80211_frame_rts {
-	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];
+	uint8_t		i_fc[2];
+	uint8_t		i_dur[2];
+	uint8_t		i_ra[IEEE80211_ADDR_LEN];
+	uint8_t		i_ta[IEEE80211_ADDR_LEN];
 	/* FCS */
 } __packed;
 
 struct ieee80211_frame_cts {
-	u_int8_t	i_fc[2];
-	u_int8_t	i_dur[2];
-	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	uint8_t		i_fc[2];
+	uint8_t		i_dur[2];
+	uint8_t		i_ra[IEEE80211_ADDR_LEN];
 	/* FCS */
 } __packed;
 
 struct ieee80211_frame_ack {
-	u_int8_t	i_fc[2];
-	u_int8_t	i_dur[2];
-	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	uint8_t		i_fc[2];
+	uint8_t		i_dur[2];
+	uint8_t		i_ra[IEEE80211_ADDR_LEN];
 	/* FCS */
 } __packed;
 
 struct ieee80211_frame_pspoll {
-	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];
+	uint8_t		i_fc[2];
+	uint8_t		i_aid[2];
+	uint8_t		i_bssid[IEEE80211_ADDR_LEN];
+	uint8_t		i_ta[IEEE80211_ADDR_LEN];
 	/* FCS */
 } __packed;
 
 struct ieee80211_frame_cfend {		/* NB: also CF-End+CF-Ack */
-	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];
+	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];
+	/* FCS */
+} __packed;
+
+struct ieee80211_frame_bar {
+	uint8_t		i_fc[2];
+	uint8_t		i_dur[2];
+	uint8_t		i_ra[IEEE80211_ADDR_LEN];
+	uint8_t		i_ta[IEEE80211_ADDR_LEN];
+	uint16_t	i_ctl;
+	uint16_t	i_seq;
 	/* FCS */
 } __packed;
 
@@ -347,8 +472,6 @@
  *		octet information[length]
  */
 
-typedef u_int8_t *ieee80211_mgt_beacon_t;
-
 #define	IEEE80211_BEACON_INTERVAL(beacon) \
 	((beacon)[8] | ((beacon)[9] << 8))
 #define	IEEE80211_BEACON_CAPABILITY(beacon) \
@@ -362,7 +485,8 @@
 #define	IEEE80211_CAPINFO_SHORT_PREAMBLE	0x0020
 #define	IEEE80211_CAPINFO_PBCC			0x0040
 #define	IEEE80211_CAPINFO_CHNL_AGILITY		0x0080
-/* bits 8-9 are reserved */
+#define	IEEE80211_CAPINFO_SPECTRUM_MGMT		0x0100
+/* bit 9 is reserved */
 #define	IEEE80211_CAPINFO_SHORT_SLOTTIME	0x0400
 #define	IEEE80211_CAPINFO_RSN			0x0800
 /* bit 12 is reserved */
@@ -373,21 +497,153 @@
  * 802.11i/WPA information element (maximally sized).
  */
 struct ieee80211_ie_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 */
+	uint8_t		wpa_id;		/* IEEE80211_ELEMID_VENDOR */
+	uint8_t		wpa_len;	/* length in bytes */
+	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 */
+	uint16_t	wpa_caps;	/* 802.11i capabilities */
+	uint16_t	wpa_pmkidcnt;	/* 802.11i pmkid count */
+	uint16_t	wpa_pmkids[8];	/* 802.11i pmkids */
+} __packed;
+
+/*
+ * 802.11n HT Capability IE
+ * NB: these reflect D1.10 
+ */
+struct ieee80211_ie_htcap {
+	uint8_t		hc_id;			/* element ID */
+	uint8_t		hc_len;			/* length in bytes */
+	uint16_t	hc_cap;			/* HT caps (see below) */
+	uint8_t		hc_param;		/* HT params (see below) */
+	uint8_t 	hc_mcsset[16]; 		/* supported MCS set */
+	uint16_t	hc_extcap;		/* extended HT capabilities */
+	uint32_t	hc_txbf;		/* txbf capabilities */
+	uint8_t		hc_antenna;		/* antenna capabilities */
+} __packed;
+
+/* HT capability flags (ht_cap) */
+#define	IEEE80211_HTCAP_LDPC		0x0001	/* LDPC supported */
+#define	IEEE80211_HTCAP_CHWIDTH40	0x0002	/* 20/40 supported */
+#define	IEEE80211_HTCAP_SMPS		0x000c	/* SM Power Save mode */
+#define	IEEE80211_HTCAP_SMPS_OFF	0x0000	/* none (static mode) */
+#define	IEEE80211_HTCAP_SMPS_DYNAMIC	0x0004	/* send RTS first */
+/* NB: SMPS value 2 is reserved */
+#define	IEEE80211_HTCAP_SMPS_ENA	0x000c	/* enabled */
+#define	IEEE80211_HTCAP_GREENFIELD	0x0010	/* Greenfield supported */
+#define	IEEE80211_HTCAP_SHORTGI20	0x0020	/* Short GI in 20MHz */
+#define	IEEE80211_HTCAP_SHORTGI40	0x0040	/* Short GI in 40MHz */
+#define	IEEE80211_HTCAP_TXSTBC		0x0080	/* STBC tx ok */
+#define	IEEE80211_HTCAP_RXSTBC		0x0300  /* STBC rx support */
+#define	IEEE80211_HTCAP_RXSTBC_S	8
+#define	IEEE80211_HTCAP_RXSTBC_1STREAM	0x0100  /* 1 spatial stream */
+#define	IEEE80211_HTCAP_RXSTBC_2STREAM	0x0200  /* 1-2 spatial streams*/
+#define	IEEE80211_HTCAP_RXSTBC_3STREAM	0x0300  /* 1-3 spatial streams*/
+#define	IEEE80211_HTCAP_DELBA		0x0400	/* HT DELBA supported */
+#define	IEEE80211_HTCAP_MAXAMSDU	0x0800	/* max A-MSDU length */
+#define	IEEE80211_HTCAP_MAXAMSDU_7935	0x0800	/* 7935 octets */
+#define	IEEE80211_HTCAP_MAXAMSDU_3839	0x0000	/* 3839 octets */
+#define	IEEE80211_HTCAP_DSSSCCK40	0x1000  /* DSSS/CCK in 40MHz */
+#define	IEEE80211_HTCAP_PSMP		0x2000  /* PSMP supported */
+#define	IEEE80211_HTCAP_40INTOLERANT	0x4000  /* 40MHz intolerant */
+#define	IEEE80211_HTCAP_LSIGTXOPPROT	0x8000  /* L-SIG TXOP prot */
+
+/* HT parameters (hc_param) */
+#define	IEEE80211_HTCAP_MAXRXAMPDU	0x03	/* max rx A-MPDU factor */
+#define	IEEE80211_HTCAP_MAXRXAMPDU_S	0
+#define	IEEE80211_HTCAP_MAXRXAMPDU_8K	0
+#define	IEEE80211_HTCAP_MAXRXAMPDU_16K	1
+#define	IEEE80211_HTCAP_MAXRXAMPDU_32K	2
+#define	IEEE80211_HTCAP_MAXRXAMPDU_64K	3
+#define	IEEE80211_HTCAP_MPDUDENSITY	0x1c	/* min MPDU start spacing */
+#define	IEEE80211_HTCAP_MPDUDENSITY_S	2
+#define	IEEE80211_HTCAP_MPDUDENSITY_NA	0	/* no time restriction */
+#define	IEEE80211_HTCAP_MPDUDENSITY_025	1	/* 1/4 us */
+#define	IEEE80211_HTCAP_MPDUDENSITY_05	2	/* 1/2 us */
+#define	IEEE80211_HTCAP_MPDUDENSITY_1	3	/* 1 us */
+#define	IEEE80211_HTCAP_MPDUDENSITY_2	4	/* 2 us */
+#define	IEEE80211_HTCAP_MPDUDENSITY_4	5	/* 4 us */
+#define	IEEE80211_HTCAP_MPDUDENSITY_8	6	/* 8 us */
+#define	IEEE80211_HTCAP_MPDUDENSITY_16	7	/* 16 us */
+
+/* HT extended capabilities (hc_extcap) */
+#define	IEEE80211_HTCAP_PCO		0x0001	/* PCO capable */
+#define	IEEE80211_HTCAP_PCOTRANS	0x0006	/* PCO transition time */
+#define	IEEE80211_HTCAP_PCOTRANS_S	1
+#define	IEEE80211_HTCAP_PCOTRANS_04	0x0002	/* 400 us */
+#define	IEEE80211_HTCAP_PCOTRANS_15	0x0004	/* 1.5 ms */
+#define	IEEE80211_HTCAP_PCOTRANS_5	0x0006	/* 5 ms */
+/* bits 3-7 reserved */
+#define	IEEE80211_HTCAP_MCSFBACK	0x0300	/* MCS feedback */
+#define	IEEE80211_HTCAP_MCSFBACK_S	8
+#define	IEEE80211_HTCAP_MCSFBACK_NONE	0x0000	/* nothing provided */
+#define	IEEE80211_HTCAP_MCSFBACK_UNSOL	0x0200	/* unsolicited feedback */
+#define	IEEE80211_HTCAP_MCSFBACK_MRQ	0x0300	/* " "+respond to MRQ */
+#define	IEEE80211_HTCAP_HTC		0x0400	/* +HTC support */
+#define	IEEE80211_HTCAP_RDR		0x0800	/* reverse direction responder*/
+/* bits 12-15 reserved */
+
+/*
+ * 802.11n HT Information IE
+ */
+struct ieee80211_ie_htinfo {
+	uint8_t		hi_id;			/* element ID */
+	uint8_t		hi_len;			/* length in bytes */
+	uint8_t		hi_ctrlchannel;		/* primary channel */
+	uint8_t		hi_byte1;		/* ht ie byte 1 */
+	uint8_t		hi_byte2;		/* ht ie byte 2 */
+	uint8_t		hi_byte3;		/* ht ie byte 3 */
+	uint16_t	hi_byte45;		/* ht ie bytes 4+5 */
+	uint8_t 	hi_basicmcsset[16]; 	/* basic MCS set */
 } __packed;
 
+/* byte1 */
+#define	IEEE80211_HTINFO_2NDCHAN	0x03	/* secondary/ext chan offset */
+#define	IEEE80211_HTINFO_2NDCHAN_S	0
+#define	IEEE80211_HTINFO_2NDCHAN_NONE	0x00	/* no secondary/ext channel */
+#define	IEEE80211_HTINFO_2NDCHAN_ABOVE	0x01	/* above private channel */
+/* NB: 2 is reserved */
+#define	IEEE80211_HTINFO_2NDCHAN_BELOW	0x03	/* below primary channel */ 
+#define	IEEE80211_HTINFO_TXWIDTH	0x04	/* tx channel width */
+#define	IEEE80211_HTINFO_TXWIDTH_20	0x00	/* 20MHz width */
+#define	IEEE80211_HTINFO_TXWIDTH_2040	0x04	/* any supported width */
+#define	IEEE80211_HTINFO_RIFSMODE	0x08	/* Reduced IFS (RIFS) use */
+#define	IEEE80211_HTINFO_RIFSMODE_PROH	0x00	/* RIFS use prohibited */
+#define	IEEE80211_HTINFO_RIFSMODE_PERM	0x08	/* RIFS use permitted */
+#define	IEEE80211_HTINFO_PMSPONLY	0x10	/* PSMP required to associate */
+#define	IEEE80211_HTINFO_SIGRAN		0xe0	/* shortest Service Interval */
+#define	IEEE80211_HTINFO_SIGRAN_S	5
+#define	IEEE80211_HTINFO_SIGRAN_5	0x00	/* 5 ms */
+/* XXX add rest */
+
+/* bytes 2+3 */
+#define	IEEE80211_HTINFO_OPMODE		0x03	/* operating mode */
+#define	IEEE80211_HTINFO_OPMODE_S	0
+#define	IEEE80211_HTINFO_OPMODE_PURE	0x00	/* no protection */
+#define	IEEE80211_HTINFO_OPMODE_PROTOPT	0x01	/* protection optional */
+#define	IEEE80211_HTINFO_OPMODE_HT20PR	0x02	/* protection for HT20 sta's */
+#define	IEEE80211_HTINFO_OPMODE_MIXED	0x03	/* protection for legacy sta's*/
+#define	IEEE80211_HTINFO_NONGF_PRESENT	0x04	/* non-GF sta's present */
+#define	IEEE80211_HTINFO_TXBL		0x08	/* transmit burst limit */
+#define	IEEE80211_HTINFO_NONHT_PRESENT	0x10	/* non-HT sta's present */
+/* bits 5-15 reserved */
+
+/* bytes 4+5 */
+#define	IEEE80211_HTINFO_2NDARYBEACON	0x01
+#define	IEEE80211_HTINFO_LSIGTXOPPROT	0x02
+#define	IEEE80211_HTINFO_PCO_ACTIVE	0x04
+#define	IEEE80211_HTINFO_40MHZPHASE	0x08
+
+/* byte5 */
+#define	IEEE80211_HTINFO_BASIC_STBCMCS	0x7f
+#define	IEEE80211_HTINFO_BASIC_STBCMCS_S 0
+#define	IEEE80211_HTINFO_DUALPROTECTED	0x80
+
 /*
  * Management information element payloads.
  */
@@ -403,36 +659,80 @@
 	IEEE80211_ELEMID_COUNTRY	= 7,
 	IEEE80211_ELEMID_CHALLENGE	= 16,
 	/* 17-31 reserved for challenge text extension */
+	IEEE80211_ELEMID_PWRCNSTR	= 32,
+	IEEE80211_ELEMID_PWRCAP		= 33,
+	IEEE80211_ELEMID_TPCREQ		= 34,
+	IEEE80211_ELEMID_TPCREP		= 35,
+	IEEE80211_ELEMID_SUPPCHAN	= 36,
+	IEEE80211_ELEMID_CHANSWITCHANN	= 37,
+	IEEE80211_ELEMID_MEASREQ	= 38,
+	IEEE80211_ELEMID_MEASREP	= 39,
+	IEEE80211_ELEMID_QUIET		= 40,
+	IEEE80211_ELEMID_IBSSDFS	= 41,
 	IEEE80211_ELEMID_ERP		= 42,
+	IEEE80211_ELEMID_HTCAP		= 45,
 	IEEE80211_ELEMID_RSN		= 48,
 	IEEE80211_ELEMID_XRATES		= 50,
+	IEEE80211_ELEMID_HTINFO		= 61,
 	IEEE80211_ELEMID_TPC		= 150,
 	IEEE80211_ELEMID_CCKM		= 156,
 	IEEE80211_ELEMID_VENDOR		= 221,	/* vendor private */
 };
 
 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 */
+	uint8_t		tim_ie;			/* IEEE80211_ELEMID_TIM */
+	uint8_t		tim_len;
+	uint8_t		tim_count;		/* DTIM count */
+	uint8_t		tim_period;		/* DTIM period */
+	uint8_t		tim_bitctl;		/* bitmap control */
+	uint8_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 */
+	uint8_t		ie;			/* IEEE80211_ELEMID_COUNTRY */
+	uint8_t		len;
+	uint8_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 */
+		uint8_t schan;			/* starting channel */
+		uint8_t nchan;			/* number channels */
+		uint8_t maxtxpwr;		/* tx power cap */
+	} __packed band[10];			/* sub bands */
 } __packed;
 
-#define IEEE80211_CHALLENGE_LEN		128
+/*
+ * 802.11h Channel Switch Announcement (CSA).
+ */
+struct ieee80211_csa_ie {
+	uint8_t		csa_ie;		/* IEEE80211_ELEMID_CHANSWITCHANN */
+	uint8_t		csa_len;
+	uint8_t		csa_mode;		/* Channel Switch Mode */
+	uint8_t		csa_newchan;		/* New Channel Number */
+	uint8_t		csa_count;		/* Channel Switch Count */
+} __packed;
 
+/*
+ * Atheros advanced capability information element.
+ */
+struct ieee80211_ath_ie {
+	uint8_t		ath_id;			/* IEEE80211_ELEMID_VENDOR */
+	uint8_t		ath_len;		/* length in bytes */
+	uint8_t		ath_oui[3];		/* 0x00, 0x03, 0x7f */
+	uint8_t		ath_oui_type;		/* OUI type */
+	uint8_t		ath_oui_subtype;	/* OUI subtype */
+	uint8_t		ath_version;		/* spec revision */
+	uint8_t		ath_capability;		/* capability info */
+#define	ATHEROS_CAP_TURBO_PRIME		0x01	/* dynamic turbo--aka Turbo' */
+#define	ATHEROS_CAP_COMPRESSION		0x02	/* data compression */
+#define	ATHEROS_CAP_FAST_FRAME		0x04	/* fast (jumbo) frames */
+#define	ATHEROS_CAP_XR			0x08	/* Xtended Range support */
+#define	ATHEROS_CAP_AR			0x10	/* Advanded Radar support */
+#define	ATHEROS_CAP_BURST		0x20	/* Bursting - not negotiated */
+#define	ATHEROS_CAP_WME			0x40	/* CWMin tuning */
+#define	ATHEROS_CAP_BOOST		0x80	/* use turbo/!turbo mode */
+	uint8_t		ath_defkeyix[2];
+} __packed;
+
+/* rate set entries are in .5 Mb/s units, and potentially marked as basic */
 #define	IEEE80211_RATE_BASIC		0x80
 #define	IEEE80211_RATE_VAL		0x7f
 
@@ -441,16 +741,14 @@
 #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
-/* bits 3-6 reserved */
-#define	ATHEROS_CAP_BOOST		0x80
-
-#define	ATH_OUI			0x7f0300		/* Atheros OUI */
+#define	ATH_OUI			0x7f0300	/* Atheros OUI */
 #define	ATH_OUI_TYPE		0x01
-#define	ATH_OUI_VERSION		0x01
+#define	ATH_OUI_SUBTYPE		0x01
+#define	ATH_OUI_VERSION		0x00
+
+#define	BCM_OUI			0x4c9000	/* Broadcom OUI */
+#define	BCM_OUI_HTCAP		51		/* pre-draft HTCAP ie */
+#define	BCM_OUI_HTINFO		52		/* pre-draft HTINFO ie */
 
 #define	WPA_OUI			0xf25000
 #define	WPA_OUI_TYPE		0x01
@@ -502,17 +800,11 @@
  *	octet status[2]
  *	octet chal.id
  *	octet chal.length
- *	octet chal.text[253]
+ *	octet chal.text[253]		NB: 1-253 bytes
  */
 
-typedef u_int8_t *ieee80211_mgt_auth_t;
-
-#define	IEEE80211_AUTH_ALGORITHM(auth) \
-	((auth)[0] | ((auth)[1] << 8))
-#define	IEEE80211_AUTH_TRANSACTION(auth) \
-	((auth)[2] | ((auth)[3] << 8))
-#define	IEEE80211_AUTH_STATUS(auth) \
-	((auth)[4] | ((auth)[5] << 8))
+/* challenge length for shared key auth */
+#define IEEE80211_CHALLENGE_LEN		128
 
 #define	IEEE80211_AUTH_ALG_OPEN		0x0000
 #define	IEEE80211_AUTH_ALG_SHARED	0x0001
@@ -531,7 +823,11 @@
 };
 
 /*
- * Reason codes
+ * Reason and status codes.
+ *
+ * Reason codes are used in management frames to indicate why an
+ * action took place (e.g. on disassociation).  Status codes are
+ * used in management frames to indicate the result of an operation.
  *
  * Unlisted codes are reserved
  */
@@ -546,11 +842,20 @@
 	IEEE80211_REASON_NOT_ASSOCED		= 7,
 	IEEE80211_REASON_ASSOC_LEAVE		= 8,
 	IEEE80211_REASON_ASSOC_NOT_AUTHED	= 9,
-
-	IEEE80211_REASON_RSN_REQUIRED		= 11,
-	IEEE80211_REASON_RSN_INCONSISTENT	= 12,
-	IEEE80211_REASON_IE_INVALID		= 13,
-	IEEE80211_REASON_MIC_FAILURE		= 14,
+	IEEE80211_REASON_DISASSOC_PWRCAP_BAD	= 10,	/* 11h */
+	IEEE80211_REASON_DISASSOC_SUPCHAN_BAD	= 11,	/* 11h */
+	IEEE80211_REASON_IE_INVALID		= 13,	/* 11i */
+	IEEE80211_REASON_MIC_FAILURE		= 14,	/* 11i */
+	IEEE80211_REASON_4WAY_HANDSHAKE_TIMEOUT	= 15,	/* 11i */
+	IEEE80211_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,	/* 11i */
+	IEEE80211_REASON_IE_IN_4WAY_DIFFERS	= 17,	/* 11i */
+	IEEE80211_REASON_GROUP_CIPHER_INVALID	= 18,	/* 11i */
+	IEEE80211_REASON_PAIRWISE_CIPHER_INVALID= 19,	/* 11i */
+	IEEE80211_REASON_AKMP_INVALID		= 20,	/* 11i */
+	IEEE80211_REASON_UNSUPP_RSN_IE_VERSION	= 21,	/* 11i */
+	IEEE80211_REASON_INVALID_RSN_IE_CAP	= 22,	/* 11i */
+	IEEE80211_REASON_802_1X_AUTH_FAILED	= 23,	/* 11i */
+	IEEE80211_REASON_CIPHER_SUITE_REJECTED	= 24,	/* 11i */
 
 	IEEE80211_STATUS_SUCCESS		= 0,
 	IEEE80211_STATUS_UNSPECIFIED		= 1,
@@ -563,13 +868,21 @@
 	IEEE80211_STATUS_TIMEOUT		= 16,
 	IEEE80211_STATUS_TOOMANY		= 17,
 	IEEE80211_STATUS_BASIC_RATE		= 18,
-	IEEE80211_STATUS_SP_REQUIRED		= 19,
-	IEEE80211_STATUS_PBCC_REQUIRED		= 20,
-	IEEE80211_STATUS_CA_REQUIRED		= 21,
-	IEEE80211_STATUS_TOO_MANY_STATIONS	= 22,
-	IEEE80211_STATUS_RATES			= 23,
-	IEEE80211_STATUS_SHORTSLOT_REQUIRED	= 25,
-	IEEE80211_STATUS_DSSSOFDM_REQUIRED	= 26,
+	IEEE80211_STATUS_SP_REQUIRED		= 19,	/* 11b */
+	IEEE80211_STATUS_PBCC_REQUIRED		= 20,	/* 11b */
+	IEEE80211_STATUS_CA_REQUIRED		= 21,	/* 11b */
+	IEEE80211_STATUS_SPECMGMT_REQUIRED	= 22,	/* 11h */
+	IEEE80211_STATUS_PWRCAP_REQUIRED	= 23,	/* 11h */
+	IEEE80211_STATUS_SUPCHAN_REQUIRED	= 24,	/* 11h */
+	IEEE80211_STATUS_SHORTSLOT_REQUIRED	= 25,	/* 11g */
+	IEEE80211_STATUS_DSSSOFDM_REQUIRED	= 26,	/* 11g */
+	IEEE80211_STATUS_INVALID_IE		= 40,	/* 11i */
+	IEEE80211_STATUS_GROUP_CIPHER_INVALID	= 41,	/* 11i */
+	IEEE80211_STATUS_PAIRWISE_CIPHER_INVALID = 42,	/* 11i */
+	IEEE80211_STATUS_AKMP_INVALID		= 43,	/* 11i */
+	IEEE80211_STATUS_UNSUPP_RSN_IE_VERSION	= 44,	/* 11i */
+	IEEE80211_STATUS_INVALID_RSN_IE_CAP	= 45,	/* 11i */
+	IEEE80211_STATUS_CIPHER_SUITE_REJECTED	= 46,	/* 11i */
 };
 
 #define	IEEE80211_WEP_KEYLEN		5	/* 40bit */
@@ -636,4 +949,93 @@
 #define	IEEE80211_FRAG_MIN		256
 #define	IEEE80211_FRAG_MAX		2346
 
+/*
+ * Beacon interval (TU's).  Min+max come from WiFi requirements.
+ * As above, we treat default as implementation-dependent so
+ * define it elsewhere.
+ */
+#define	IEEE80211_BINTVAL_MAX	1000	/* max beacon interval (TU's) */
+#define	IEEE80211_BINTVAL_MIN	25	/* min beacon interval (TU's) */
+
+/*
+ * DTIM period (beacons).  Min+max are not really defined
+ * by the protocol but we want them publicly visible so
+ * define them here.
+ */
+#define	IEEE80211_DTIM_MAX	15	/* max DTIM period */
+#define	IEEE80211_DTIM_MIN	1	/* min DTIM period */
+
+/*
+ * Beacon miss threshold (beacons).  As for DTIM, we define
+ * them here to be publicly visible.  Note the max may be
+ * clamped depending on device capabilities.
+ */
+#define	IEEE80211_HWBMISS_MIN 	1
+#define	IEEE80211_HWBMISS_MAX 	255
+
+/*
+ * 802.11 frame duration definitions.
+ */
+
+struct ieee80211_duration {
+	uint16_t	d_rts_dur;
+	uint16_t	d_data_dur;
+	uint16_t	d_plcp_len;
+	uint8_t		d_residue;	/* unused octets in time slot */
+};
+
+/* One Time Unit (TU) is 1Kus = 1024 microseconds. */
+#define IEEE80211_DUR_TU		1024
+
+/* IEEE 802.11b durations for DSSS PHY in microseconds */
+#define IEEE80211_DUR_DS_LONG_PREAMBLE	144
+#define IEEE80211_DUR_DS_SHORT_PREAMBLE	72
+
+#define IEEE80211_DUR_DS_SLOW_PLCPHDR	48
+#define IEEE80211_DUR_DS_FAST_PLCPHDR	24
+#define IEEE80211_DUR_DS_SLOW_ACK	112
+#define IEEE80211_DUR_DS_FAST_ACK	56
+#define IEEE80211_DUR_DS_SLOW_CTS	112
+#define IEEE80211_DUR_DS_FAST_CTS	56
+
+#define IEEE80211_DUR_DS_SLOT		20
+#define IEEE80211_DUR_DS_SIFS		10
+#define IEEE80211_DUR_DS_PIFS	(IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SLOT)
+#define IEEE80211_DUR_DS_DIFS	(IEEE80211_DUR_DS_SIFS + \
+				 2 * IEEE80211_DUR_DS_SLOT)
+#define IEEE80211_DUR_DS_EIFS	(IEEE80211_DUR_DS_SIFS + \
+				 IEEE80211_DUR_DS_SLOW_ACK + \
+				 IEEE80211_DUR_DS_LONG_PREAMBLE + \
+				 IEEE80211_DUR_DS_SLOW_PLCPHDR + \
+				 IEEE80211_DUR_DIFS)
+
+/*
+ * Atheros fast-frame encapsulation format.
+ * FF max payload:
+ * 802.2 + FFHDR + HPAD + 802.3 + 802.2 + 1500 + SPAD + 802.3 + 802.2 + 1500:
+ *   8   +   4   +  4   +   14  +   8   + 1500 +  6   +   14  +   8   + 1500
+ * = 3066
+ */
+/* fast frame header is 32-bits */
+#define	ATH_FF_PROTO	0x0000003f	/* protocol */
+#define	ATH_FF_PROTO_S	0
+#define	ATH_FF_FTYPE	0x000000c0	/* frame type */
+#define	ATH_FF_FTYPE_S	6
+#define	ATH_FF_HLEN32	0x00000300	/* optional hdr length */
+#define	ATH_FF_HLEN32_S	8
+#define	ATH_FF_SEQNUM	0x001ffc00	/* sequence number */
+#define	ATH_FF_SEQNUM_S	10
+#define	ATH_FF_OFFSET	0xffe00000	/* offset to 2nd payload */
+#define	ATH_FF_OFFSET_S	21
+
+#define	ATH_FF_MAX_HDR_PAD	4
+#define	ATH_FF_MAX_SEP_PAD	6
+#define	ATH_FF_MAX_HDR		30
+
+#define	ATH_FF_PROTO_L2TUNNEL	0	/* L2 tunnel protocol */
+#define	ATH_FF_ETH_TYPE		0x88bd	/* Ether type for encapsulated frames */
+#define	ATH_FF_SNAP_ORGCODE_0	0x00
+#define	ATH_FF_SNAP_ORGCODE_1	0x03
+#define	ATH_FF_SNAP_ORGCODE_2	0x7f
+
 #endif /* _NET80211_IEEE80211_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_amrr.c
@@ -0,0 +1,164 @@
+/*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
+
+/*-
+ * Copyright (c) 2006
+ *	Damien Bergamini <damien.bergamini at free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_amrr.c,v 1.3 2007/06/11 03:36:54 sam Exp $");
+
+/*-
+ * Naive implementation of the Adaptive Multi Rate Retry algorithm:
+ *
+ * "IEEE 802.11 Rate Adaptation: A Practical Approach"
+ *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
+ *  INRIA Sophia - Projet Planete
+ *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_amrr.h>
+
+#define is_success(amn)	\
+	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
+#define is_failure(amn)	\
+	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
+#define is_enough(amn)		\
+	((amn)->amn_txcnt > 10)
+#define is_min_rate(ni)		\
+	((ni)->ni_txrate == 0)
+#define is_max_rate(ni)		\
+	((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1)
+#define increase_rate(ni)	\
+	((ni)->ni_txrate++)
+#define decrease_rate(ni)	\
+	((ni)->ni_txrate--)
+#define reset_cnt(amn)		\
+	do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0)
+
+void
+ieee80211_amrr_init(struct ieee80211_amrr *amrr,
+    struct ieee80211com *ic, int amin, int amax)
+{
+	/* XXX bounds check? */
+	amrr->amrr_min_success_threshold = amin;
+	amrr->amrr_max_success_threshold = amax;
+	amrr->amrr_ic = ic;
+}
+
+void
+ieee80211_amrr_node_init(struct ieee80211_amrr *amrr,
+    struct ieee80211_amrr_node *amn)
+{
+	amn->amn_success = 0;
+	amn->amn_recovery = 0;
+	amn->amn_txcnt = amn->amn_retrycnt = 0;
+	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
+}
+
+/*
+ * Update ni->ni_txrate.
+ */
+void
+ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
+    struct ieee80211_amrr_node *amn)
+{
+	int need_change = 0;
+
+	if (is_success(amn) && is_enough(amn)) {
+		amn->amn_success++;
+		if (amn->amn_success >= amn->amn_success_threshold &&
+		    !is_max_rate(ni)) {
+			amn->amn_recovery = 1;
+			amn->amn_success = 0;
+			increase_rate(ni);
+			IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
+			    "AMRR increasing rate %d (txcnt=%d "
+			    "retrycnt=%d)\n",
+			    ni->ni_rates.rs_rates[ni->ni_txrate] &
+				IEEE80211_RATE_VAL,
+			    amn->amn_txcnt, amn->amn_retrycnt);
+			need_change = 1;
+		} else {
+			amn->amn_recovery = 0;
+		}
+	} else if (is_failure(amn)) {
+		amn->amn_success = 0;
+		if (!is_min_rate(ni)) {
+			if (amn->amn_recovery) {
+				amn->amn_success_threshold *= 2;
+				if (amn->amn_success_threshold >
+				    amrr->amrr_max_success_threshold)
+					amn->amn_success_threshold =
+					    amrr->amrr_max_success_threshold;
+			} else {
+				amn->amn_success_threshold =
+				    amrr->amrr_min_success_threshold;
+			}
+			decrease_rate(ni);
+			IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
+			    "AMRR decreasing rate %d (txcnt=%d "
+			    "retrycnt=%d)\n",
+			    ni->ni_rates.rs_rates[ni->ni_txrate] &
+				IEEE80211_RATE_VAL,
+			    amn->amn_txcnt, amn->amn_retrycnt);
+			need_change = 1;
+		}
+		amn->amn_recovery = 0;
+	}
+
+	if (is_enough(amn) || need_change)
+		reset_cnt(amn);
+}
+
+/*
+ * Module glue.
+ */
+static int
+amrr_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		if (bootverbose)
+			printf("wlan_amrr: <AMRR Transmit Rate Control Algorithm>\n");
+		return 0;
+	case MOD_UNLOAD:
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t amrr_mod = {
+	"wlan_amrr",
+	amrr_modevent,
+	0
+};
+DECLARE_MODULE(wlan_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_amrr, 1);
+MODULE_DEPEND(wlan_amrr, wlan, 1, 1, 1);
Index: xform.h
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/xform.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/xform.h -L sys/opencrypto/xform.h -u -r1.1.1.1 -r1.2
--- sys/opencrypto/xform.h
+++ sys/opencrypto/xform.h
@@ -1,4 +1,4 @@
-/*	$FreeBSD: src/sys/opencrypto/xform.h,v 1.2 2005/01/07 02:29:16 imp Exp $	*/
+/*	$FreeBSD: src/sys/opencrypto/xform.h,v 1.4 2007/05/09 19:37:02 gnn Exp $	*/
 /*	$OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $	*/
 
 /*-
@@ -36,7 +36,7 @@
 	char *name;
 	u_int16_t keysize;
 	u_int16_t hashsize; 
-	u_int16_t authsize;
+	u_int16_t blocksize;
 	u_int16_t ctxsize;
 	void (*Init) (void *);
 	int  (*Update) (void *, u_int8_t *, u_int16_t);
@@ -81,13 +81,14 @@
 extern struct enc_xform enc_xform_skipjack;
 extern struct enc_xform enc_xform_rijndael128;
 extern struct enc_xform enc_xform_arc4;
+extern struct enc_xform enc_xform_camellia;
 
 extern struct auth_hash auth_hash_null;
 extern struct auth_hash auth_hash_key_md5;
 extern struct auth_hash auth_hash_key_sha1;
-extern struct auth_hash auth_hash_hmac_md5_96;
-extern struct auth_hash auth_hash_hmac_sha1_96;
-extern struct auth_hash auth_hash_hmac_ripemd_160_96;
+extern struct auth_hash auth_hash_hmac_md5;
+extern struct auth_hash auth_hash_hmac_sha1;
+extern struct auth_hash auth_hash_hmac_ripemd_160;
 extern struct auth_hash auth_hash_hmac_sha2_256;
 extern struct auth_hash auth_hash_hmac_sha2_384;
 extern struct auth_hash auth_hash_hmac_sha2_512;
Index: cryptosoft.h
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptosoft.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cryptosoft.h -L sys/opencrypto/cryptosoft.h -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cryptosoft.h
+++ sys/opencrypto/cryptosoft.h
@@ -1,4 +1,4 @@
-/*	$FreeBSD: src/sys/opencrypto/cryptosoft.h,v 1.2 2005/01/07 02:29:16 imp Exp $	*/
+/*	$FreeBSD: src/sys/opencrypto/cryptosoft.h,v 1.4 2007/03/21 03:42:51 sam Exp $	*/
 /*	$OpenBSD: cryptosoft.h,v 1.10 2002/04/22 23:10:09 deraadt Exp $	*/
 
 /*-
@@ -32,7 +32,8 @@
 		struct {
 			u_int8_t	 *SW_ictx;
 			u_int8_t	 *SW_octx;
-			u_int32_t	 SW_klen;
+			u_int16_t	 SW_klen;
+			u_int16_t	 SW_mlen;
 			struct auth_hash *SW_axf;
 		} SWCR_AUTH;
 		struct {
@@ -48,6 +49,7 @@
 #define sw_ictx		SWCR_UN.SWCR_AUTH.SW_ictx
 #define sw_octx		SWCR_UN.SWCR_AUTH.SW_octx
 #define sw_klen		SWCR_UN.SWCR_AUTH.SW_klen
+#define sw_mlen		SWCR_UN.SWCR_AUTH.SW_mlen
 #define sw_axf		SWCR_UN.SWCR_AUTH.SW_axf
 #define sw_kschedule	SWCR_UN.SWCR_ENC.SW_kschedule
 #define sw_exf		SWCR_UN.SWCR_ENC.SW_exf
@@ -58,8 +60,8 @@
 };
 
 #ifdef _KERNEL
-extern u_int8_t hmac_ipad_buffer[64];
-extern u_int8_t hmac_opad_buffer[64];
+extern u_int8_t hmac_ipad_buffer[];
+extern u_int8_t hmac_opad_buffer[];
 #endif /* _KERNEL */
 
 #endif /* _CRYPTO_CRYPTO_H_ */
Index: cryptodev.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptodev.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/opencrypto/cryptodev.c -L sys/opencrypto/cryptodev.c -u -r1.2 -r1.3
--- sys/opencrypto/cryptodev.c
+++ sys/opencrypto/cryptodev.c
@@ -2,6 +2,7 @@
 
 /*-
  * Copyright (c) 2001 Theo de Raadt
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -32,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.25.2.2 2006/03/01 21:40:14 wkoszek Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.35 2007/10/08 20:08:34 kib Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -50,6 +51,7 @@
 #include <sys/kernel.h>
 #include <sys/module.h>
 #include <sys/fcntl.h>
+#include <sys/bus.h>
 
 #include <opencrypto/cryptodev.h>
 #include <opencrypto/xform.h>
@@ -71,7 +73,6 @@
 
 	caddr_t		mackey;
 	int		mackeylen;
-	u_char		tmp_mac[CRYPTO_MAX_MAC_LEN];
 
 	struct iovec	iovec;
 	struct uio	uio;
@@ -114,6 +115,7 @@
 static	int cryptodev_op(struct csession *, struct crypt_op *,
 			struct ucred *, struct thread *td);
 static	int cryptodev_key(struct crypt_kop *);
+static	int cryptodev_find(struct crypt_find_op *);
 
 static int
 cryptof_rw(
@@ -127,6 +129,22 @@
 	return (EIO);
 }
 
+/*
+ * Check a crypto identifier to see if it requested
+ * a software device/driver.  This can be done either
+ * by device name/class or through search constraints.
+ */
+static int
+checkforsoftware(int crid)
+{
+	if (crid & CRYPTOCAP_F_SOFTWARE)
+		return EINVAL;		/* XXX */
+	if ((crid & CRYPTOCAP_F_HARDWARE) == 0 &&
+	    (crypto_getcaps(crid) & CRYPTOCAP_F_HARDWARE) == 0)
+		return EINVAL;		/* XXX */
+	return 0;
+}
+
 /* ARGSUSED */
 static int
 cryptof_ioctl(
@@ -136,6 +154,7 @@
 	struct ucred *active_cred,
 	struct thread *td)
 {
+#define	SES2(p)	((struct session2_op *)p)
 	struct cryptoini cria, crie;
 	struct fcrypt *fcr = fp->f_data;
 	struct csession *cse;
@@ -143,16 +162,14 @@
 	struct crypt_op *cop;
 	struct enc_xform *txform = NULL;
 	struct auth_hash *thash = NULL;
+	struct crypt_kop *kop;
 	u_int64_t sid;
 	u_int32_t ses;
-	int error = 0;
+	int error = 0, crid;
 
-	/*
-	 * XXX: Not sure Giant is needed, but better safe than sorry
-	 */
-	mtx_lock(&Giant);
 	switch (cmd) {
 	case CIOCGSESSION:
+	case CIOCGSESSION2:
 		sop = (struct session_op *)data;
 		switch (sop->cipher) {
 		case 0:
@@ -181,8 +198,10 @@
 		case CRYPTO_ARC4:
 			txform = &enc_xform_arc4;
 			break;
+ 		case CRYPTO_CAMELLIA_CBC:
+ 			txform = &enc_xform_camellia;
+ 			break;
 		default:
-			mtx_unlock(&Giant);
 			return (EINVAL);
 		}
 
@@ -190,25 +209,22 @@
 		case 0:
 			break;
 		case CRYPTO_MD5_HMAC:
-			thash = &auth_hash_hmac_md5_96;
+			thash = &auth_hash_hmac_md5;
 			break;
 		case CRYPTO_SHA1_HMAC:
-			thash = &auth_hash_hmac_sha1_96;
+			thash = &auth_hash_hmac_sha1;
 			break;
-		case CRYPTO_SHA2_HMAC:
-			if (sop->mackeylen == auth_hash_hmac_sha2_256.keysize)
-				thash = &auth_hash_hmac_sha2_256;
-			else if (sop->mackeylen == auth_hash_hmac_sha2_384.keysize)
-				thash = &auth_hash_hmac_sha2_384;
-			else if (sop->mackeylen == auth_hash_hmac_sha2_512.keysize)
-				thash = &auth_hash_hmac_sha2_512;
-			else {
-				mtx_unlock(&Giant);
-				return (EINVAL);
-			}
+		case CRYPTO_SHA2_256_HMAC:
+			thash = &auth_hash_hmac_sha2_256;
+			break;
+		case CRYPTO_SHA2_384_HMAC:
+			thash = &auth_hash_hmac_sha2_384;
+			break;
+		case CRYPTO_SHA2_512_HMAC:
+			thash = &auth_hash_hmac_sha2_512;
 			break;
 		case CRYPTO_RIPEMD160_HMAC:
-			thash = &auth_hash_hmac_ripemd_160_96;
+			thash = &auth_hash_hmac_ripemd_160;
 			break;
 #ifdef notdef
 		case CRYPTO_MD5:
@@ -222,7 +238,6 @@
 			thash = &auth_hash_null;
 			break;
 		default:
-			mtx_unlock(&Giant);
 			return (EINVAL);
 		}
 
@@ -264,7 +279,15 @@
 			}
 		}
 
-		error = crypto_newsession(&sid, (txform ? &crie : &cria), 1);
+		/* NB: CIOGSESSION2 has the crid */
+		if (cmd == CIOCGSESSION2) {
+			crid = SES2(sop)->crid;
+			error = checkforsoftware(crid);
+			if (error)
+				goto bail;
+		} else
+			crid = CRYPTOCAP_F_HARDWARE;
+		error = crypto_newsession(&sid, (txform ? &crie : &cria), crid);
 		if (error)
 			goto bail;
 
@@ -278,7 +301,10 @@
 			goto bail;
 		}
 		sop->ses = cse->ses;
-
+		if (cmd == CIOCGSESSION2) {
+			/* return hardware/driver id */
+			SES2(sop)->crid = CRYPTO_SESID2HID(cse->sid);
+		}
 bail:
 		if (error) {
 			if (crie.cri_key)
@@ -290,33 +316,53 @@
 	case CIOCFSESSION:
 		ses = *(u_int32_t *)data;
 		cse = csefind(fcr, ses);
-		if (cse == NULL) {
-			mtx_unlock(&Giant);
+		if (cse == NULL)
 			return (EINVAL);
-		}
 		csedelete(fcr, cse);
 		error = csefree(cse);
 		break;
 	case CIOCCRYPT:
 		cop = (struct crypt_op *)data;
 		cse = csefind(fcr, cop->ses);
-		if (cse == NULL) {
-			mtx_unlock(&Giant);
+		if (cse == NULL)
 			return (EINVAL);
-		}
 		error = cryptodev_op(cse, cop, active_cred, td);
 		break;
 	case CIOCKEY:
-		error = cryptodev_key((struct crypt_kop *)data);
+	case CIOCKEY2:
+		if (!crypto_userasymcrypto)
+			return (EPERM);		/* XXX compat? */
+		mtx_lock(&Giant);
+		kop = (struct crypt_kop *)data;
+		if (cmd == CIOCKEY) {
+			/* NB: crypto core enforces s/w driver use */
+			kop->crk_crid =
+			    CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+		}
+		error = cryptodev_key(kop);
+		mtx_unlock(&Giant);
 		break;
 	case CIOCASYMFEAT:
-		error = crypto_getfeat((int *)data);
+		if (!crypto_userasymcrypto) {
+			/*
+			 * NB: if user asym crypto operations are
+			 * not permitted return "no algorithms"
+			 * so well-behaved applications will just
+			 * fallback to doing them in software.
+			 */
+			*(int *)data = 0;
+		} else
+			error = crypto_getfeat((int *)data);
+		break;
+	case CIOCFINDDEV:
+		error = cryptodev_find((struct crypt_find_op *)data);
 		break;
 	default:
 		error = EINVAL;
+		break;
 	}
-	mtx_unlock(&Giant);
 	return (error);
+#undef SES2
 }
 
 static int cryptodev_cb(void *);
@@ -349,7 +395,10 @@
 	cse->uio.uio_rw = UIO_WRITE;
 	cse->uio.uio_td = td;
 	cse->uio.uio_iov[0].iov_len = cop->len;
-	cse->uio.uio_iov[0].iov_base = malloc(cop->len, M_XDATA, M_WAITOK);
+	if (cse->thash)
+		cse->uio.uio_iov[0].iov_len += cse->thash->hashsize;
+	cse->uio.uio_iov[0].iov_base = malloc(cse->uio.uio_iov[0].iov_len,
+	    M_XDATA, M_WAITOK);
 
 	crp = crypto_getreq((cse->txform != NULL) + (cse->thash != NULL));
 	if (crp == NULL) {
@@ -376,7 +425,7 @@
 	if (crda) {
 		crda->crd_skip = 0;
 		crda->crd_len = cop->len;
-		crda->crd_inject = 0;	/* ??? */
+		crda->crd_inject = cop->len;
 
 		crda->crd_alg = cse->mac;
 		crda->crd_key = cse->mackey;
@@ -426,12 +475,9 @@
 		crde->crd_len -= cse->txform->blocksize;
 	}
 
-	if (cop->mac) {
-		if (crda == NULL) {
-			error = EINVAL;
-			goto bail;
-		}
-		crp->crp_mac=cse->tmp_mac;
+	if (cop->mac && crda == NULL) {
+		error = EINVAL;
+		goto bail;
 	}
 
 	/*
@@ -465,7 +511,8 @@
 		goto bail;
 
 	if (cop->mac &&
-	    (error = copyout(crp->crp_mac, cop->mac, cse->thash->authsize)))
+	    (error = copyout((caddr_t)cse->uio.uio_iov[0].iov_base + cop->len,
+	    cop->mac, cse->thash->hashsize)))
 		goto bail;
 
 bail:
@@ -482,12 +529,16 @@
 {
 	struct cryptop *crp = (struct cryptop *) op;
 	struct csession *cse = (struct csession *)crp->crp_opaque;
+	int error;
 
-	cse->error = crp->crp_etype;
-	if (crp->crp_etype == EAGAIN)
-		return crypto_dispatch(crp);
+	error = crp->crp_etype;
+	if (error == EAGAIN)
+		error = crypto_dispatch(crp);
 	mtx_lock(&cse->lock);
-	wakeup_one(crp);
+	if (error != 0 || (crp->crp_flags & CRYPTO_F_DONE)) {
+		cse->error = error;
+		wakeup_one(crp);
+	}
 	mtx_unlock(&cse->lock);
 	return (0);
 }
@@ -497,7 +548,7 @@
 {
 	struct cryptkop *krp = (struct cryptkop *) op;
 
-	wakeup(krp);
+	wakeup_one(krp);
 	return (0);
 }
 
@@ -539,19 +590,23 @@
 		return (EINVAL);
 	}
 
-	krp = (struct cryptkop *)malloc(sizeof *krp, M_XDATA, M_WAITOK);
+	krp = (struct cryptkop *)malloc(sizeof *krp, M_XDATA, M_WAITOK|M_ZERO);
 	if (!krp)
 		return (ENOMEM);
-	bzero(krp, sizeof *krp);
 	krp->krp_op = kop->crk_op;
 	krp->krp_status = kop->crk_status;
 	krp->krp_iparams = kop->crk_iparams;
 	krp->krp_oparams = kop->crk_oparams;
+	krp->krp_crid = kop->crk_crid;
 	krp->krp_status = 0;
 	krp->krp_callback = (int (*) (struct cryptkop *)) cryptodevkey_cb;
 
-	for (i = 0; i < CRK_MAXPARAM; i++)
+	for (i = 0; i < CRK_MAXPARAM; i++) {
+		if (kop->crk_param[i].crp_nbits > 65536)
+			/* Limit is the same as in OpenBSD */
+			goto fail;
 		krp->krp_param[i].crp_nbits = kop->crk_param[i].crp_nbits;
+	}
 	for (i = 0; i < krp->krp_iparams + krp->krp_oparams; i++) {
 		size = (krp->krp_param[i].crp_nbits + 7) / 8;
 		if (size == 0)
@@ -573,6 +628,7 @@
 		goto fail;
 	}
 	
+	kop->crk_crid = krp->krp_crid;		/* device that did the work */
 	if (krp->krp_status != 0) {
 		error = krp->krp_status;
 		goto fail;
@@ -599,6 +655,25 @@
 	return (error);
 }
 
+static int
+cryptodev_find(struct crypt_find_op *find)
+{
+	device_t dev;
+
+	if (find->crid != -1) {
+		dev = crypto_find_device_byhid(find->crid);
+		if (dev == NULL)
+			return (ENOENT);
+		strlcpy(find->name, device_get_nameunit(dev),
+		    sizeof(find->name));
+	} else {
+		find->crid = crypto_find_driver(find->name);
+		if (find->crid == -1)
+			return (ENOENT);
+	}
+	return (0);
+}
+
 /* ARGSUSED */
 static int
 cryptof_poll(
@@ -765,13 +840,21 @@
 			return (error);
 		}
 		/* falloc automatically provides an extra reference to 'f'. */
+		FILE_LOCK(f);
 		f->f_flag = FREAD | FWRITE;
 		f->f_type = DTYPE_CRYPTO;
-		f->f_ops = &cryptofops;
 		f->f_data = fcr;
+		f->f_ops = &cryptofops;
+		FILE_UNLOCK(f);
 		*(u_int32_t *)data = fd;
 		fdrop(f, td);
 		break;
+	case CRIOFINDDEV:
+		error = cryptodev_find((struct crypt_find_op *)data);
+		break;
+	case CRIOASYMFEAT:
+		error = crypto_getfeat((int *)data);
+		break;
 	default:
 		error = EINVAL;
 		break;
--- /dev/null
+++ sys/opencrypto/cryptodev_if.m
@@ -0,0 +1,55 @@
+#-
+# Copyright (c) 2006, Sam Leffler
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY 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/opencrypto/cryptodev_if.m,v 1.1 2007/03/21 03:43:33 sam Exp $
+#
+
+#include <sys/malloc.h>
+#include <opencrypto/cryptodev.h>
+
+INTERFACE cryptodev;
+
+METHOD int newsession {
+	device_t	dev;
+	uint32_t	*sid;
+	struct cryptoini *cri;
+};
+
+METHOD int freesession {
+	device_t	dev;
+	uint64_t	sid;
+};
+
+METHOD int process {
+	device_t	dev;
+	struct cryptop	*op;
+	int		flags;
+};
+
+METHOD int kprocess {
+	device_t	dev;
+	struct cryptkop	*op;
+	int		flags;
+};
Index: cryptodev.h
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptodev.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cryptodev.h -L sys/opencrypto/cryptodev.h -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cryptodev.h
+++ sys/opencrypto/cryptodev.h
@@ -1,8 +1,9 @@
-/*	$FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.10 2005/01/07 02:29:16 imp Exp $	*/
+/*	$FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.25 2007/05/09 19:37:02 gnn Exp $	*/
 /*	$OpenBSD: cryptodev.h,v 1.31 2002/06/11 11:14:29 beck Exp $	*/
 
 /*-
  * The author of this code is Angelos D. Keromytis (angelos at cis.upenn.edu)
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
  *
  * This code was written by Angelos D. Keromytis in Athens, Greece, in
  * February 2000. Network Security Technologies Inc. (NSTI) kindly
@@ -61,22 +62,43 @@
 #define CRYPTO_DRIVERS_INITIAL	4
 #define CRYPTO_SW_SESSIONS	32
 
+/* Hash values */
+#define	NULL_HASH_LEN		16
+#define	MD5_HASH_LEN		16
+#define	SHA1_HASH_LEN		20
+#define	RIPEMD160_HASH_LEN	20
+#define	SHA2_256_HASH_LEN	32
+#define	SHA2_384_HASH_LEN	48
+#define	SHA2_512_HASH_LEN	64
+#define	MD5_KPDK_HASH_LEN	16
+#define	SHA1_KPDK_HASH_LEN	20
+/* Maximum hash algorithm result length */
+#define	HASH_MAX_LEN		SHA2_512_HASH_LEN /* Keep this updated */
+
 /* HMAC values */
-#define HMAC_BLOCK_LEN		64
-#define HMAC_IPAD_VAL		0x36
-#define HMAC_OPAD_VAL		0x5C
+#define	NULL_HMAC_BLOCK_LEN		64
+#define	MD5_HMAC_BLOCK_LEN		64
+#define	SHA1_HMAC_BLOCK_LEN		64
+#define	RIPEMD160_HMAC_BLOCK_LEN	64
+#define	SHA2_256_HMAC_BLOCK_LEN		64
+#define	SHA2_384_HMAC_BLOCK_LEN		128
+#define	SHA2_512_HMAC_BLOCK_LEN		128
+/* Maximum HMAC block length */
+#define	HMAC_MAX_BLOCK_LEN		SHA2_512_HMAC_BLOCK_LEN /* Keep this updated */
+#define	HMAC_IPAD_VAL			0x36
+#define	HMAC_OPAD_VAL			0x5C
 
 /* Encryption algorithm block sizes */
+#define NULL_BLOCK_LEN		4
 #define DES_BLOCK_LEN		8
 #define DES3_BLOCK_LEN		8
 #define BLOWFISH_BLOCK_LEN	8
 #define SKIPJACK_BLOCK_LEN	8
 #define CAST128_BLOCK_LEN	8
 #define RIJNDAEL128_BLOCK_LEN	16
-#define EALG_MAX_BLOCK_LEN	16 /* Keep this updated */
-
-/* Maximum hash algorithm result length */
-#define AALG_MAX_RESULT_LEN	64 /* Keep this updated */
+#define AES_BLOCK_LEN		RIJNDAEL128_BLOCK_LEN
+#define CAMELLIA_BLOCK_LEN	16
+#define EALG_MAX_BLOCK_LEN	AES_BLOCK_LEN /* Keep this updated */
 
 #define	CRYPTO_ALGORITHM_MIN	1
 #define CRYPTO_DES_CBC		1
@@ -94,17 +116,32 @@
 #define CRYPTO_ARC4		12
 #define	CRYPTO_MD5		13
 #define	CRYPTO_SHA1		14
-#define	CRYPTO_SHA2_HMAC	15
-#define CRYPTO_NULL_HMAC	16
-#define CRYPTO_NULL_CBC		17
-#define CRYPTO_DEFLATE_COMP	18 /* Deflate compression algorithm */
-#define CRYPTO_ALGORITHM_MAX	18 /* Keep updated - see below */
+#define	CRYPTO_NULL_HMAC	15
+#define	CRYPTO_NULL_CBC		16
+#define	CRYPTO_DEFLATE_COMP	17 /* Deflate compression algorithm */
+#define	CRYPTO_SHA2_256_HMAC	18
+#define	CRYPTO_SHA2_384_HMAC	19
+#define	CRYPTO_SHA2_512_HMAC	20
+#define CRYPTO_CAMELLIA_CBC	21
+#define	CRYPTO_ALGORITHM_MAX	21 /* Keep updated - see below */
 
 /* Algorithm flags */
 #define	CRYPTO_ALG_FLAG_SUPPORTED	0x01 /* Algorithm is supported */
 #define	CRYPTO_ALG_FLAG_RNG_ENABLE	0x02 /* Has HW RNG for DH/DSA */
 #define	CRYPTO_ALG_FLAG_DSA_SHA		0x04 /* Can do SHA on msg */
 
+/*
+ * Crypto driver/device flags.  They can set in the crid
+ * parameter when creating a session or submitting a key
+ * op to affect the device/driver assigned.  If neither
+ * of these are specified then the crid is assumed to hold
+ * the driver id of an existing (and suitable) device that
+ * must be used to satisfy the request.
+ */
+#define CRYPTO_FLAG_HARDWARE	0x01000000	/* hardware accelerated */
+#define CRYPTO_FLAG_SOFTWARE	0x02000000	/* software implementation */
+
+/* NB: deprecated */
 struct session_op {
 	u_int32_t	cipher;		/* ie. CRYPTO_DES_CBC */
 	u_int32_t	mac;		/* ie. CRYPTO_MD5_HMAC */
@@ -117,6 +154,20 @@
   	u_int32_t	ses;		/* returns: session # */ 
 };
 
+struct session2_op {
+	u_int32_t	cipher;		/* ie. CRYPTO_DES_CBC */
+	u_int32_t	mac;		/* ie. CRYPTO_MD5_HMAC */
+
+	u_int32_t	keylen;		/* cipher key */
+	caddr_t		key;
+	int		mackeylen;	/* mac key */
+	caddr_t		mackey;
+
+  	u_int32_t	ses;		/* returns: session # */ 
+	int		crid;		/* driver id + flags (rw) */
+	int		pad[4];		/* for future expansion */
+};
+
 struct crypt_op {
 	u_int32_t	ses;
 	u_int16_t	op;		/* i.e. COP_ENCRYPT */
@@ -130,7 +181,15 @@
 	caddr_t		iv;
 };
 
-#define CRYPTO_MAX_MAC_LEN	20
+/*
+ * Parameters for looking up a crypto driver/device by
+ * device name or by id.  The latter are returned for
+ * created sessions (crid) and completed key operations.
+ */
+struct crypt_find_op {
+	int		crid;		/* driver id + flags */
+	char		name[32];	/* device/driver name */
+};
 
 /* bignum parameter, in packed bytes, ... */
 struct crparam {
@@ -145,7 +204,7 @@
 	u_int		crk_status;	/* return status */
 	u_short		crk_iparams;	/* # of input parameters */
 	u_short		crk_oparams;	/* # of output parameters */
-	u_int		crk_pad1;
+	u_int		crk_crid;	/* NB: only used by CIOCKEY2 (rw) */
 	struct crparam	crk_param[CRK_MAXPARAM];
 };
 #define	CRK_ALGORITM_MIN	0
@@ -167,14 +226,18 @@
  * Please use F_SETFD against the cloned descriptor.
  */
 #define	CRIOGET		_IOWR('c', 100, u_int32_t)
+#define	CRIOASYMFEAT	CIOCASYMFEAT
+#define	CRIOFINDDEV	CIOCFINDDEV
 
 /* the following are done against the cloned descriptor */
 #define	CIOCGSESSION	_IOWR('c', 101, struct session_op)
 #define	CIOCFSESSION	_IOW('c', 102, u_int32_t)
 #define CIOCCRYPT	_IOWR('c', 103, struct crypt_op)
 #define CIOCKEY		_IOWR('c', 104, struct crypt_kop)
-
 #define CIOCASYMFEAT	_IOR('c', 105, u_int32_t)
+#define	CIOCGSESSION2	_IOWR('c', 106, struct session2_op)
+#define	CIOCKEY2	_IOWR('c', 107, struct crypt_kop)
+#define	CIOCFINDDEV	_IOWR('c', 108, struct crypt_find_op)
 
 struct cryptotstat {
 	struct timespec	acc;		/* total accumulated time */
@@ -209,7 +272,8 @@
 struct cryptoini {
 	int		cri_alg;	/* Algorithm to use */
 	int		cri_klen;	/* Key length, in bits */
-	int		cri_rnd;	/* Algorithm rounds, where relevant */
+	int		cri_mlen;	/* Number of bytes we want from the
+					   entire hash. 0 means all. */
 	caddr_t		cri_key;	/* key to use */
 	u_int8_t	cri_iv[EALG_MAX_BLOCK_LEN];	/* IV to use */
 	struct cryptoini *cri_next;
@@ -233,7 +297,6 @@
 	struct cryptoini	CRD_INI; /* Initialization/context data */
 #define crd_iv		CRD_INI.cri_iv
 #define crd_key		CRD_INI.cri_key
-#define crd_rnd		CRD_INI.cri_rnd
 #define crd_alg		CRD_INI.cri_alg
 #define crd_klen	CRD_INI.cri_klen
 
@@ -274,7 +337,6 @@
 
 	int (*crp_callback)(struct cryptop *); /* Callback function */
 
-	caddr_t		crp_mac;
 	struct bintime	crp_tstamp;	/* performance time stamp */
 };
 
@@ -297,40 +359,12 @@
 	u_int		krp_status;	/* return status */
 	u_short		krp_iparams;	/* # of input parameters */
 	u_short		krp_oparams;	/* # of output parameters */
+	u_int		krp_crid;	/* desired device, etc. */
 	u_int32_t	krp_hid;
 	struct crparam	krp_param[CRK_MAXPARAM];	/* kvm */
 	int		(*krp_callback)(struct cryptkop *);
 };
 
-/* Crypto capabilities structure */
-struct cryptocap {
-	u_int32_t	cc_sessions;
-
-	/*
-	 * Largest possible operator length (in bits) for each type of
-	 * encryption algorithm.
-	 */
-	u_int16_t	cc_max_op_len[CRYPTO_ALGORITHM_MAX + 1];
-
-	u_int8_t	cc_alg[CRYPTO_ALGORITHM_MAX + 1];
-
-	u_int8_t	cc_kalg[CRK_ALGORITHM_MAX + 1];
-
-	u_int8_t	cc_flags;
-	u_int8_t	cc_qblocked;		/* symmetric q blocked */
-	u_int8_t	cc_kqblocked;		/* asymmetric q blocked */
-#define CRYPTOCAP_F_CLEANUP	0x01		/* needs resource cleanup */
-#define CRYPTOCAP_F_SOFTWARE	0x02		/* software implementation */
-#define CRYPTOCAP_F_SYNC	0x04		/* operates synchronously */
-
-	void		*cc_arg;		/* callback argument */
-	int		(*cc_newsession)(void*, u_int32_t*, struct cryptoini*);
-	int		(*cc_process)(void*, struct cryptop *, int);
-	int		(*cc_freesession)(void*, u_int64_t);
-	void		*cc_karg;		/* callback argument */
-	int		(*cc_kprocess) (void*, struct cryptkop *, int);
-};
-
 /*
  * Session ids are 64 bits.  The lower 32 bits contain a "local id" which
  * is a driver-private session identifier.  The upper 32 bits contain a
@@ -338,24 +372,24 @@
  * a copy of the driver's capabilities that can be used by client code to
  * optimize operation.
  */
-#define	CRYPTO_SESID2HID(_sid)	(((_sid) >> 32) & 0xffffff)
-#define	CRYPTO_SESID2CAPS(_sid)	(((_sid) >> 56) & 0xff)
+#define	CRYPTO_SESID2HID(_sid)	(((_sid) >> 32) & 0x00ffffff)
+#define	CRYPTO_SESID2CAPS(_sid)	(((_sid) >> 32) & 0xff000000)
 #define	CRYPTO_SESID2LID(_sid)	(((u_int32_t) (_sid)) & 0xffffffff)
 
 MALLOC_DECLARE(M_CRYPTO_DATA);
 
 extern	int crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard);
 extern	int crypto_freesession(u_int64_t sid);
-extern	int32_t crypto_get_driverid(u_int32_t flags);
+#define CRYPTOCAP_F_HARDWARE	CRYPTO_FLAG_HARDWARE
+#define CRYPTOCAP_F_SOFTWARE	CRYPTO_FLAG_SOFTWARE
+#define CRYPTOCAP_F_SYNC	0x04000000	/* operates synchronously */
+extern	int32_t crypto_get_driverid(device_t dev, int flags);
+extern	int crypto_find_driver(const char *);
+extern	device_t crypto_find_device_byhid(int hid);
+extern	int crypto_getcaps(int hid);
 extern	int crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
-	    u_int32_t flags,
-	    int (*newses)(void*, u_int32_t*, struct cryptoini*),
-	    int (*freeses)(void*, u_int64_t),
-	    int (*process)(void*, struct cryptop *, int),
-	    void *arg);
-extern	int crypto_kregister(u_int32_t, int, u_int32_t,
-	    int (*)(void*, struct cryptkop *, int),
-	    void *arg);
+	    u_int32_t flags);
+extern	int crypto_kregister(u_int32_t, int, u_int32_t);
 extern	int crypto_unregister(u_int32_t driverid, int alg);
 extern	int crypto_unregister_all(u_int32_t driverid);
 extern	int crypto_dispatch(struct cryptop *crp);
@@ -384,5 +418,14 @@
 extern	void cuio_copydata(struct uio* uio, int off, int len, caddr_t cp);
 extern	void cuio_copyback(struct uio* uio, int off, int len, caddr_t cp);
 extern	struct iovec *cuio_getptr(struct uio *uio, int loc, int *off);
+extern	int cuio_apply(struct uio *uio, int off, int len,
+	    int (*f)(void *, void *, u_int), void *arg);
+
+extern	void crypto_copyback(int flags, caddr_t buf, int off, int size,
+	    caddr_t in);
+extern	void crypto_copydata(int flags, caddr_t buf, int off, int size,
+	    caddr_t out);
+extern	int crypto_apply(int flags, caddr_t buf, int off, int len,
+	    int (*f)(void *, void *, u_int), void *arg);
 #endif /* _KERNEL */
 #endif /* _CRYPTO_CRYPTO_H_ */
Index: criov.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/criov.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/criov.c -L sys/opencrypto/criov.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/criov.c
+++ sys/opencrypto/criov.c
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/criov.c,v 1.3 2005/01/07 02:29:16 imp Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/criov.c,v 1.5 2006/06/04 22:15:13 pjd Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -36,10 +36,28 @@
 #include <sys/errno.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/mbuf.h>
 #include <sys/uio.h>
 
 #include <opencrypto/cryptodev.h>
 
+/*
+ * This macro is only for avoiding code duplication, as we need to skip
+ * given number of bytes in the same way in three functions below.
+ */
+#define	CUIO_SKIP()	do {						\
+	KASSERT(off >= 0, ("%s: off %d < 0", __func__, off));		\
+	KASSERT(len >= 0, ("%s: len %d < 0", __func__, len));		\
+	while (off > 0) {						\
+		KASSERT(iol >= 0, ("%s: empty in skip", __func__));	\
+		if (off < iov->iov_len)					\
+			break;						\
+		off -= iov->iov_len;					\
+		iol--;							\
+		iov++;							\
+	}								\
+} while (0)
+
 void
 cuio_copydata(struct uio* uio, int off, int len, caddr_t cp)
 {
@@ -47,22 +65,9 @@
 	int iol = uio->uio_iovcnt;
 	unsigned count;
 
-	if (off < 0)
-		panic("cuio_copydata: off %d < 0", off);
-	if (len < 0)
-		panic("cuio_copydata: len %d < 0", len);
-	while (off > 0) {
-		if (iol == 0)
-			panic("iov_copydata: empty in skip");
-		if (off < iov->iov_len)
-			break;
-		off -= iov->iov_len;
-		iol--;
-		iov++;
-	}
+	CUIO_SKIP();
 	while (len > 0) {
-		if (iol == 0)
-			panic("cuio_copydata: empty");
+		KASSERT(iol >= 0, ("%s: empty", __func__));
 		count = min(iov->iov_len - off, len);
 		bcopy(((caddr_t)iov->iov_base) + off, cp, count);
 		len -= count;
@@ -80,22 +85,9 @@
 	int iol = uio->uio_iovcnt;
 	unsigned count;
 
-	if (off < 0)
-		panic("cuio_copyback: off %d < 0", off);
-	if (len < 0)
-		panic("cuio_copyback: len %d < 0", len);
-	while (off > 0) {
-		if (iol == 0)
-			panic("cuio_copyback: empty in skip");
-		if (off < iov->iov_len)
-			break;
-		off -= iov->iov_len;
-		iol--;
-		iov++;
-	}
+	CUIO_SKIP();
 	while (len > 0) {
-		if (iol == 0)
-			panic("uio_copyback: empty");
+		KASSERT(iol >= 0, ("%s: empty", __func__));
 		count = min(iov->iov_len - off, len);
 		bcopy(cp, ((caddr_t)iov->iov_base) + off, count);
 		len -= count;
@@ -137,3 +129,70 @@
 
 	return (NULL);
 }
+
+/*
+ * Apply function f to the data in an iovec list starting "off" bytes from
+ * the beginning, continuing for "len" bytes.
+ */
+int
+cuio_apply(struct uio *uio, int off, int len, int (*f)(void *, void *, u_int),
+    void *arg)
+{
+	struct iovec *iov = uio->uio_iov;
+	int iol = uio->uio_iovcnt;
+	unsigned count;
+	int rval;
+
+	CUIO_SKIP();
+	while (len > 0) {
+		KASSERT(iol >= 0, ("%s: empty", __func__));
+		count = min(iov->iov_len - off, len);
+		rval = (*f)(arg, ((caddr_t)iov->iov_base) + off, count);
+		if (rval)
+			return (rval);
+		len -= count;
+		off = 0;
+		iol--;
+		iov++;
+	}
+	return (0);
+}
+
+void
+crypto_copyback(int flags, caddr_t buf, int off, int size, caddr_t in)
+{
+
+	if ((flags & CRYPTO_F_IMBUF) != 0)
+		m_copyback((struct mbuf *)buf, off, size, in);
+	else if ((flags & CRYPTO_F_IOV) != 0)
+		cuio_copyback((struct uio *)buf, off, size, in);
+	else
+		bcopy(in, buf + off, size);
+}
+
+void
+crypto_copydata(int flags, caddr_t buf, int off, int size, caddr_t out)
+{
+
+	if ((flags & CRYPTO_F_IMBUF) != 0)
+		m_copydata((struct mbuf *)buf, off, size, out);
+	else if ((flags & CRYPTO_F_IOV) != 0)
+		cuio_copydata((struct uio *)buf, off, size, out);
+	else
+		bcopy(buf + off, out, size);
+}
+
+int
+crypto_apply(int flags, caddr_t buf, int off, int len,
+    int (*f)(void *, void *, u_int), void *arg)
+{
+	int error;
+
+	if ((flags & CRYPTO_F_IMBUF) != 0)
+		error = m_apply((struct mbuf *)buf, off, len, f, arg);
+	else if ((flags & CRYPTO_F_IOV) != 0)
+		error = cuio_apply((struct uio *)buf, off, len, f, arg);
+	else
+		error = (*f)(arg, buf + off, len);
+	return (error);
+}
Index: crypto.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/crypto.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/opencrypto/crypto.c -L sys/opencrypto/crypto.c -u -r1.2 -r1.3
--- sys/opencrypto/crypto.c
+++ sys/opencrypto/crypto.c
@@ -1,4 +1,38 @@
-/*	$OpenBSD: crypto.c,v 1.38 2002/06/11 11:14:29 beck Exp $	*/
+/*-
+ * Copyright (c) 2002-2006 Sam Leffler.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.27 2007/03/21 03:42:51 sam Exp $");
+
+/*
+ * Cryptographic Subsystem.
+ *
+ * This code is derived from the Openbsd Cryptographic Framework (OCF)
+ * that has the copyright shown below.  Very little of the original
+ * code remains.
+ */
+
 /*-
  * The author of this code is Angelos D. Keromytis (angelos at cis.upenn.edu)
  *
@@ -20,11 +54,10 @@
  * PURPOSE.
  */
 
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.16.2.1 2006/03/05 00:48:05 wkoszek Exp $");
-
 #define	CRYPTO_TIMING				/* enable timing support */
 
+#include "opt_ddb.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/eventhandler.h>
@@ -37,10 +70,16 @@
 #include <sys/proc.h>
 #include <sys/sysctl.h>
 
+#include <ddb/ddb.h>
+
 #include <vm/uma.h>
 #include <opencrypto/cryptodev.h>
 #include <opencrypto/xform.h>			/* XXX for M_XDATA */
 
+#include <sys/kobj.h>
+#include <sys/bus.h>
+#include "cryptodev_if.h"
+
 /*
  * Crypto drivers register themselves by allocating a slot in the
  * crypto_drivers table with crypto_get_driverid() and then registering
@@ -49,6 +88,33 @@
 static	struct mtx crypto_drivers_mtx;		/* lock on driver table */
 #define	CRYPTO_DRIVER_LOCK()	mtx_lock(&crypto_drivers_mtx)
 #define	CRYPTO_DRIVER_UNLOCK()	mtx_unlock(&crypto_drivers_mtx)
+#define	CRYPTO_DRIVER_ASSERT()	mtx_assert(&crypto_drivers_mtx, MA_OWNED)
+
+/*
+ * Crypto device/driver capabilities structure.
+ *
+ * Synchronization:
+ * (d) - protected by CRYPTO_DRIVER_LOCK()
+ * (q) - protected by CRYPTO_Q_LOCK()
+ * Not tagged fields are read-only.
+ */
+struct cryptocap {
+	device_t	cc_dev;			/* (d) device/driver */
+	u_int32_t	cc_sessions;		/* (d) # of sessions */
+	u_int32_t	cc_koperations;		/* (d) # os asym operations */
+	/*
+	 * Largest possible operator length (in bits) for each type of
+	 * encryption algorithm. XXX not used
+	 */
+	u_int16_t	cc_max_op_len[CRYPTO_ALGORITHM_MAX + 1];
+	u_int8_t	cc_alg[CRYPTO_ALGORITHM_MAX + 1];
+	u_int8_t	cc_kalg[CRK_ALGORITHM_MAX + 1];
+
+	int		cc_flags;		/* (d) flags */
+#define CRYPTOCAP_F_CLEANUP	0x80000000	/* needs resource cleanup */
+	int		cc_qblocked;		/* (q) symmetric q blocked */
+	int		cc_kqblocked;		/* (q) asymmetric q blocked */
+};
 static	struct cryptocap *crypto_drivers = NULL;
 static	int crypto_drivers_num = 0;
 
@@ -59,6 +125,7 @@
  * have one per-queue but having one simplifies handling of block/unblock
  * operations.
  */
+static	int crp_sleep = 0;
 static	TAILQ_HEAD(,cryptop) crp_q;		/* request queues */
 static	TAILQ_HEAD(,cryptkop) crp_kq;
 static	struct mtx crypto_q_mtx;
@@ -78,6 +145,7 @@
 static	struct mtx crypto_ret_q_mtx;
 #define	CRYPTO_RETQ_LOCK()	mtx_lock(&crypto_ret_q_mtx)
 #define	CRYPTO_RETQ_UNLOCK()	mtx_unlock(&crypto_ret_q_mtx)
+#define	CRYPTO_RETQ_EMPTY()	(TAILQ_EMPTY(&crp_ret_q) && TAILQ_EMPTY(&crp_ret_kq))
 
 static	uma_zone_t cryptop_zone;
 static	uma_zone_t cryptodesc_zone;
@@ -98,8 +166,8 @@
 static	void crypto_ret_proc(void);
 static	struct proc *cryptoretproc;
 static	void crypto_destroy(void);
-static	int crypto_invoke(struct cryptop *crp, int hint);
-static	int crypto_kinvoke(struct cryptkop *krp, int hint);
+static	int crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint);
+static	int crypto_kinvoke(struct cryptkop *krp, int flags);
 
 static	struct cryptostats cryptostats;
 SYSCTL_STRUCT(_kern, OID_AUTO, crypto_stats, CTLFLAG_RW, &cryptostats,
@@ -222,112 +290,146 @@
 	mtx_destroy(&crypto_drivers_mtx);
 }
 
+static struct cryptocap *
+crypto_checkdriver(u_int32_t hid)
+{
+	if (crypto_drivers == NULL)
+		return NULL;
+	return (hid >= crypto_drivers_num ? NULL : &crypto_drivers[hid]);
+}
+
 /*
- * Initialization code, both for static and dynamic loading.
+ * Compare a driver's list of supported algorithms against another
+ * list; return non-zero if all algorithms are supported.
  */
 static int
-crypto_modevent(module_t mod, int type, void *unused)
+driver_suitable(const struct cryptocap *cap, const struct cryptoini *cri)
 {
-	int error = EINVAL;
+	const struct cryptoini *cr;
 
-	switch (type) {
-	case MOD_LOAD:
-		error = crypto_init();
-		if (error == 0 && bootverbose)
-			printf("crypto: <crypto core>\n");
-		break;
-	case MOD_UNLOAD:
-		/*XXX disallow if active sessions */
-		error = 0;
-		crypto_destroy();
-		return 0;
-	}
-	return error;
+	/* See if all the algorithms are supported. */
+	for (cr = cri; cr; cr = cr->cri_next)
+		if (cap->cc_alg[cr->cri_alg] == 0)
+			return 0;
+	return 1;
 }
 
-static moduledata_t crypto_mod = {
-	"crypto",
-	crypto_modevent,
-	0
-};
-MODULE_VERSION(crypto, 1);
-DECLARE_MODULE(crypto, crypto_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_DEPEND(crypto, zlib, 1, 1, 1);
-
 /*
- * Create a new session.
+ * Select a driver for a new session that supports the specified
+ * algorithms and, optionally, is constrained according to the flags.
+ * The algorithm we use here is pretty stupid; just use the
+ * first driver that supports all the algorithms we need. If there
+ * are multiple drivers we choose the driver with the fewest active
+ * sessions.  We prefer hardware-backed drivers to software ones.
+ *
+ * XXX We need more smarts here (in real life too, but that's
+ * XXX another story altogether).
  */
-int
-crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard)
+static struct cryptocap *
+crypto_select_driver(const struct cryptoini *cri, int flags)
 {
-	struct cryptoini *cr;
-	u_int32_t hid, lid;
-	int err = EINVAL;
+	struct cryptocap *cap, *best;
+	int match, hid;
 
-	CRYPTO_DRIVER_LOCK();
-
-	if (crypto_drivers == NULL)
-		goto done;
+	CRYPTO_DRIVER_ASSERT();
 
 	/*
-	 * The algorithm we use here is pretty stupid; just use the
-	 * first driver that supports all the algorithms we need.
-	 *
-	 * XXX We need more smarts here (in real life too, but that's
-	 * XXX another story altogether).
+	 * Look first for hardware crypto devices if permitted.
 	 */
-
+	if (flags & CRYPTOCAP_F_HARDWARE)
+		match = CRYPTOCAP_F_HARDWARE;
+	else
+		match = CRYPTOCAP_F_SOFTWARE;
+	best = NULL;
+again:
 	for (hid = 0; hid < crypto_drivers_num; hid++) {
-		struct cryptocap *cap = &crypto_drivers[hid];
+		cap = &crypto_drivers[hid];
 		/*
-		 * If it's not initialized or has remaining sessions
-		 * referencing it, skip.
+		 * If it's not initialized, is in the process of
+		 * going away, or is not appropriate (hardware
+		 * or software based on match), then skip.
 		 */
-		if (cap->cc_newsession == NULL ||
-		    (cap->cc_flags & CRYPTOCAP_F_CLEANUP))
+		if (cap->cc_dev == NULL ||
+		    (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+		    (cap->cc_flags & match) == 0)
 			continue;
 
-		/* Hardware required -- ignore software drivers. */
-		if (hard > 0 && (cap->cc_flags & CRYPTOCAP_F_SOFTWARE))
-			continue;
-		/* Software required -- ignore hardware drivers. */
-		if (hard < 0 && (cap->cc_flags & CRYPTOCAP_F_SOFTWARE) == 0)
-			continue;
-
-		/* See if all the algorithms are supported. */
-		for (cr = cri; cr; cr = cr->cri_next)
-			if (cap->cc_alg[cr->cri_alg] == 0)
-				break;
-
-		if (cr == NULL) {
-			/* Ok, all algorithms are supported. */
+		/* verify all the algorithms are supported. */
+		if (driver_suitable(cap, cri)) {
+			if (best == NULL ||
+			    cap->cc_sessions < best->cc_sessions)
+				best = cap;
+		}
+	}
+	if (best != NULL)
+		return best;
+	if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+		/* sort of an Algol 68-style for loop */
+		match = CRYPTOCAP_F_SOFTWARE;
+		goto again;
+	}
+	return best;
+}
 
-			/*
-			 * Can't do everything in one session.
-			 *
-			 * XXX Fix this. We need to inject a "virtual" session layer right
-			 * XXX about here.
-			 */
+/*
+ * Create a new session.  The crid argument specifies a crypto
+ * driver to use or constraints on a driver to select (hardware
+ * only, software only, either).  Whatever driver is selected
+ * must be capable of the requested crypto algorithms.
+ */
+int
+crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int crid)
+{
+	struct cryptocap *cap;
+	u_int32_t hid, lid;
+	int err;
 
-			/* Call the driver initialization routine. */
-			lid = hid;		/* Pass the driver ID. */
-			err = (*cap->cc_newsession)(cap->cc_arg, &lid, cri);
-			if (err == 0) {
-				/* XXX assert (hid &~ 0xffffff) == 0 */
-				/* XXX assert (cap->cc_flags &~ 0xff) == 0 */
-				(*sid) = ((cap->cc_flags & 0xff) << 24) | hid;
-				(*sid) <<= 32;
-				(*sid) |= (lid & 0xffffffff);
-				cap->cc_sessions++;
-			}
-			break;
-		}
+	CRYPTO_DRIVER_LOCK();
+	if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+		/*
+		 * Use specified driver; verify it is capable.
+		 */
+		cap = crypto_checkdriver(crid);
+		if (cap != NULL && !driver_suitable(cap, cri))
+			cap = NULL;
+	} else {
+		/*
+		 * No requested driver; select based on crid flags.
+		 */
+		cap = crypto_select_driver(cri, crid);
+		/*
+		 * if NULL then can't do everything in one session.
+		 * XXX Fix this. We need to inject a "virtual" session
+		 * XXX layer right about here.
+		 */
 	}
-done:
+	if (cap != NULL) {
+		/* Call the driver initialization routine. */
+		hid = cap - crypto_drivers;
+		lid = hid;		/* Pass the driver ID. */
+		err = CRYPTODEV_NEWSESSION(cap->cc_dev, &lid, cri);
+		if (err == 0) {
+			(*sid) = (cap->cc_flags & 0xff000000)
+			       | (hid & 0x00ffffff);
+			(*sid) <<= 32;
+			(*sid) |= (lid & 0xffffffff);
+			cap->cc_sessions++;
+		}
+	} else
+		err = EINVAL;
 	CRYPTO_DRIVER_UNLOCK();
 	return err;
 }
 
+static void
+crypto_remove(struct cryptocap *cap)
+{
+
+	mtx_assert(&crypto_drivers_mtx, MA_OWNED);
+	if (cap->cc_sessions == 0 && cap->cc_koperations == 0)
+		bzero(cap, sizeof(*cap));
+}
+
 /*
  * Delete an existing session (or a reserved session on an unregistered
  * driver).
@@ -335,6 +437,7 @@
 int
 crypto_freesession(u_int64_t sid)
 {
+	struct cryptocap *cap;
 	u_int32_t hid;
 	int err;
 
@@ -352,24 +455,16 @@
 		err = ENOENT;
 		goto done;
 	}
+	cap = &crypto_drivers[hid];
 
-	if (crypto_drivers[hid].cc_sessions)
-		crypto_drivers[hid].cc_sessions--;
+	if (cap->cc_sessions)
+		cap->cc_sessions--;
 
 	/* Call the driver cleanup routine, if available. */
-	if (crypto_drivers[hid].cc_freesession)
-		err = crypto_drivers[hid].cc_freesession(
-				crypto_drivers[hid].cc_arg, sid);
-	else
-		err = 0;
+	err = CRYPTODEV_FREESESSION(cap->cc_dev, sid);
 
-	/*
-	 * If this was the last session of a driver marked as invalid,
-	 * make the entry available for reuse.
-	 */
-	if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
-	    crypto_drivers[hid].cc_sessions == 0)
-		bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
+	if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+		crypto_remove(cap);
 
 done:
 	CRYPTO_DRIVER_UNLOCK();
@@ -381,18 +476,25 @@
  * support for the algorithms they handle.
  */
 int32_t
-crypto_get_driverid(u_int32_t flags)
+crypto_get_driverid(device_t dev, int flags)
 {
 	struct cryptocap *newdrv;
 	int i;
 
+	if ((flags & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+		printf("%s: no flags specified when registering driver\n",
+		    device_get_nameunit(dev));
+		return -1;
+	}
+
 	CRYPTO_DRIVER_LOCK();
 
-	for (i = 0; i < crypto_drivers_num; i++)
-		if (crypto_drivers[i].cc_process == NULL &&
-		    (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) == 0 &&
-		    crypto_drivers[i].cc_sessions == 0)
+	for (i = 0; i < crypto_drivers_num; i++) {
+		if (crypto_drivers[i].cc_dev == NULL &&
+		    (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) == 0) {
 			break;
+		}
+	}
 
 	/* Out of entries, allocate some more. */
 	if (i == crypto_drivers_num) {
@@ -422,21 +524,61 @@
 
 	/* NB: state is zero'd on free */
 	crypto_drivers[i].cc_sessions = 1;	/* Mark */
+	crypto_drivers[i].cc_dev = dev;
 	crypto_drivers[i].cc_flags = flags;
 	if (bootverbose)
-		printf("crypto: assign driver %u, flags %u\n", i, flags);
+		printf("crypto: assign %s driver id %u, flags %u\n",
+		    device_get_nameunit(dev), i, flags);
 
 	CRYPTO_DRIVER_UNLOCK();
 
 	return i;
 }
 
-static struct cryptocap *
-crypto_checkdriver(u_int32_t hid)
+/*
+ * Lookup a driver by name.  We match against the full device
+ * name and unit, and against just the name.  The latter gives
+ * us a simple widlcarding by device name.  On success return the
+ * driver/hardware identifier; otherwise return -1.
+ */
+int
+crypto_find_driver(const char *match)
 {
-	if (crypto_drivers == NULL)
-		return NULL;
-	return (hid >= crypto_drivers_num ? NULL : &crypto_drivers[hid]);
+	int i, len = strlen(match);
+
+	CRYPTO_DRIVER_LOCK();
+	for (i = 0; i < crypto_drivers_num; i++) {
+		device_t dev = crypto_drivers[i].cc_dev;
+		if (dev == NULL ||
+		    (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP))
+			continue;
+		if (strncmp(match, device_get_nameunit(dev), len) == 0 ||
+		    strncmp(match, device_get_name(dev), len) == 0)
+			break;
+	}
+	CRYPTO_DRIVER_UNLOCK();
+	return i < crypto_drivers_num ? i : -1;
+}
+
+/*
+ * Return the device_t for the specified driver or NULL
+ * if the driver identifier is invalid.
+ */
+device_t
+crypto_find_device_byhid(int hid)
+{
+	struct cryptocap *cap = crypto_checkdriver(hid);
+	return cap != NULL ? cap->cc_dev : NULL;
+}
+
+/*
+ * Return the device/driver capabilities.
+ */
+int
+crypto_getcaps(int hid)
+{
+	struct cryptocap *cap = crypto_checkdriver(hid);
+	return cap != NULL ? cap->cc_flags : 0;
 }
 
 /*
@@ -444,9 +586,7 @@
  * is called once for each algorithm supported a driver.
  */
 int
-crypto_kregister(u_int32_t driverid, int kalg, u_int32_t flags,
-    int (*kprocess)(void*, struct cryptkop *, int),
-    void *karg)
+crypto_kregister(u_int32_t driverid, int kalg, u_int32_t flags)
 {
 	struct cryptocap *cap;
 	int err;
@@ -464,16 +604,11 @@
 
 		cap->cc_kalg[kalg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
 		if (bootverbose)
-			printf("crypto: driver %u registers key alg %u flags %u\n"
-				, driverid
+			printf("crypto: %s registers key alg %u flags %u\n"
+				, device_get_nameunit(cap->cc_dev)
 				, kalg
 				, flags
 			);
-
-		if (cap->cc_kprocess == NULL) {
-			cap->cc_karg = karg;
-			cap->cc_kprocess = kprocess;
-		}
 		err = 0;
 	} else
 		err = EINVAL;
@@ -488,11 +623,7 @@
  */
 int
 crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
-    u_int32_t flags,
-    int (*newses)(void*, u_int32_t*, struct cryptoini*),
-    int (*freeses)(void*, u_int64_t),
-    int (*process)(void*, struct cryptop *, int),
-    void *arg)
+    u_int32_t flags)
 {
 	struct cryptocap *cap;
 	int err;
@@ -512,20 +643,13 @@
 		cap->cc_alg[alg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
 		cap->cc_max_op_len[alg] = maxoplen;
 		if (bootverbose)
-			printf("crypto: driver %u registers alg %u flags %u maxoplen %u\n"
-				, driverid
+			printf("crypto: %s registers alg %u flags %u maxoplen %u\n"
+				, device_get_nameunit(cap->cc_dev)
 				, alg
 				, flags
 				, maxoplen
 			);
-
-		if (cap->cc_process == NULL) {
-			cap->cc_arg = arg;
-			cap->cc_newsession = newses;
-			cap->cc_process = process;
-			cap->cc_freesession = freeses;
-			cap->cc_sessions = 0;		/* Unmark */
-		}
+		cap->cc_sessions = 0;		/* Unmark */
 		err = 0;
 	} else
 		err = EINVAL;
@@ -534,6 +658,27 @@
 	return err;
 }
 
+static void
+driver_finis(struct cryptocap *cap)
+{
+	u_int32_t ses, kops;
+
+	CRYPTO_DRIVER_ASSERT();
+
+	ses = cap->cc_sessions;
+	kops = cap->cc_koperations;
+	bzero(cap, sizeof(*cap));
+	if (ses != 0 || kops != 0) {
+		/*
+		 * If there are pending sessions,
+		 * just mark as invalid.
+		 */
+		cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
+		cap->cc_sessions = ses;
+		cap->cc_koperations = kops;
+	}
+}
+
 /*
  * Unregister a crypto driver. If there are pending sessions using it,
  * leave enough information around so that subsequent calls using those
@@ -543,12 +688,10 @@
 int
 crypto_unregister(u_int32_t driverid, int alg)
 {
-	int i, err;
-	u_int32_t ses;
 	struct cryptocap *cap;
+	int i, err;
 
 	CRYPTO_DRIVER_LOCK();
-
 	cap = crypto_checkdriver(driverid);
 	if (cap != NULL &&
 	    (CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX) &&
@@ -561,22 +704,13 @@
 			if (cap->cc_alg[i] != 0)
 				break;
 
-		if (i == CRYPTO_ALGORITHM_MAX + 1) {
-			ses = cap->cc_sessions;
-			bzero(cap, sizeof(struct cryptocap));
-			if (ses != 0) {
-				/*
-				 * If there are pending sessions, just mark as invalid.
-				 */
-				cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
-				cap->cc_sessions = ses;
-			}
-		}
+		if (i == CRYPTO_ALGORITHM_MAX + 1)
+			driver_finis(cap);
 		err = 0;
 	} else
 		err = EINVAL;
-
 	CRYPTO_DRIVER_UNLOCK();
+
 	return err;
 }
 
@@ -590,32 +724,18 @@
 int
 crypto_unregister_all(u_int32_t driverid)
 {
-	int i, err;
-	u_int32_t ses;
 	struct cryptocap *cap;
+	int err;
 
 	CRYPTO_DRIVER_LOCK();
-
 	cap = crypto_checkdriver(driverid);
 	if (cap != NULL) {
-		for (i = CRYPTO_ALGORITHM_MIN; i <= CRYPTO_ALGORITHM_MAX; i++) {
-			cap->cc_alg[i] = 0;
-			cap->cc_max_op_len[i] = 0;
-		}
-		ses = cap->cc_sessions;
-		bzero(cap, sizeof(struct cryptocap));
-		if (ses != 0) {
-			/*
-			 * If there are pending sessions, just mark as invalid.
-			 */
-			cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
-			cap->cc_sessions = ses;
-		}
+		driver_finis(cap);
 		err = 0;
 	} else
 		err = EINVAL;
-
 	CRYPTO_DRIVER_UNLOCK();
+
 	return err;
 }
 
@@ -627,21 +747,16 @@
 crypto_unblock(u_int32_t driverid, int what)
 {
 	struct cryptocap *cap;
-	int needwakeup, err;
+	int err;
 
 	CRYPTO_Q_LOCK();
 	cap = crypto_checkdriver(driverid);
 	if (cap != NULL) {
-		needwakeup = 0;
-		if (what & CRYPTO_SYMQ) {
-			needwakeup |= cap->cc_qblocked;
+		if (what & CRYPTO_SYMQ)
 			cap->cc_qblocked = 0;
-		}
-		if (what & CRYPTO_ASYMQ) {
-			needwakeup |= cap->cc_kqblocked;
+		if (what & CRYPTO_ASYMQ)
 			cap->cc_kqblocked = 0;
-		}
-		if (needwakeup)
+		if (crp_sleep)
 			wakeup_one(&crp_q);
 		err = 0;
 	} else
@@ -657,7 +772,8 @@
 int
 crypto_dispatch(struct cryptop *crp)
 {
-	u_int32_t hid = CRYPTO_SESID2HID(crp->crp_sid);
+	struct cryptocap *cap;
+	u_int32_t hid;
 	int result;
 
 	cryptostats.cs_ops++;
@@ -667,57 +783,33 @@
 		binuptime(&crp->crp_tstamp);
 #endif
 
-	CRYPTO_Q_LOCK();
+	hid = CRYPTO_SESID2HID(crp->crp_sid);
+
 	if ((crp->crp_flags & CRYPTO_F_BATCH) == 0) {
-		struct cryptocap *cap;
 		/*
 		 * Caller marked the request to be processed
 		 * immediately; dispatch it directly to the
 		 * driver unless the driver is currently blocked.
 		 */
 		cap = crypto_checkdriver(hid);
-		if (cap && !cap->cc_qblocked) {
-			result = crypto_invoke(crp, 0);
-			if (result == ERESTART) {
-				/*
-				 * The driver ran out of resources, mark the
-				 * driver ``blocked'' for cryptop's and put
-				 * the request on the queue.
-				 *
-				 * XXX ops are placed at the tail so their
-				 * order is preserved but this can place them
-				 * behind batch'd ops.
-				 */
-				crypto_drivers[hid].cc_qblocked = 1;
-				TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
-				cryptostats.cs_blocks++;
-				result = 0;
-			}
-		} else {
+		/* Driver cannot disappeared when there is an active session. */
+		KASSERT(cap != NULL, ("%s: Driver disappeared.", __func__));
+		if (!cap->cc_qblocked) {
+			result = crypto_invoke(cap, crp, 0);
+			if (result != ERESTART)
+				return (result);
 			/*
-			 * The driver is blocked, just queue the op until
-			 * it unblocks and the kernel thread gets kicked.
+			 * The driver ran out of resources, put the request on
+			 * the queue.
 			 */
-			TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
-			result = 0;
 		}
-	} else {
-		int wasempty;
-		/*
-		 * Caller marked the request as ``ok to delay'';
-		 * queue it for the dispatch thread.  This is desirable
-		 * when the operation is low priority and/or suitable
-		 * for batching.
-		 */
-		wasempty = TAILQ_EMPTY(&crp_q);
-		TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
-		if (wasempty)
-			wakeup_one(&crp_q);
-		result = 0;
 	}
+	CRYPTO_Q_LOCK();
+	TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
+	if (crp_sleep)
+		wakeup_one(&crp_q);
 	CRYPTO_Q_UNLOCK();
-
-	return result;
+	return 0;
 }
 
 /*
@@ -727,78 +819,143 @@
 int
 crypto_kdispatch(struct cryptkop *krp)
 {
-	struct cryptocap *cap;
-	int result;
+	int error;
 
 	cryptostats.cs_kops++;
 
-	CRYPTO_Q_LOCK();
-	cap = crypto_checkdriver(krp->krp_hid);
-	if (cap && !cap->cc_kqblocked) {
-		result = crypto_kinvoke(krp, 0);
-		if (result == ERESTART) {
-			/*
-			 * The driver ran out of resources, mark the
-			 * driver ``blocked'' for cryptkop's and put
-			 * the request back in the queue.  It would
-			 * best to put the request back where we got
-			 * it but that's hard so for now we put it
-			 * at the front.  This should be ok; putting
-			 * it at the end does not work.
-			 */
-			crypto_drivers[krp->krp_hid].cc_kqblocked = 1;
-			TAILQ_INSERT_TAIL(&crp_kq, krp, krp_next);
-			cryptostats.cs_kblocks++;
-		}
-	} else {
-		/*
-		 * The driver is blocked, just queue the op until
-		 * it unblocks and the kernel thread gets kicked.
-		 */
+	error = crypto_kinvoke(krp, krp->krp_crid);
+	if (error == ERESTART) {
+		CRYPTO_Q_LOCK();
 		TAILQ_INSERT_TAIL(&crp_kq, krp, krp_next);
-		result = 0;
+		if (crp_sleep)
+			wakeup_one(&crp_q);
+		CRYPTO_Q_UNLOCK();
+		error = 0;
 	}
-	CRYPTO_Q_UNLOCK();
-
-	return result;
+	return error;
 }
 
 /*
- * Dispatch an assymetric crypto request to the appropriate crypto devices.
+ * Verify a driver is suitable for the specified operation.
  */
-static int
-crypto_kinvoke(struct cryptkop *krp, int hint)
+static __inline int
+kdriver_suitable(const struct cryptocap *cap, const struct cryptkop *krp)
 {
-	u_int32_t hid;
-	int error;
+	return (cap->cc_kalg[krp->krp_op] & CRYPTO_ALG_FLAG_SUPPORTED) != 0;
+}
 
-	mtx_assert(&crypto_q_mtx, MA_OWNED);
+/*
+ * Select a driver for an asym operation.  The driver must
+ * support the necessary algorithm.  The caller can constrain
+ * which device is selected with the flags parameter.  The
+ * algorithm we use here is pretty stupid; just use the first
+ * driver that supports the algorithms we need. If there are
+ * multiple suitable drivers we choose the driver with the
+ * fewest active operations.  We prefer hardware-backed
+ * drivers to software ones when either may be used.
+ */
+static struct cryptocap *
+crypto_select_kdriver(const struct cryptkop *krp, int flags)
+{
+	struct cryptocap *cap, *best, *blocked;
+	int match, hid;
 
-	/* Sanity checks. */
-	if (krp == NULL)
-		return EINVAL;
-	if (krp->krp_callback == NULL) {
-		free(krp, M_XDATA);		/* XXX allocated in cryptodev */
-		return EINVAL;
-	}
+	CRYPTO_DRIVER_ASSERT();
 
+	/*
+	 * Look first for hardware crypto devices if permitted.
+	 */
+	if (flags & CRYPTOCAP_F_HARDWARE)
+		match = CRYPTOCAP_F_HARDWARE;
+	else
+		match = CRYPTOCAP_F_SOFTWARE;
+	best = NULL;
+	blocked = NULL;
+again:
 	for (hid = 0; hid < crypto_drivers_num; hid++) {
-		if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
-		    !crypto_devallowsoft)
-			continue;
-		if (crypto_drivers[hid].cc_kprocess == NULL)
-			continue;
-		if ((crypto_drivers[hid].cc_kalg[krp->krp_op] &
-		    CRYPTO_ALG_FLAG_SUPPORTED) == 0)
+		cap = &crypto_drivers[hid];
+		/*
+		 * If it's not initialized, is in the process of
+		 * going away, or is not appropriate (hardware
+		 * or software based on match), then skip.
+		 */
+		if (cap->cc_dev == NULL ||
+		    (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+		    (cap->cc_flags & match) == 0)
 			continue;
-		break;
+
+		/* verify all the algorithms are supported. */
+		if (kdriver_suitable(cap, krp)) {
+			if (best == NULL ||
+			    cap->cc_koperations < best->cc_koperations)
+				best = cap;
+		}
 	}
-	if (hid < crypto_drivers_num) {
-		krp->krp_hid = hid;
-		error = crypto_drivers[hid].cc_kprocess(
-				crypto_drivers[hid].cc_karg, krp, hint);
-	} else
-		error = ENODEV;
+	if (best != NULL)
+		return best;
+	if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+		/* sort of an Algol 68-style for loop */
+		match = CRYPTOCAP_F_SOFTWARE;
+		goto again;
+	}
+	return best;
+}
+
+/*
+ * Dispatch an assymetric crypto request.
+ */
+static int
+crypto_kinvoke(struct cryptkop *krp, int crid)
+{
+	struct cryptocap *cap = NULL;
+	int error;
+
+	KASSERT(krp != NULL, ("%s: krp == NULL", __func__));
+	KASSERT(krp->krp_callback != NULL,
+	    ("%s: krp->crp_callback == NULL", __func__));
+
+	CRYPTO_DRIVER_LOCK();
+	if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+		cap = crypto_checkdriver(crid);
+		if (cap != NULL) {
+			/*
+			 * Driver present, it must support the necessary
+			 * algorithm and, if s/w drivers are excluded,
+			 * it must be registered as hardware-backed.
+			 */
+			if (!kdriver_suitable(cap, krp) ||
+			    (!crypto_devallowsoft &&
+			     (cap->cc_flags & CRYPTOCAP_F_HARDWARE) == 0))
+				cap = NULL;
+		}
+	} else {
+		/*
+		 * No requested driver; select based on crid flags.
+		 */
+		if (!crypto_devallowsoft)	/* NB: disallow s/w drivers */
+			crid &= ~CRYPTOCAP_F_SOFTWARE;
+		cap = crypto_select_kdriver(krp, crid);
+	}
+	if (cap != NULL && !cap->cc_kqblocked) {
+		krp->krp_hid = cap - crypto_drivers;
+		cap->cc_koperations++;
+		CRYPTO_DRIVER_UNLOCK();
+		error = CRYPTODEV_KPROCESS(cap->cc_dev, krp, 0);
+		CRYPTO_DRIVER_LOCK();
+		if (error == ERESTART) {
+			cap->cc_koperations--;
+			CRYPTO_DRIVER_UNLOCK();
+			return (error);
+		}
+	} else {
+		/*
+		 * NB: cap is !NULL if device is blocked; in
+		 *     that case return ERESTART so the operation
+		 *     is resubmitted if possible.
+		 */
+		error = (cap == NULL) ? ENODEV : ERESTART;
+	}
+	CRYPTO_DRIVER_UNLOCK();
 
 	if (error) {
 		krp->krp_status = error;
@@ -837,49 +994,37 @@
  * Dispatch a crypto request to the appropriate crypto devices.
  */
 static int
-crypto_invoke(struct cryptop *crp, int hint)
+crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint)
 {
-	u_int32_t hid;
-	int (*process)(void*, struct cryptop *, int);
+
+	KASSERT(crp != NULL, ("%s: crp == NULL", __func__));
+	KASSERT(crp->crp_callback != NULL,
+	    ("%s: crp->crp_callback == NULL", __func__));
+	KASSERT(crp->crp_desc != NULL, ("%s: crp->crp_desc == NULL", __func__));
 
 #ifdef CRYPTO_TIMING
 	if (crypto_timing)
 		crypto_tstat(&cryptostats.cs_invoke, &crp->crp_tstamp);
 #endif
-	/* Sanity checks. */
-	if (crp == NULL)
-		return EINVAL;
-	if (crp->crp_callback == NULL) {
-		crypto_freereq(crp);
-		return EINVAL;
-	}
-	if (crp->crp_desc == NULL) {
-		crp->crp_etype = EINVAL;
-		crypto_done(crp);
-		return 0;
-	}
-
-	hid = CRYPTO_SESID2HID(crp->crp_sid);
-	if (hid < crypto_drivers_num) {
-		if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP)
-			crypto_freesession(crp->crp_sid);
-		process = crypto_drivers[hid].cc_process;
-	} else {
-		process = NULL;
-	}
-
-	if (process == NULL) {
+	if (cap->cc_flags & CRYPTOCAP_F_CLEANUP) {
 		struct cryptodesc *crd;
 		u_int64_t nid;
 
 		/*
 		 * Driver has unregistered; migrate the session and return
 		 * an error to the caller so they'll resubmit the op.
+		 *
+		 * XXX: What if there are more already queued requests for this
+		 *      session?
 		 */
+		crypto_freesession(crp->crp_sid);
+
 		for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
 			crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
 
-		if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
+		/* XXX propagate flags from initial session? */
+		if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI),
+		    CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE) == 0)
 			crp->crp_sid = nid;
 
 		crp->crp_etype = EAGAIN;
@@ -889,7 +1034,7 @@
 		/*
 		 * Invoke the driver to process the request.
 		 */
-		return (*process)(crypto_drivers[hid].cc_arg, crp, hint);
+		return CRYPTODEV_PROCESS(cap->cc_dev, crp, hint);
 	}
 }
 
@@ -904,11 +1049,31 @@
 	if (crp == NULL)
 		return;
 
+#ifdef DIAGNOSTIC
+	{
+		struct cryptop *crp2;
+
+		CRYPTO_Q_LOCK();
+		TAILQ_FOREACH(crp2, &crp_q, crp_next) {
+			KASSERT(crp2 != crp,
+			    ("Freeing cryptop from the crypto queue (%p).",
+			    crp));
+		}
+		CRYPTO_Q_UNLOCK();
+		CRYPTO_RETQ_LOCK();
+		TAILQ_FOREACH(crp2, &crp_ret_q, crp_next) {
+			KASSERT(crp2 != crp,
+			    ("Freeing cryptop from the return queue (%p).",
+			    crp));
+		}
+		CRYPTO_RETQ_UNLOCK();
+	}
+#endif
+
 	while ((crd = crp->crp_desc) != NULL) {
 		crp->crp_desc = crd->crd_next;
 		uma_zfree(cryptodesc_zone, crd);
 	}
-
 	uma_zfree(cryptop_zone, crp);
 }
 
@@ -982,16 +1147,13 @@
 #endif
 			crp->crp_callback(crp);
 	} else {
-		int wasempty;
 		/*
 		 * Normal case; queue the callback for the thread.
 		 */
 		CRYPTO_RETQ_LOCK();
-		wasempty = TAILQ_EMPTY(&crp_ret_q);
-		TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next);
-
-		if (wasempty)
+		if (CRYPTO_RETQ_EMPTY())
 			wakeup_one(&crp_ret_q);	/* shared wait channel */
+		TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next);
 		CRYPTO_RETQ_UNLOCK();
 	}
 }
@@ -1002,16 +1164,24 @@
 void
 crypto_kdone(struct cryptkop *krp)
 {
-	int wasempty;
+	struct cryptocap *cap;
 
 	if (krp->krp_status != 0)
 		cryptostats.cs_kerrs++;
+	CRYPTO_DRIVER_LOCK();
+	/* XXX: What if driver is loaded in the meantime? */
+	if (krp->krp_hid < crypto_drivers_num) {
+		cap = &crypto_drivers[krp->krp_hid];
+		cap->cc_koperations--;
+		KASSERT(cap->cc_koperations >= 0, ("cc_koperations < 0"));
+		if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+			crypto_remove(cap);
+	}
+	CRYPTO_DRIVER_UNLOCK();
 	CRYPTO_RETQ_LOCK();
-	wasempty = TAILQ_EMPTY(&crp_ret_kq);
-	TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next);
-
-	if (wasempty)
+	if (CRYPTO_RETQ_EMPTY())
 		wakeup_one(&crp_ret_q);		/* shared wait channel */
+	TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next);
 	CRYPTO_RETQ_UNLOCK();
 }
 
@@ -1020,24 +1190,19 @@
 {
 	int hid, kalg, feat = 0;
 
-	if (!crypto_userasymcrypto)
-		goto out;	  
-
 	CRYPTO_DRIVER_LOCK();
 	for (hid = 0; hid < crypto_drivers_num; hid++) {
-		if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
+		const struct cryptocap *cap = &crypto_drivers[hid];
+
+		if ((cap->cc_flags & CRYPTOCAP_F_SOFTWARE) &&
 		    !crypto_devallowsoft) {
 			continue;
 		}
-		if (crypto_drivers[hid].cc_kprocess == NULL)
-			continue;
 		for (kalg = 0; kalg < CRK_ALGORITHM_MAX; kalg++)
-			if ((crypto_drivers[hid].cc_kalg[kalg] &
-			    CRYPTO_ALG_FLAG_SUPPORTED) != 0)
+			if (cap->cc_kalg[kalg] & CRYPTO_ALG_FLAG_SUPPORTED)
 				feat |=  1 << kalg;
 	}
 	CRYPTO_DRIVER_UNLOCK();
-out:
 	*featp = feat;
 	return (0);
 }
@@ -1069,6 +1234,7 @@
 	struct cryptop *crp, *submit;
 	struct cryptkop *krp;
 	struct cryptocap *cap;
+	u_int32_t hid;
 	int result, hint;
 
 	CRYPTO_Q_LOCK();
@@ -1081,9 +1247,15 @@
 		submit = NULL;
 		hint = 0;
 		TAILQ_FOREACH(crp, &crp_q, crp_next) {
-			u_int32_t hid = CRYPTO_SESID2HID(crp->crp_sid);
+			hid = CRYPTO_SESID2HID(crp->crp_sid);
 			cap = crypto_checkdriver(hid);
-			if (cap == NULL || cap->cc_process == NULL) {
+			/*
+			 * Driver cannot disappeared when there is an active
+			 * session.
+			 */
+			KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+			    __func__, __LINE__));
+			if (cap == NULL || cap->cc_dev == NULL) {
 				/* Op needs to be migrated, process it. */
 				if (submit == NULL)
 					submit = crp;
@@ -1112,7 +1284,11 @@
 		}
 		if (submit != NULL) {
 			TAILQ_REMOVE(&crp_q, submit, crp_next);
-			result = crypto_invoke(submit, hint);
+			hid = CRYPTO_SESID2HID(submit->crp_sid);
+			cap = crypto_checkdriver(hid);
+			KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+			    __func__, __LINE__));
+			result = crypto_invoke(cap, submit, hint);
 			if (result == ERESTART) {
 				/*
 				 * The driver ran out of resources, mark the
@@ -1133,8 +1309,18 @@
 		/* As above, but for key ops */
 		TAILQ_FOREACH(krp, &crp_kq, krp_next) {
 			cap = crypto_checkdriver(krp->krp_hid);
-			if (cap == NULL || cap->cc_kprocess == NULL) {
-				/* Op needs to be migrated, process it. */
+			if (cap == NULL || cap->cc_dev == NULL) {
+				/*
+				 * Operation needs to be migrated, invalidate
+				 * the assigned device so it will reselect a
+				 * new one below.  Propagate the original
+				 * crid selection flags if supplied.
+				 */
+				krp->krp_hid = krp->krp_crid &
+				    (CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE);
+				if (krp->krp_hid == 0)
+					krp->krp_hid =
+				    CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE;
 				break;
 			}
 			if (!cap->cc_kqblocked)
@@ -1142,7 +1328,7 @@
 		}
 		if (krp != NULL) {
 			TAILQ_REMOVE(&crp_kq, krp, krp_next);
-			result = crypto_kinvoke(krp, 0);
+			result = crypto_kinvoke(krp, krp->krp_hid);
 			if (result == ERESTART) {
 				/*
 				 * The driver ran out of resources, mark the
@@ -1173,7 +1359,9 @@
 			 * out of order if dispatched to different devices
 			 * and some become blocked while others do not.
 			 */
+			crp_sleep = 1;
 			msleep(&crp_q, &crypto_q_mtx, PWAIT, "crypto_wait", 0);
+			crp_sleep = 0;
 			if (cryptoproc == NULL)
 				break;
 			cryptostats.cs_intrs++;
@@ -1246,3 +1434,131 @@
 
 	crypto_finis(&crp_ret_q);
 }
+
+#ifdef DDB
+static void
+db_show_drivers(void)
+{
+	int hid;
+
+	db_printf("%12s %4s %4s %8s %2s %2s\n"
+		, "Device"
+		, "Ses"
+		, "Kops"
+		, "Flags"
+		, "QB"
+		, "KB"
+	);
+	for (hid = 0; hid < crypto_drivers_num; hid++) {
+		const struct cryptocap *cap = &crypto_drivers[hid];
+		if (cap->cc_dev == NULL)
+			continue;
+		db_printf("%-12s %4u %4u %08x %2u %2u\n"
+		    , device_get_nameunit(cap->cc_dev)
+		    , cap->cc_sessions
+		    , cap->cc_koperations
+		    , cap->cc_flags
+		    , cap->cc_qblocked
+		    , cap->cc_kqblocked
+		);
+	}
+}
+
+DB_SHOW_COMMAND(crypto, db_show_crypto)
+{
+	struct cryptop *crp;
+
+	db_show_drivers();
+	db_printf("\n");
+
+	db_printf("%4s %8s %4s %4s %4s %4s %8s %8s\n",
+	    "HID", "Caps", "Ilen", "Olen", "Etype", "Flags",
+	    "Desc", "Callback");
+	TAILQ_FOREACH(crp, &crp_q, crp_next) {
+		db_printf("%4u %08x %4u %4u %4u %04x %8p %8p\n"
+		    , (int) CRYPTO_SESID2HID(crp->crp_sid)
+		    , (int) CRYPTO_SESID2CAPS(crp->crp_sid)
+		    , crp->crp_ilen, crp->crp_olen
+		    , crp->crp_etype
+		    , crp->crp_flags
+		    , crp->crp_desc
+		    , crp->crp_callback
+		);
+	}
+	if (!TAILQ_EMPTY(&crp_ret_q)) {
+		db_printf("\n%4s %4s %4s %8s\n",
+		    "HID", "Etype", "Flags", "Callback");
+		TAILQ_FOREACH(crp, &crp_ret_q, crp_next) {
+			db_printf("%4u %4u %04x %8p\n"
+			    , (int) CRYPTO_SESID2HID(crp->crp_sid)
+			    , crp->crp_etype
+			    , crp->crp_flags
+			    , crp->crp_callback
+			);
+		}
+	}
+}
+
+DB_SHOW_COMMAND(kcrypto, db_show_kcrypto)
+{
+	struct cryptkop *krp;
+
+	db_show_drivers();
+	db_printf("\n");
+
+	db_printf("%4s %5s %4s %4s %8s %4s %8s\n",
+	    "Op", "Status", "#IP", "#OP", "CRID", "HID", "Callback");
+	TAILQ_FOREACH(krp, &crp_kq, krp_next) {
+		db_printf("%4u %5u %4u %4u %08x %4u %8p\n"
+		    , krp->krp_op
+		    , krp->krp_status
+		    , krp->krp_iparams, krp->krp_oparams
+		    , krp->krp_crid, krp->krp_hid
+		    , krp->krp_callback
+		);
+	}
+	if (!TAILQ_EMPTY(&crp_ret_q)) {
+		db_printf("%4s %5s %8s %4s %8s\n",
+		    "Op", "Status", "CRID", "HID", "Callback");
+		TAILQ_FOREACH(krp, &crp_ret_kq, krp_next) {
+			db_printf("%4u %5u %08x %4u %8p\n"
+			    , krp->krp_op
+			    , krp->krp_status
+			    , krp->krp_crid, krp->krp_hid
+			    , krp->krp_callback
+			);
+		}
+	}
+}
+#endif
+
+int crypto_modevent(module_t mod, int type, void *unused);
+
+/*
+ * Initialization code, both for static and dynamic loading.
+ * Note this is not invoked with the usual MODULE_DECLARE
+ * mechanism but instead is listed as a dependency by the
+ * cryptosoft driver.  This guarantees proper ordering of
+ * calls on module load/unload.
+ */
+int
+crypto_modevent(module_t mod, int type, void *unused)
+{
+	int error = EINVAL;
+
+	switch (type) {
+	case MOD_LOAD:
+		error = crypto_init();
+		if (error == 0 && bootverbose)
+			printf("crypto: <crypto core>\n");
+		break;
+	case MOD_UNLOAD:
+		/*XXX disallow if active sessions */
+		error = 0;
+		crypto_destroy();
+		return 0;
+	}
+	return error;
+}
+MODULE_VERSION(crypto, 1);
+MODULE_DEPEND(crypto, zlib, 1, 1, 1);
Index: xform.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/xform.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/xform.c -L sys/opencrypto/xform.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/xform.c
+++ sys/opencrypto/xform.c
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/xform.c,v 1.5.2.1 2005/08/30 15:01:50 pjd Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/xform.c,v 1.9 2007/05/09 19:37:02 gnn Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -51,6 +51,7 @@
 #include <crypto/blowfish/blowfish.h>
 #include <crypto/des/des.h>
 #include <crypto/rijndael/rijndael.h>
+#include <crypto/camellia/camellia.h>
 #include <crypto/sha1.h>
 
 #include <opencrypto/cast.h>
@@ -74,24 +75,28 @@
 static	int cast5_setkey(u_int8_t **, u_int8_t *, int);
 static	int skipjack_setkey(u_int8_t **, u_int8_t *, int);
 static	int rijndael128_setkey(u_int8_t **, u_int8_t *, int);
+static	int cml_setkey(u_int8_t **, u_int8_t *, int);
 static	void des1_encrypt(caddr_t, u_int8_t *);
 static	void des3_encrypt(caddr_t, u_int8_t *);
 static	void blf_encrypt(caddr_t, u_int8_t *);
 static	void cast5_encrypt(caddr_t, u_int8_t *);
 static	void skipjack_encrypt(caddr_t, u_int8_t *);
 static	void rijndael128_encrypt(caddr_t, u_int8_t *);
+static	void cml_encrypt(caddr_t, u_int8_t *);
 static	void des1_decrypt(caddr_t, u_int8_t *);
 static	void des3_decrypt(caddr_t, u_int8_t *);
 static	void blf_decrypt(caddr_t, u_int8_t *);
 static	void cast5_decrypt(caddr_t, u_int8_t *);
 static	void skipjack_decrypt(caddr_t, u_int8_t *);
 static	void rijndael128_decrypt(caddr_t, u_int8_t *);
+static	void cml_decrypt(caddr_t, u_int8_t *);
 static	void des1_zerokey(u_int8_t **);
 static	void des3_zerokey(u_int8_t **);
 static	void blf_zerokey(u_int8_t **);
 static	void cast5_zerokey(u_int8_t **);
 static	void skipjack_zerokey(u_int8_t **);
 static	void rijndael128_zerokey(u_int8_t **);
+static	void cml_zerokey(u_int8_t **);
 
 static	void null_init(void *);
 static	int null_update(void *, u_int8_t *, u_int16_t);
@@ -114,7 +119,7 @@
 struct enc_xform enc_xform_null = {
 	CRYPTO_NULL_CBC, "NULL",
 	/* NB: blocksize of 4 is to generate a properly aligned ESP header */
-	4, 0, 256, /* 2048 bits, max key */
+	NULL_BLOCK_LEN, 0, 256, /* 2048 bits, max key */
 	null_encrypt,
 	null_decrypt,
 	null_setkey,
@@ -123,7 +128,7 @@
 
 struct enc_xform enc_xform_des = {
 	CRYPTO_DES_CBC, "DES",
-	8, 8, 8,
+	DES_BLOCK_LEN, 8, 8,
 	des1_encrypt,
 	des1_decrypt,
 	des1_setkey,
@@ -132,7 +137,7 @@
 
 struct enc_xform enc_xform_3des = {
 	CRYPTO_3DES_CBC, "3DES",
-	8, 24, 24,
+	DES3_BLOCK_LEN, 24, 24,
 	des3_encrypt,
 	des3_decrypt,
 	des3_setkey,
@@ -141,7 +146,7 @@
 
 struct enc_xform enc_xform_blf = {
 	CRYPTO_BLF_CBC, "Blowfish",
-	8, 5, 56 /* 448 bits, max key */,
+	BLOWFISH_BLOCK_LEN, 5, 56 /* 448 bits, max key */,
 	blf_encrypt,
 	blf_decrypt,
 	blf_setkey,
@@ -150,7 +155,7 @@
 
 struct enc_xform enc_xform_cast5 = {
 	CRYPTO_CAST_CBC, "CAST-128",
-	8, 5, 16,
+	CAST128_BLOCK_LEN, 5, 16,
 	cast5_encrypt,
 	cast5_decrypt,
 	cast5_setkey,
@@ -159,7 +164,7 @@
 
 struct enc_xform enc_xform_skipjack = {
 	CRYPTO_SKIPJACK_CBC, "Skipjack",
-	8, 10, 10,
+	SKIPJACK_BLOCK_LEN, 10, 10,
 	skipjack_encrypt,
 	skipjack_decrypt,
 	skipjack_setkey,
@@ -168,7 +173,7 @@
 
 struct enc_xform enc_xform_rijndael128 = {
 	CRYPTO_RIJNDAEL128_CBC, "Rijndael-128/AES",
-	16, 8, 32,
+	RIJNDAEL128_BLOCK_LEN, 8, 32,
 	rijndael128_encrypt,
 	rijndael128_decrypt,
 	rijndael128_setkey,
@@ -184,63 +189,72 @@
 	NULL,
 };
 
+struct enc_xform enc_xform_camellia = {
+	CRYPTO_CAMELLIA_CBC, "Camellia",
+	CAMELLIA_BLOCK_LEN, 8, 32,
+	cml_encrypt,
+	cml_decrypt,
+	cml_setkey,
+	cml_zerokey,
+};
+
 /* Authentication instances */
 struct auth_hash auth_hash_null = {
 	CRYPTO_NULL_HMAC, "NULL-HMAC",
-	0, 0, 12, sizeof(int),			/* NB: context isn't used */
+	0, NULL_HASH_LEN, NULL_HMAC_BLOCK_LEN, sizeof(int),	/* NB: context isn't used */
 	null_init, null_update, null_final
 };
 
-struct auth_hash auth_hash_hmac_md5_96 = {
+struct auth_hash auth_hash_hmac_md5 = {
 	CRYPTO_MD5_HMAC, "HMAC-MD5",
-	16, 16, 12, sizeof(MD5_CTX),
+	16, MD5_HASH_LEN, MD5_HMAC_BLOCK_LEN, sizeof(MD5_CTX),
 	(void (*) (void *)) MD5Init, MD5Update_int,
 	(void (*) (u_int8_t *, void *)) MD5Final
 };
 
-struct auth_hash auth_hash_hmac_sha1_96 = {
+struct auth_hash auth_hash_hmac_sha1 = {
 	CRYPTO_SHA1_HMAC, "HMAC-SHA1",
-	20, 20, 12, sizeof(SHA1_CTX),
+	20, SHA1_HASH_LEN, SHA1_HMAC_BLOCK_LEN, sizeof(SHA1_CTX),
 	SHA1Init_int, SHA1Update_int, SHA1Final_int
 };
 
-struct auth_hash auth_hash_hmac_ripemd_160_96 = {
+struct auth_hash auth_hash_hmac_ripemd_160 = {
 	CRYPTO_RIPEMD160_HMAC, "HMAC-RIPEMD-160",
-	20, 20, 12, sizeof(RMD160_CTX),
+	20, RIPEMD160_HASH_LEN, RIPEMD160_HMAC_BLOCK_LEN, sizeof(RMD160_CTX),
 	(void (*)(void *)) RMD160Init, RMD160Update_int,
 	(void (*)(u_int8_t *, void *)) RMD160Final
 };
 
 struct auth_hash auth_hash_key_md5 = {
 	CRYPTO_MD5_KPDK, "Keyed MD5", 
-	0, 16, 12, sizeof(MD5_CTX),
+	0, MD5_KPDK_HASH_LEN, 0, sizeof(MD5_CTX),
 	(void (*)(void *)) MD5Init, MD5Update_int,
 	(void (*)(u_int8_t *, void *)) MD5Final
 };
 
 struct auth_hash auth_hash_key_sha1 = {
 	CRYPTO_SHA1_KPDK, "Keyed SHA1",
-	0, 20, 12, sizeof(SHA1_CTX),
+	0, SHA1_KPDK_HASH_LEN, 0, sizeof(SHA1_CTX),
 	SHA1Init_int, SHA1Update_int, SHA1Final_int
 };
 
 struct auth_hash auth_hash_hmac_sha2_256 = {
-	CRYPTO_SHA2_HMAC, "HMAC-SHA2",
-	32, 32, 12, sizeof(SHA256_CTX),
+	CRYPTO_SHA2_256_HMAC, "HMAC-SHA2-256",
+	32, SHA2_256_HASH_LEN, SHA2_256_HMAC_BLOCK_LEN, sizeof(SHA256_CTX),
 	(void (*)(void *)) SHA256_Init, SHA256Update_int,
 	(void (*)(u_int8_t *, void *)) SHA256_Final
 };
 
 struct auth_hash auth_hash_hmac_sha2_384 = {
-	CRYPTO_SHA2_HMAC, "HMAC-SHA2-384",
-	48, 48, 12, sizeof(SHA384_CTX),
+	CRYPTO_SHA2_384_HMAC, "HMAC-SHA2-384",
+	48, SHA2_384_HASH_LEN, SHA2_384_HMAC_BLOCK_LEN, sizeof(SHA384_CTX),
 	(void (*)(void *)) SHA384_Init, SHA384Update_int,
 	(void (*)(u_int8_t *, void *)) SHA384_Final
 };
 
 struct auth_hash auth_hash_hmac_sha2_512 = {
-	CRYPTO_SHA2_HMAC, "HMAC-SHA2-512",
-	64, 64, 12, sizeof(SHA512_CTX),
+	CRYPTO_SHA2_512_HMAC, "HMAC-SHA2-512",
+	64, SHA2_512_HASH_LEN, SHA2_512_HMAC_BLOCK_LEN, sizeof(SHA512_CTX),
 	(void (*)(void *)) SHA512_Init, SHA512Update_int,
 	(void (*)(u_int8_t *, void *)) SHA512_Final
 };
@@ -533,6 +547,45 @@
 	*sched = NULL;
 }
 
+static void
+cml_encrypt(caddr_t key, u_int8_t *blk)
+{
+	camellia_encrypt((camellia_ctx *) key, (u_char *) blk, (u_char *) blk);
+}
+
+static void
+cml_decrypt(caddr_t key, u_int8_t *blk)
+{
+	camellia_decrypt(((camellia_ctx *) key), (u_char *) blk,
+	    (u_char *) blk);
+}
+
+static int
+cml_setkey(u_int8_t **sched, u_int8_t *key, int len)
+{
+	int err;
+
+	if (len != 16 && len != 24 && len != 32)
+		return (EINVAL);
+	MALLOC(*sched, u_int8_t *, sizeof(camellia_ctx), M_CRYPTO_DATA,
+	    M_NOWAIT|M_ZERO);
+	if (*sched != NULL) {
+		camellia_set_key((camellia_ctx *) *sched, (u_char *) key,
+		    len * 8);
+		err = 0;
+	} else
+		err = ENOMEM;
+	return err;
+}
+
+static void
+cml_zerokey(u_int8_t **sched)
+{
+	bzero(*sched, sizeof(camellia_ctx));
+	FREE(*sched, M_CRYPTO_DATA);
+	*sched = NULL;
+}
+
 /*
  * And now for auth.
  */
--- sys/opencrypto/crypto_if.m
+++ /dev/null
@@ -1,128 +0,0 @@
-#-
-# Copyright (c) 2002, Sam Leffler
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Boris Popov.
-# 4. Neither the name of the author nor the names of any co-contributors
-#    may be used to endorse or promote products derived from this software
-#    without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY 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/opencrypto/crypto_if.m,v 1.2 2005/01/07 02:29:16 imp Exp $
-#
-
-#include <crypto/cryptodev.h>
-
-INTERFACE crypto;
-
-METHOD int32_t get_driverid {
-	u_int32_t	flags;
-};
-
-# XXX define typedefs to work around inadequate parser
-HEADER {
-	typedef int crypto_newsession_cb(void*, u_int32_t*, struct cryptoini*);
-	typedef int crypto_freesession_cb(void*, u_int64_t*);
-	typedef int crypto_process_cb(void*, struct cryptop*);
-	typedef int crypto_kprocess_cb(void*, struct cryptkop*);
-};
-
-METHOD int register {
-	u_int32_t	driverid;
-	int		alg;
-	u_int16_t	maxoplen;
-	u_int32_t	flags;
-	crypto_newsession_cb* newses;
-	crypto_freesession_cb* freeses;
-	crypto_process_cb* process;
-	void		*arg;
-};
-
-METHOD int kregister {
-	u_int32_t	driverid;
-	int		kalg;
-	u_int32_t	flags;
-	crypto_kprocess_cb* kprocess;
-	void		*arg;
-};
-
-METHOD int unregister {
-	u_int32_t	driverid;
-	int		alg;
-};
-
-METHOD int unregister_all {
-	u_int32_t	driverid;
-};
-
-METHOD int newsession {
-	u_int64_t	*sid;
-	struct cryptoini *cri;
-	int		hard;
-};
-
-METHOD int freesession {
-	u_int64_t	sid;
-};
-
-METHOD int dispatch {
-	struct cryptop	*crp;
-};
-
-METHOD int kdispatch {
-	struct cryptkop	*krp;
-};
-
-METHOD int crypto_unblock {
-	u_int32_t	driverid;
-	int		what;
-};
-
-METHOD int invoke {
-	struct cryptop	*crp;
-};
-
-METHOD int kinvoke {
-	struct cryptkop	*krp;
-};
-
-METHOD struct cryptop * getreq {
-	int	num;
-};
-
-METHOD void freereq {
-	struct cryptop	*crp;
-};
-
-METHOD void done {
-	struct cryptop	*crp;
-};
-
-METHOD void kdone {
-	struct cryptkop	*krp;
-};
-
-METHOD int getfeat {
-	int		*featp;
-};
Index: cast.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cast.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cast.c -L sys/opencrypto/cast.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cast.c
+++ sys/opencrypto/cast.c
@@ -7,7 +7,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/cast.c,v 1.3 2005/01/07 02:29:16 imp Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/cast.c,v 1.4 2007/07/05 06:59:14 peter Exp $");
 
 #include <sys/types.h>
 #include <opencrypto/cast.h>
@@ -131,7 +131,7 @@
 
 void cast_setkey(cast_key* key, u_int8_t* rawkey, int keybytes)
 {
-u_int32_t t[4], z[4], x[4];
+u_int32_t t[4] = {0, 0, 0, 0}, z[4] = {0, 0, 0, 0}, x[4];
 int i;
 
 	/* Set number of rounds to 12 or 16, depending on key length */
Index: cryptosoft.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptosoft.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cryptosoft.c -L sys/opencrypto/cryptosoft.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cryptosoft.c
+++ sys/opencrypto/cryptosoft.c
@@ -2,6 +2,7 @@
 
 /*-
  * The author of this code is Angelos D. Keromytis (angelos at cis.upenn.edu)
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
  *
  * This code was written by Angelos D. Keromytis in Athens, Greece, in
  * February 2000. Network Security Technologies Inc. (NSTI) kindly
@@ -22,12 +23,13 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/cryptosoft.c,v 1.9 2005/03/11 12:37:06 ume Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/cryptosoft.c,v 1.19 2007/05/09 19:37:02 gnn Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
+#include <sys/module.h>
 #include <sys/sysctl.h>
 #include <sys/errno.h>
 #include <sys/random.h>
@@ -45,54 +47,28 @@
 #include <opencrypto/cryptosoft.h>
 #include <opencrypto/xform.h>
 
-u_int8_t hmac_ipad_buffer[64] = {
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
-};
-
-u_int8_t hmac_opad_buffer[64] = {
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
-	0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C
-};
+#include <sys/kobj.h>
+#include <sys/bus.h>
+#include "cryptodev_if.h"
+
+static	int32_t swcr_id;
+static	struct swcr_data **swcr_sessions = NULL;
+static	u_int32_t swcr_sesnum;
 
-
-struct swcr_data **swcr_sessions = NULL;
-u_int32_t swcr_sesnum = 0;
-int32_t swcr_id = -1;
-
-#define COPYBACK(x, a, b, c, d) \
-	(x) == CRYPTO_BUF_MBUF ? m_copyback((struct mbuf *)a,b,c,d) \
-	: cuio_copyback((struct uio *)a,b,c,d)
-#define COPYDATA(x, a, b, c, d) \
-	(x) == CRYPTO_BUF_MBUF ? m_copydata((struct mbuf *)a,b,c,d) \
-	: cuio_copydata((struct uio *)a,b,c,d)
+u_int8_t hmac_ipad_buffer[HMAC_MAX_BLOCK_LEN];
+u_int8_t hmac_opad_buffer[HMAC_MAX_BLOCK_LEN];
 
 static	int swcr_encdec(struct cryptodesc *, struct swcr_data *, caddr_t, int);
-static	int swcr_authcompute(struct cryptop *crp, struct cryptodesc *crd,
-			     struct swcr_data *sw, caddr_t buf, int outtype);
+static	int swcr_authcompute(struct cryptodesc *, struct swcr_data *, caddr_t, int);
 static	int swcr_compdec(struct cryptodesc *, struct swcr_data *, caddr_t, int);
-static	int swcr_process(void *, struct cryptop *, int);
-static	int swcr_newsession(void *, u_int32_t *, struct cryptoini *);
-static	int swcr_freesession(void *, u_int64_t);
+static	int swcr_freesession(device_t dev, u_int64_t tid);
 
 /*
  * Apply a symmetric encryption/decryption algorithm.
  */
 static int
 swcr_encdec(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf,
-    int outtype)
+    int flags)
 {
 	unsigned char iv[EALG_MAX_BLOCK_LEN], blk[EALG_MAX_BLOCK_LEN], *idat;
 	unsigned char *ivp, piv[EALG_MAX_BLOCK_LEN];
@@ -111,32 +87,12 @@
 		/* IV explicitly provided ? */
 		if (crd->crd_flags & CRD_F_IV_EXPLICIT)
 			bcopy(crd->crd_iv, iv, blks);
-		else {
-			/* Get random IV */
-			for (i = 0;
-			    i + sizeof (u_int32_t) < EALG_MAX_BLOCK_LEN;
-			    i += sizeof (u_int32_t)) {
-				u_int32_t temp = arc4random();
-
-				bcopy(&temp, iv + i, sizeof(u_int32_t));
-			}
-			/*
-			 * What if the block size is not a multiple
-			 * of sizeof (u_int32_t), which is the size of
-			 * what arc4random() returns ?
-			 */
-			if (EALG_MAX_BLOCK_LEN % sizeof (u_int32_t) != 0) {
-				u_int32_t temp = arc4random();
-
-				bcopy (&temp, iv + i,
-				    EALG_MAX_BLOCK_LEN - i);
-			}
-		}
+		else
+			arc4rand(iv, blks, 0);
 
 		/* Do we need to write the IV */
-		if (!(crd->crd_flags & CRD_F_IV_PRESENT)) {
-			COPYBACK(outtype, buf, crd->crd_inject, blks, iv);
-		}
+		if (!(crd->crd_flags & CRD_F_IV_PRESENT))
+			crypto_copyback(flags, buf, crd->crd_inject, blks, iv);
 
 	} else {	/* Decryption */
 			/* IV explicitly provided ? */
@@ -144,7 +100,7 @@
 			bcopy(crd->crd_iv, iv, blks);
 		else {
 			/* Get IV off buf */
-			COPYDATA(outtype, buf, crd->crd_inject, blks, iv);
+			crypto_copydata(flags, buf, crd->crd_inject, blks, iv);
 		}
 	}
 
@@ -160,40 +116,7 @@
 	}
 	ivp = iv;
 
-	if (outtype == CRYPTO_BUF_CONTIG) {
-		if (crd->crd_flags & CRD_F_ENCRYPT) {
-			for (i = crd->crd_skip;
-			    i < crd->crd_skip + crd->crd_len; i += blks) {
-				/* XOR with the IV/previous block, as appropriate. */
-				if (i == crd->crd_skip)
-					for (k = 0; k < blks; k++)
-						buf[i + k] ^= ivp[k];
-				else
-					for (k = 0; k < blks; k++)
-						buf[i + k] ^= buf[i + k - blks];
-				exf->encrypt(sw->sw_kschedule, buf + i);
-			}
-		} else {		/* Decrypt */
-			/*
-			 * Start at the end, so we don't need to keep the encrypted
-			 * block as the IV for the next block.
-			 */
-			for (i = crd->crd_skip + crd->crd_len - blks;
-			    i >= crd->crd_skip; i -= blks) {
-				exf->decrypt(sw->sw_kschedule, buf + i);
-
-				/* XOR with the IV/previous block, as appropriate */
-				if (i == crd->crd_skip)
-					for (k = 0; k < blks; k++)
-						buf[i + k] ^= ivp[k];
-				else
-					for (k = 0; k < blks; k++)
-						buf[i + k] ^= buf[i + k - blks];
-			}
-		}
-
-		return 0;
-	} else if (outtype == CRYPTO_BUF_MBUF) {
+	if (flags & CRYPTO_F_IMBUF) {
 		struct mbuf *m = (struct mbuf *) buf;
 
 		/* Find beginning of data */
@@ -318,7 +241,7 @@
 		}
 
 		return 0; /* Done with mbuf encryption/decryption */
-	} else if (outtype == CRYPTO_BUF_IOV) {
+	} else if (flags & CRYPTO_F_IOV) {
 		struct uio *uio = (struct uio *) buf;
 		struct iovec *iov;
 
@@ -431,21 +354,101 @@
 			}
 		}
 
-		return 0; /* Done with mbuf encryption/decryption */
+		return 0; /* Done with iovec encryption/decryption */
+	} else {	/* contiguous buffer */
+		if (crd->crd_flags & CRD_F_ENCRYPT) {
+			for (i = crd->crd_skip;
+			    i < crd->crd_skip + crd->crd_len; i += blks) {
+				/* XOR with the IV/previous block, as appropriate. */
+				if (i == crd->crd_skip)
+					for (k = 0; k < blks; k++)
+						buf[i + k] ^= ivp[k];
+				else
+					for (k = 0; k < blks; k++)
+						buf[i + k] ^= buf[i + k - blks];
+				exf->encrypt(sw->sw_kschedule, buf + i);
+			}
+		} else {		/* Decrypt */
+			/*
+			 * Start at the end, so we don't need to keep the encrypted
+			 * block as the IV for the next block.
+			 */
+			for (i = crd->crd_skip + crd->crd_len - blks;
+			    i >= crd->crd_skip; i -= blks) {
+				exf->decrypt(sw->sw_kschedule, buf + i);
+
+				/* XOR with the IV/previous block, as appropriate */
+				if (i == crd->crd_skip)
+					for (k = 0; k < blks; k++)
+						buf[i + k] ^= ivp[k];
+				else
+					for (k = 0; k < blks; k++)
+						buf[i + k] ^= buf[i + k - blks];
+			}
+		}
+
+		return 0; /* Done with contiguous buffer encryption/decryption */
 	}
 
 	/* Unreachable */
 	return EINVAL;
 }
 
+static void
+swcr_authprepare(struct auth_hash *axf, struct swcr_data *sw, u_char *key,
+    int klen)
+{
+	int k;
+
+	klen /= 8;
+
+	switch (axf->type) {
+	case CRYPTO_MD5_HMAC:
+	case CRYPTO_SHA1_HMAC:
+	case CRYPTO_SHA2_256_HMAC:
+	case CRYPTO_SHA2_384_HMAC:
+	case CRYPTO_SHA2_512_HMAC:
+	case CRYPTO_NULL_HMAC:
+	case CRYPTO_RIPEMD160_HMAC:
+		for (k = 0; k < klen; k++)
+			key[k] ^= HMAC_IPAD_VAL;
+	
+		axf->Init(sw->sw_ictx);
+		axf->Update(sw->sw_ictx, key, klen);
+		axf->Update(sw->sw_ictx, hmac_ipad_buffer, axf->blocksize - klen);
+	
+		for (k = 0; k < klen; k++)
+			key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
+	
+		axf->Init(sw->sw_octx);
+		axf->Update(sw->sw_octx, key, klen);
+		axf->Update(sw->sw_octx, hmac_opad_buffer, axf->blocksize - klen);
+	
+		for (k = 0; k < klen; k++)
+			key[k] ^= HMAC_OPAD_VAL;
+		break;
+	case CRYPTO_MD5_KPDK:
+	case CRYPTO_SHA1_KPDK:
+		sw->sw_klen = klen;
+		bcopy(key, sw->sw_octx, klen);
+		axf->Init(sw->sw_ictx);
+		axf->Update(sw->sw_ictx, key, klen);
+		axf->Final(NULL, sw->sw_ictx);
+		break;
+	default:
+		printf("%s: CRD_F_KEY_EXPLICIT flag given, but algorithm %d "
+		    "doesn't use keys.\n", __func__, axf->type);
+	}
+}
+
 /*
  * Compute keyed-hash authenticator.
  */
 static int
-swcr_authcompute(struct cryptop *crp, struct cryptodesc *crd,
-    struct swcr_data *sw, caddr_t buf, int outtype)
+swcr_authcompute(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf,
+    int flags)
 {
-	unsigned char aalg[AALG_MAX_RESULT_LEN];
+	unsigned char aalg[HASH_MAX_LEN];
 	struct auth_hash *axf;
 	union authctx ctx;
 	int err;
@@ -455,28 +458,22 @@
 
 	axf = sw->sw_axf;
 
+	if (crd->crd_flags & CRD_F_KEY_EXPLICIT)
+		swcr_authprepare(axf, sw, crd->crd_key, crd->crd_klen);
+
 	bcopy(sw->sw_ictx, &ctx, axf->ctxsize);
 
-	switch (outtype) {
-	case CRYPTO_BUF_CONTIG:
-		axf->Update(&ctx, buf + crd->crd_skip, crd->crd_len);
-		break;
-	case CRYPTO_BUF_MBUF:
-		err = m_apply((struct mbuf *) buf, crd->crd_skip, crd->crd_len,
-		    (int (*)(void *, void *, unsigned int)) axf->Update,
-		    (caddr_t) &ctx);
-		if (err)
-			return err;
-		break;
-	case CRYPTO_BUF_IOV:
-	default:
-		return EINVAL;
-	}
+	err = crypto_apply(flags, buf, crd->crd_skip, crd->crd_len,
+	    (int (*)(void *, void *, unsigned int))axf->Update, (caddr_t)&ctx);
+	if (err)
+		return err;
 
 	switch (sw->sw_alg) {
 	case CRYPTO_MD5_HMAC:
 	case CRYPTO_SHA1_HMAC:
-	case CRYPTO_SHA2_HMAC:
+	case CRYPTO_SHA2_256_HMAC:
+	case CRYPTO_SHA2_384_HMAC:
+	case CRYPTO_SHA2_512_HMAC:
 	case CRYPTO_RIPEMD160_HMAC:
 		if (sw->sw_octx == NULL)
 			return EINVAL;
@@ -502,11 +499,8 @@
 	}
 
 	/* Inject the authentication data */
-	if (outtype == CRYPTO_BUF_CONTIG)
-		bcopy(aalg, buf + crd->crd_inject, axf->authsize);
-	else
-		m_copyback((struct mbuf *) buf, crd->crd_inject,
-		    axf->authsize, aalg);
+	crypto_copyback(flags, buf, crd->crd_inject,
+	    sw->sw_mlen == 0 ? axf->hashsize : sw->sw_mlen, aalg);
 	return 0;
 }
 
@@ -515,7 +509,7 @@
  */
 static int
 swcr_compdec(struct cryptodesc *crd, struct swcr_data *sw,
-    caddr_t buf, int outtype)
+    caddr_t buf, int flags)
 {
 	u_int8_t *data, *out;
 	struct comp_algo *cxf;
@@ -532,7 +526,7 @@
 	MALLOC(data, u_int8_t *, crd->crd_len, M_CRYPTO_DATA,  M_NOWAIT);
 	if (data == NULL)
 		return (EINVAL);
-	COPYDATA(outtype, buf, crd->crd_skip, crd->crd_len, data);
+	crypto_copydata(flags, buf, crd->crd_skip, crd->crd_len, data);
 
 	if (crd->crd_flags & CRD_F_COMP)
 		result = cxf->compress(data, crd->crd_len, &out);
@@ -556,13 +550,13 @@
 		}
 	}
 
-	COPYBACK(outtype, buf, crd->crd_skip, result, out);
+	crypto_copyback(flags, buf, crd->crd_skip, result, out);
 	if (result < crd->crd_len) {
 		adj = result - crd->crd_len;
-		if (outtype == CRYPTO_BUF_MBUF) {
+		if (flags & CRYPTO_F_IMBUF) {
 			adj = result - crd->crd_len;
 			m_adj((struct mbuf *)buf, adj);
-		} else {
+		} else if (flags & CRYPTO_F_IOV) {
 			struct uio *uio = (struct uio *)buf;
 			int ind;
 
@@ -590,14 +584,14 @@
  * Generate a new software session.
  */
 static int
-swcr_newsession(void *arg, u_int32_t *sid, struct cryptoini *cri)
+swcr_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri)
 {
 	struct swcr_data **swd;
 	struct auth_hash *axf;
 	struct enc_xform *txf;
 	struct comp_algo *cxf;
 	u_int32_t i;
-	int k, error;
+	int error;
 
 	if (sid == NULL || cri == NULL)
 		return EINVAL;
@@ -628,7 +622,7 @@
 		}
 
 		/* Copy existing sessions */
-		if (swcr_sessions) {
+		if (swcr_sessions != NULL) {
 			bcopy(swcr_sessions, swd,
 			    (swcr_sesnum / 2) * sizeof(struct swcr_data *));
 			free(swcr_sessions, M_CRYPTO_DATA);
@@ -644,7 +638,7 @@
 		MALLOC(*swd, struct swcr_data *, sizeof(struct swcr_data),
 		    M_CRYPTO_DATA, M_NOWAIT|M_ZERO);
 		if (*swd == NULL) {
-			swcr_freesession(NULL, i);
+			swcr_freesession(dev, i);
 			return ENOBUFS;
 		}
 
@@ -667,77 +661,65 @@
 		case CRYPTO_RIJNDAEL128_CBC:
 			txf = &enc_xform_rijndael128;
 			goto enccommon;
+		case CRYPTO_CAMELLIA_CBC:
+			txf = &enc_xform_camellia;
+			goto enccommon;
 		case CRYPTO_NULL_CBC:
 			txf = &enc_xform_null;
 			goto enccommon;
 		enccommon:
-			error = txf->setkey(&((*swd)->sw_kschedule),
-					cri->cri_key, cri->cri_klen / 8);
-			if (error) {
-				swcr_freesession(NULL, i);
-				return error;
+			if (cri->cri_key != NULL) {
+				error = txf->setkey(&((*swd)->sw_kschedule),
+				    cri->cri_key, cri->cri_klen / 8);
+				if (error) {
+					swcr_freesession(dev, i);
+					return error;
+				}
 			}
 			(*swd)->sw_exf = txf;
 			break;
 	
 		case CRYPTO_MD5_HMAC:
-			axf = &auth_hash_hmac_md5_96;
+			axf = &auth_hash_hmac_md5;
 			goto authcommon;
 		case CRYPTO_SHA1_HMAC:
-			axf = &auth_hash_hmac_sha1_96;
+			axf = &auth_hash_hmac_sha1;
 			goto authcommon;
-		case CRYPTO_SHA2_HMAC:
-			if (cri->cri_klen == 256)
-				axf = &auth_hash_hmac_sha2_256;
-			else if (cri->cri_klen == 384)
-				axf = &auth_hash_hmac_sha2_384;
-			else if (cri->cri_klen == 512)
-				axf = &auth_hash_hmac_sha2_512;
-			else {
-				swcr_freesession(NULL, i);
-				return EINVAL;
-			}
+		case CRYPTO_SHA2_256_HMAC:
+			axf = &auth_hash_hmac_sha2_256;
+			goto authcommon;
+		case CRYPTO_SHA2_384_HMAC:
+			axf = &auth_hash_hmac_sha2_384;
+			goto authcommon;
+		case CRYPTO_SHA2_512_HMAC:
+			axf = &auth_hash_hmac_sha2_512;
 			goto authcommon;
 		case CRYPTO_NULL_HMAC:
 			axf = &auth_hash_null;
 			goto authcommon;
 		case CRYPTO_RIPEMD160_HMAC:
-			axf = &auth_hash_hmac_ripemd_160_96;
+			axf = &auth_hash_hmac_ripemd_160;
 		authcommon:
 			(*swd)->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
 			    M_NOWAIT);
 			if ((*swd)->sw_ictx == NULL) {
-				swcr_freesession(NULL, i);
+				swcr_freesession(dev, i);
 				return ENOBUFS;
 			}
 	
 			(*swd)->sw_octx = malloc(axf->ctxsize, M_CRYPTO_DATA,
 			    M_NOWAIT);
 			if ((*swd)->sw_octx == NULL) {
-				swcr_freesession(NULL, i);
+				swcr_freesession(dev, i);
 				return ENOBUFS;
 			}
-	
-			for (k = 0; k < cri->cri_klen / 8; k++)
-				cri->cri_key[k] ^= HMAC_IPAD_VAL;
-	
-			axf->Init((*swd)->sw_ictx);
-			axf->Update((*swd)->sw_ictx, cri->cri_key,
-			    cri->cri_klen / 8);
-			axf->Update((*swd)->sw_ictx, hmac_ipad_buffer,
-			    HMAC_BLOCK_LEN - (cri->cri_klen / 8));
-	
-			for (k = 0; k < cri->cri_klen / 8; k++)
-				cri->cri_key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
-	
-			axf->Init((*swd)->sw_octx);
-			axf->Update((*swd)->sw_octx, cri->cri_key,
-			    cri->cri_klen / 8);
-			axf->Update((*swd)->sw_octx, hmac_opad_buffer,
-			    HMAC_BLOCK_LEN - (cri->cri_klen / 8));
-	
-			for (k = 0; k < cri->cri_klen / 8; k++)
-				cri->cri_key[k] ^= HMAC_OPAD_VAL;
+
+			if (cri->cri_key != NULL) {
+				swcr_authprepare(axf, *swd, cri->cri_key,
+				    cri->cri_klen);
+			}
+
+			(*swd)->sw_mlen = cri->cri_mlen;
 			(*swd)->sw_axf = axf;
 			break;
 	
@@ -751,24 +733,24 @@
 			(*swd)->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
 			    M_NOWAIT);
 			if ((*swd)->sw_ictx == NULL) {
-				swcr_freesession(NULL, i);
+				swcr_freesession(dev, i);
 				return ENOBUFS;
 			}
 	
-			/* Store the key so we can "append" it to the payload */
-			(*swd)->sw_octx = malloc(cri->cri_klen / 8, M_CRYPTO_DATA,
-			    M_NOWAIT);
+			(*swd)->sw_octx = malloc(cri->cri_klen / 8,
+			    M_CRYPTO_DATA, M_NOWAIT);
 			if ((*swd)->sw_octx == NULL) {
-				swcr_freesession(NULL, i);
+				swcr_freesession(dev, i);
 				return ENOBUFS;
 			}
-	
-			(*swd)->sw_klen = cri->cri_klen / 8;
-			bcopy(cri->cri_key, (*swd)->sw_octx, cri->cri_klen / 8);
-			axf->Init((*swd)->sw_ictx);
-			axf->Update((*swd)->sw_ictx, cri->cri_key,
-			    cri->cri_klen / 8);
-			axf->Final(NULL, (*swd)->sw_ictx);
+
+			/* Store the key so we can "append" it to the payload */
+			if (cri->cri_key != NULL) {
+				swcr_authprepare(axf, *swd, cri->cri_key,
+				    cri->cri_klen);
+			}
+
+			(*swd)->sw_mlen = cri->cri_mlen;
 			(*swd)->sw_axf = axf;
 			break;
 #ifdef notdef
@@ -782,11 +764,12 @@
 			(*swd)->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
 			    M_NOWAIT);
 			if ((*swd)->sw_ictx == NULL) {
-				swcr_freesession(NULL, i);
+				swcr_freesession(dev, i);
 				return ENOBUFS;
 			}
 
 			axf->Init((*swd)->sw_ictx);
+			(*swd)->sw_mlen = cri->cri_mlen;
 			(*swd)->sw_axf = axf;
 			break;
 #endif
@@ -795,7 +778,7 @@
 			(*swd)->sw_cxf = cxf;
 			break;
 		default:
-			swcr_freesession(NULL, i);
+			swcr_freesession(dev, i);
 			return EINVAL;
 		}
 	
@@ -810,7 +793,7 @@
  * Free a session.
  */
 static int
-swcr_freesession(void *arg, u_int64_t tid)
+swcr_freesession(device_t dev, u_int64_t tid)
 {
 	struct swcr_data *swd;
 	struct enc_xform *txf;
@@ -836,6 +819,7 @@
 		case CRYPTO_CAST_CBC:
 		case CRYPTO_SKIPJACK_CBC:
 		case CRYPTO_RIJNDAEL128_CBC:
+		case CRYPTO_CAMELLIA_CBC:
 		case CRYPTO_NULL_CBC:
 			txf = swd->sw_exf;
 
@@ -845,7 +829,9 @@
 
 		case CRYPTO_MD5_HMAC:
 		case CRYPTO_SHA1_HMAC:
-		case CRYPTO_SHA2_HMAC:
+		case CRYPTO_SHA2_256_HMAC:
+		case CRYPTO_SHA2_384_HMAC:
+		case CRYPTO_SHA2_512_HMAC:
 		case CRYPTO_RIPEMD160_HMAC:
 		case CRYPTO_NULL_HMAC:
 			axf = swd->sw_axf;
@@ -896,12 +882,11 @@
  * Process a software request.
  */
 static int
-swcr_process(void *arg, struct cryptop *crp, int hint)
+swcr_process(device_t dev, struct cryptop *crp, int hint)
 {
 	struct cryptodesc *crd;
 	struct swcr_data *sw;
 	u_int32_t lid;
-	int type;
 
 	/* Sanity check */
 	if (crp == NULL)
@@ -918,14 +903,6 @@
 		goto done;
 	}
 
-	if (crp->crp_flags & CRYPTO_F_IMBUF) {
-		type = CRYPTO_BUF_MBUF;
-	} else if (crp->crp_flags & CRYPTO_F_IOV) {
-		type = CRYPTO_BUF_IOV;
-	} else {
-		type = CRYPTO_BUF_CONTIG;
-	}
-
 	/* Go through crypto descriptors, processing as we go */
 	for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
 		/*
@@ -955,8 +932,9 @@
 		case CRYPTO_CAST_CBC:
 		case CRYPTO_SKIPJACK_CBC:
 		case CRYPTO_RIJNDAEL128_CBC:
+		case CRYPTO_CAMELLIA_CBC:
 			if ((crp->crp_etype = swcr_encdec(crd, sw,
-			    crp->crp_buf, type)) != 0)
+			    crp->crp_buf, crp->crp_flags)) != 0)
 				goto done;
 			break;
 		case CRYPTO_NULL_CBC:
@@ -964,21 +942,23 @@
 			break;
 		case CRYPTO_MD5_HMAC:
 		case CRYPTO_SHA1_HMAC:
-		case CRYPTO_SHA2_HMAC:
+		case CRYPTO_SHA2_256_HMAC:
+		case CRYPTO_SHA2_384_HMAC:
+		case CRYPTO_SHA2_512_HMAC:
 		case CRYPTO_RIPEMD160_HMAC:
 		case CRYPTO_NULL_HMAC:
 		case CRYPTO_MD5_KPDK:
 		case CRYPTO_SHA1_KPDK:
 		case CRYPTO_MD5:
 		case CRYPTO_SHA1:
-			if ((crp->crp_etype = swcr_authcompute(crp, crd, sw,
-			    crp->crp_buf, type)) != 0)
+			if ((crp->crp_etype = swcr_authcompute(crd, sw,
+			    crp->crp_buf, crp->crp_flags)) != 0)
 				goto done;
 			break;
 
 		case CRYPTO_DEFLATE_COMP:
 			if ((crp->crp_etype = swcr_compdec(crd, sw, 
-			    crp->crp_buf, type)) != 0)
+			    crp->crp_buf, crp->crp_flags)) != 0)
 				goto done;
 			else
 				crp->crp_olen = (int)sw->sw_size;
@@ -996,19 +976,37 @@
 	return 0;
 }
 
-/*
- * Initialize the driver, called from the kernel main().
- */
 static void
-swcr_init(void)
+swcr_identify(device_t *dev, device_t parent)
+{
+	/* NB: order 10 is so we get attached after h/w devices */
+	if (device_find_child(parent, "cryptosoft", -1) == NULL &&
+	    BUS_ADD_CHILD(parent, 10, "cryptosoft", -1) == 0)
+		panic("cryptosoft: could not attach");
+}
+
+static int
+swcr_probe(device_t dev)
+{
+	device_set_desc(dev, "software crypto");
+	return (0);
+}
+
+static int
+swcr_attach(device_t dev)
 {
-	swcr_id = crypto_get_driverid(CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC);
-	if (swcr_id < 0)
-		panic("Software crypto device cannot initialize!");
-	crypto_register(swcr_id, CRYPTO_DES_CBC,
-	    0, 0, swcr_newsession, swcr_freesession, swcr_process, NULL);
+	memset(hmac_ipad_buffer, HMAC_IPAD_VAL, HMAC_MAX_BLOCK_LEN);
+	memset(hmac_opad_buffer, HMAC_OPAD_VAL, HMAC_MAX_BLOCK_LEN);
+
+	swcr_id = crypto_get_driverid(dev,
+			CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC);
+	if (swcr_id < 0) {
+		device_printf(dev, "cannot initialize!");
+		return ENOMEM;
+	}
 #define	REGISTER(alg) \
-	crypto_register(swcr_id, alg, 0,0,NULL,NULL,NULL,NULL)
+	crypto_register(swcr_id, alg, 0,0)
+	REGISTER(CRYPTO_DES_CBC);
 	REGISTER(CRYPTO_3DES_CBC);
 	REGISTER(CRYPTO_BLF_CBC);
 	REGISTER(CRYPTO_CAST_CBC);
@@ -1016,7 +1014,9 @@
 	REGISTER(CRYPTO_NULL_CBC);
 	REGISTER(CRYPTO_MD5_HMAC);
 	REGISTER(CRYPTO_SHA1_HMAC);
-	REGISTER(CRYPTO_SHA2_HMAC);
+	REGISTER(CRYPTO_SHA2_256_HMAC);
+	REGISTER(CRYPTO_SHA2_384_HMAC);
+	REGISTER(CRYPTO_SHA2_512_HMAC);
 	REGISTER(CRYPTO_RIPEMD160_HMAC);
 	REGISTER(CRYPTO_NULL_HMAC);
 	REGISTER(CRYPTO_MD5_KPDK);
@@ -1024,7 +1024,50 @@
 	REGISTER(CRYPTO_MD5);
 	REGISTER(CRYPTO_SHA1);
 	REGISTER(CRYPTO_RIJNDAEL128_CBC);
+ 	REGISTER(CRYPTO_CAMELLIA_CBC);
 	REGISTER(CRYPTO_DEFLATE_COMP);
 #undef REGISTER
+
+	return 0;
 }
-SYSINIT(cryptosoft_init, SI_SUB_PSEUDO, SI_ORDER_ANY, swcr_init, NULL)
+
+static void
+swcr_detach(device_t dev)
+{
+	crypto_unregister_all(swcr_id);
+	if (swcr_sessions != NULL)
+		FREE(swcr_sessions, M_CRYPTO_DATA);
+}
+
+static device_method_t swcr_methods[] = {
+	DEVMETHOD(device_identify,	swcr_identify),
+	DEVMETHOD(device_probe,		swcr_probe),
+	DEVMETHOD(device_attach,	swcr_attach),
+	DEVMETHOD(device_detach,	swcr_detach),
+
+	DEVMETHOD(cryptodev_newsession,	swcr_newsession),
+	DEVMETHOD(cryptodev_freesession,swcr_freesession),
+	DEVMETHOD(cryptodev_process,	swcr_process),
+
+	{0, 0},
+};
+
+static driver_t swcr_driver = {
+	"cryptosoft",
+	swcr_methods,
+	0,		/* NB: no softc */
+};
+static devclass_t swcr_devclass;
+
+/*
+ * NB: We explicitly reference the crypto module so we
+ * get the necessary ordering when built as a loadable
+ * module.  This is required because we bundle the crypto
+ * module code together with the cryptosoft driver (otherwise
+ * normal module dependencies would handle things).
+ */
+extern int crypto_modevent(struct module *, int, void *);
+/* XXX where to attach */
+DRIVER_MODULE(cryptosoft, nexus, swcr_driver, swcr_devclass, crypto_modevent,0);
+MODULE_VERSION(cryptosoft, 1);
+MODULE_DEPEND(cryptosoft, crypto, 1, 1, 1);


More information about the Midnightbsd-cvs mailing list