changeset 10266:bbc5945eddd7

6814606 Solaris gldv3/wifi needs to support 802.11n 6814289 iwh needs to support 802.11n
author Quaker Fang <Quaker.Fang@Sun.COM>
date Thu, 06 Aug 2009 13:05:38 +0800
parents b988146c84e5
children dd1cf2c420df
files usr/src/lib/libdladm/common/libdlwlan.c usr/src/lib/libdladm/common/libdlwlan.h usr/src/pkgdefs/etc/exception_list_i386 usr/src/pkgdefs/etc/exception_list_sparc usr/src/uts/common/Makefile.files usr/src/uts/common/inet/wifi_ioctl.h usr/src/uts/common/io/arn/arn_core.h usr/src/uts/common/io/iwh/iwh.c usr/src/uts/common/io/iwh/iwh_hw.h usr/src/uts/common/io/iwh/iwh_var.h usr/src/uts/common/io/mac/plugins/mac_wifi.c usr/src/uts/common/io/net80211/net80211.c usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c usr/src/uts/common/io/net80211/net80211_crypto_tkip.c usr/src/uts/common/io/net80211/net80211_crypto_wep.c usr/src/uts/common/io/net80211/net80211_ht.c usr/src/uts/common/io/net80211/net80211_impl.h usr/src/uts/common/io/net80211/net80211_input.c usr/src/uts/common/io/net80211/net80211_ioctl.c usr/src/uts/common/io/net80211/net80211_node.c usr/src/uts/common/io/net80211/net80211_output.c usr/src/uts/common/io/net80211/net80211_proto.c usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/mac_wifi.h usr/src/uts/common/sys/net80211.h usr/src/uts/common/sys/net80211_ht.h usr/src/uts/common/sys/net80211_proto.h
diffstat 27 files changed, 5882 insertions(+), 848 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/libdladm/common/libdlwlan.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/lib/libdladm/common/libdlwlan.c	Thu Aug 06 13:05:38 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -122,6 +122,8 @@
 	{ "a",		DLADM_WLAN_MODE_80211A		},
 	{ "b",		DLADM_WLAN_MODE_80211B		},
 	{ "g",		DLADM_WLAN_MODE_80211G		},
+	{ "n",		DLADM_WLAN_MODE_80211GN		},
+	{ "n",		DLADM_WLAN_MODE_80211AN		}
 };
 
 static val_desc_t	auth_vals[] = {
@@ -161,11 +163,16 @@
 static dladm_wlan_mode_t
 do_convert_mode(wl_phy_conf_t *phyp)
 {
+	wl_erp_t *wlep = &phyp->wl_phy_erp_conf;
+	wl_ofdm_t *wlop = &phyp->wl_phy_ofdm_conf;
+
 	switch (phyp->wl_phy_fhss_conf.wl_fhss_subtype) {
 	case WL_ERP:
-		return (DLADM_WLAN_MODE_80211G);
+		return (wlep->wl_erp_ht_enabled ?
+		    DLADM_WLAN_MODE_80211GN : DLADM_WLAN_MODE_80211G);
 	case WL_OFDM:
-		return (DLADM_WLAN_MODE_80211A);
+		return (wlop->wl_ofdm_ht_enabled ?
+		    DLADM_WLAN_MODE_80211AN : DLADM_WLAN_MODE_80211A);
 	case WL_DSSS:
 	case WL_FHSS:
 		return (DLADM_WLAN_MODE_80211B);
--- a/usr/src/lib/libdladm/common/libdlwlan.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/lib/libdladm/common/libdlwlan.h	Thu Aug 06 13:05:38 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -110,7 +110,9 @@
 	DLADM_WLAN_MODE_NONE = 0,
 	DLADM_WLAN_MODE_80211A,
 	DLADM_WLAN_MODE_80211B,
-	DLADM_WLAN_MODE_80211G
+	DLADM_WLAN_MODE_80211G,
+	DLADM_WLAN_MODE_80211GN,
+	DLADM_WLAN_MODE_80211AN
 } dladm_wlan_mode_t;
 
 typedef enum {
--- a/usr/src/pkgdefs/etc/exception_list_i386	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/pkgdefs/etc/exception_list_i386	Thu Aug 06 13:05:38 2009 +0800
@@ -670,8 +670,9 @@
 usr/include/sys/1394/id1394.h		i386
 # Private net80211 headers
 usr/include/sys/net80211.h		i386
+usr/include/sys/net80211_crypto.h	i386
+usr/include/sys/net80211_ht.h		i386
 usr/include/sys/net80211_proto.h	i386
-usr/include/sys/net80211_crypto.h	i386
 #
 # PPPoE files not delivered to customers.
 usr/include/net/pppoe.h			i386
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Thu Aug 06 13:05:38 2009 +0800
@@ -677,8 +677,9 @@
 usr/include/net/wpa.h			sparc
 # Private net80211 headers
 usr/include/sys/net80211.h		sparc
+usr/include/sys/net80211_crypto.h	sparc
+usr/include/sys/net80211_ht.h		sparc
 usr/include/sys/net80211_proto.h	sparc
-usr/include/sys/net80211_crypto.h	sparc
 #
 # User<->kernel interface used by cfgadm/USB only
 #
--- a/usr/src/uts/common/Makefile.files	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/Makefile.files	Thu Aug 06 13:05:38 2009 +0800
@@ -625,7 +625,8 @@
 NET80211_OBJS += net80211.o net80211_proto.o net80211_input.o \
 		 net80211_output.o net80211_node.o net80211_crypto.o \
 		 net80211_crypto_none.o net80211_crypto_wep.o net80211_ioctl.o \
-		 net80211_crypto_tkip.o net80211_crypto_ccmp.o
+		 net80211_crypto_tkip.o net80211_crypto_ccmp.o	\
+		 net80211_ht.o
 
 VNIC_OBJS +=	vnic_ctl.o vnic_dev.o
 
--- a/usr/src/uts/common/inet/wifi_ioctl.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/inet/wifi_ioctl.h	Thu Aug 06 13:05:38 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -30,8 +30,6 @@
 #ifndef	__WIFI_IOCTL_H
 #define	__WIFI_IOCTL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 
 #ifdef	__cplusplus
@@ -262,6 +260,7 @@
 	uint32_t wl_ofdm_subtype;
 	uint32_t wl_ofdm_frequency;
 	uint32_t wl_ofdm_freq_supported;
+	boolean_t wl_ofdm_ht_enabled;
 } wl_ofdm_t;
 
 typedef struct wl_erp {
@@ -277,6 +276,7 @@
 	boolean_t wl_erp_dsss_ofdm_enabled;
 	boolean_t wl_erp_have_sst;
 	boolean_t wl_erp_sst_enabled;
+	boolean_t wl_erp_ht_enabled;
 } wl_erp_t;
 
 typedef union wl_phy_conf {
--- a/usr/src/uts/common/io/arn/arn_core.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/arn/arn_core.h	Thu Aug 06 13:05:38 2009 +0800
@@ -359,11 +359,6 @@
 #define	WME_BA_BMP_SIZE		64
 #define	WME_MAX_BA		WME_BA_BMP_SIZE
 #define	ATH_TID_MAX_BUFS	(2 * WME_MAX_BA)
-#define	TID_TO_WME_AC(_tid)				\
-	((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE :	\
-	(((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK :	\
-	(((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI :	\
-	WME_AC_VO)
 
 /* Wireless Multimedia Extension Defines */
 #define	WME_AC_BE	0 /* best effort */
--- a/usr/src/uts/common/io/iwh/iwh.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/iwh/iwh.c	Thu Aug 06 13:05:38 2009 +0800
@@ -4,7 +4,7 @@
  */
 
 /*
- * Copyright (c) 2008, Intel Corporation
+ * Copyright (c) 2009, Intel Corporation
  * All rights reserved.
  */
 
@@ -52,6 +52,7 @@
 #include <sys/mac_wifi.h>
 #include <sys/net80211.h>
 #include <sys/net80211_proto.h>
+#include <sys/net80211_ht.h>
 #include <sys/varargs.h>
 #include <sys/policy.h>
 #include <sys/pci.h>
@@ -79,6 +80,11 @@
 #define	IWH_DEBUG_RADIO		(1 << 13)
 #define	IWH_DEBUG_RESUME	(1 << 14)
 #define	IWH_DEBUG_CALIBRATION	(1 << 15)
+#define	IWH_DEBUG_BA		(1 << 16)
+#define	IWH_DEBUG_RXON		(1 << 17)
+#define	IWH_DEBUG_HWRATE	(1 << 18)
+#define	IWH_DEBUG_HTRATE	(1 << 19)
+#define	IWH_DEBUG_QOS		(1 << 20)
 /*
  * if want to see debug message of a given section,
  * please set this flag to one of above values
@@ -90,6 +96,8 @@
 #define	IWH_DBG(x)
 #endif
 
+#define	MS(v, f)    (((v) & f) >> f##_S)
+
 static void	*iwh_soft_state_p = NULL;
 
 /*
@@ -324,8 +332,34 @@
 static void	iwh_destroy_locks(iwh_sc_t *);
 static int	iwh_send(ieee80211com_t *, mblk_t *, uint8_t);
 static void	iwh_thread(iwh_sc_t *);
-static int	iwh_run_state_config(iwh_sc_t *sc);
-static int	iwh_fast_recover(iwh_sc_t *sc);
+static int	iwh_run_state_config(iwh_sc_t *);
+static int	iwh_fast_recover(iwh_sc_t *);
+static int	iwh_wme_update(ieee80211com_t *);
+static int	iwh_qosparam_to_hw(iwh_sc_t *, int);
+static int	iwh_wme_to_qos_ac(int);
+static uint16_t	iwh_cw_e_to_cw(uint8_t);
+static int	iwh_wmeparam_check(struct wmeParams *);
+static inline int	iwh_wme_tid_qos_ac(int);
+static inline int	iwh_qos_ac_to_txq(int);
+static int	iwh_wme_tid_to_txq(int);
+static void	iwh_init_ht_conf(iwh_sc_t *);
+static void	iwh_overwrite_11n_rateset(iwh_sc_t *);
+static void	iwh_overwrite_ic_default(iwh_sc_t *);
+static void	iwh_config_rxon_chain(iwh_sc_t *);
+static int	iwh_add_ap_sta(iwh_sc_t *);
+static int	iwh_ap_lq(iwh_sc_t *);
+static void	iwh_recv_action(struct ieee80211_node *,
+    const uint8_t *, const uint8_t *);
+static int	iwh_send_action(struct ieee80211_node *,
+    int, int, uint16_t[4]);
+static int	iwh_is_max_rate(ieee80211_node_t *);
+static int	iwh_is_min_rate(ieee80211_node_t *);
+static void	iwh_increase_rate(ieee80211_node_t *);
+static void	iwh_decrease_rate(ieee80211_node_t *);
+static int	iwh_alloc_dma_mem(iwh_sc_t *, size_t,
+    ddi_dma_attr_t *, ddi_device_acc_attr_t *,
+    uint_t, iwh_dma_t *);
+static void	iwh_free_dma_mem(iwh_dma_t *);
 
 /*
  * GLD specific operations
@@ -346,7 +380,6 @@
 
 /*
  * Supported rates for 802.11b/g modes (in 500Kbps unit).
- * 11n support will be added later.
  */
 static const struct ieee80211_rateset iwh_rateset_11b =
 	{ 4, { 2, 4, 11, 22 } };
@@ -355,6 +388,11 @@
 	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
 
 /*
+ * Default 11n reates supported by this station.
+ */
+extern struct ieee80211_htrateset ieee80211_rateset_11n;
+
+/*
  * For mfthread only
  */
 extern pri_t minclsyspri;
@@ -452,7 +490,7 @@
 		va_end(ap);
 	}
 }
-#endif
+#endif	/* DEBUG */
 
 /*
  * device operations
@@ -462,41 +500,41 @@
 {
 	iwh_sc_t		*sc;
 	ieee80211com_t		*ic;
-	int			instance, err, i;
+	int			instance, i;
 	char			strbuf[32];
 	wifi_data_t		wd = { 0 };
 	mac_register_t		*macp;
 	int			intr_type;
 	int			intr_count;
 	int			intr_actual;
+	int			err = DDI_FAILURE;
 
 	switch (cmd) {
 	case DDI_ATTACH:
 		break;
+
 	case DDI_RESUME:
+		instance = ddi_get_instance(dip);
 		sc = ddi_get_soft_state(iwh_soft_state_p,
-		    ddi_get_instance(dip));
+		    instance);
 		ASSERT(sc != NULL);
 
-		mutex_enter(&sc->sc_glock);
+		mutex_enter(&sc->sc_suspend_lock);
 		sc->sc_flags &= ~IWH_F_SUSPEND;
-		mutex_exit(&sc->sc_glock);
-
-		if (sc->sc_flags & IWH_F_RUNNING)
+
+		if (sc->sc_flags & IWH_F_RUNNING) {
 			(void) iwh_init(sc);
-
-		mutex_enter(&sc->sc_glock);
-		sc->sc_flags |= IWH_F_LAZY_RESUME;
-		mutex_exit(&sc->sc_glock);
-
-		IWH_DBG((IWH_DEBUG_RESUME, "iwh: resume\n"));
+		}
+		mutex_exit(&sc->sc_suspend_lock);
+
+		IWH_DBG((IWH_DEBUG_RESUME, "iwh_attach(): "
+		    "resume\n"));
 		return (DDI_SUCCESS);
+
 	default:
-		err = DDI_FAILURE;
 		goto attach_fail1;
 	}
 
-
 	instance = ddi_get_instance(dip);
 	err = ddi_soft_state_zalloc(iwh_soft_state_p, instance);
 	if (err != DDI_SUCCESS) {
@@ -504,7 +542,10 @@
 		    "failed to allocate soft state\n");
 		goto attach_fail1;
 	}
+
 	sc = ddi_get_soft_state(iwh_soft_state_p, instance);
+	ASSERT(sc != NULL);
+
 	sc->sc_dip = dip;
 
 	/*
@@ -518,6 +559,20 @@
 		goto attach_fail2;
 	}
 
+	sc->sc_dev_id = ddi_get16(sc->sc_cfg_handle,
+	    (uint16_t *)(sc->sc_cfg_base + PCI_CONF_DEVID));
+	if ((sc->sc_dev_id != 0x4232) &&
+	    (sc->sc_dev_id != 0x4235) &&
+	    (sc->sc_dev_id != 0x4236) &&
+	    (sc->sc_dev_id != 0x4237) &&
+	    (sc->sc_dev_id != 0x423a)) {
+		cmn_err(CE_WARN, "iwh_attach(): "
+		    "Do not support this device\n");
+		goto attach_fail3;
+	}
+
+	iwh_init_ht_conf(sc);
+	iwh_overwrite_11n_rateset(sc);
 
 	sc->sc_rev = ddi_get8(sc->sc_cfg_handle,
 	    (uint8_t *)(sc->sc_cfg_base + PCI_CONF_REVID));
@@ -526,17 +581,18 @@
 	 * keep from disturbing C3 state of CPU
 	 */
 	ddi_put8(sc->sc_cfg_handle, (uint8_t *)(sc->sc_cfg_base + 0x41), 0);
+
+	/*
+	 * determine the size of buffer for frame and command to ucode
+	 */
 	sc->sc_clsz = ddi_get16(sc->sc_cfg_handle,
 	    (uint16_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ));
 	if (!sc->sc_clsz) {
 		sc->sc_clsz = 16;
 	}
-
-	/*
-	 * determine the size of buffer for frame and command to ucode
-	 */
 	sc->sc_clsz = (sc->sc_clsz << 2);
-	sc->sc_dmabuf_sz = roundup(0x1000 + sizeof (struct ieee80211_frame) +
+
+	sc->sc_dmabuf_sz = roundup(0x2000 + sizeof (struct ieee80211_frame) +
 	    IEEE80211_MTU + IEEE80211_CRC_LEN +
 	    (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
 	    IEEE80211_WEP_CRCLEN), sc->sc_clsz);
@@ -594,10 +650,9 @@
 	    DDI_INTR_PRI(sc->sc_intr_pri));
 	mutex_init(&sc->sc_mt_lock, NULL, MUTEX_DRIVER,
 	    DDI_INTR_PRI(sc->sc_intr_pri));
-	mutex_init(&sc->sc_ucode_lock, NULL, MUTEX_DRIVER,
+	mutex_init(&sc->sc_suspend_lock, NULL, MUTEX_DRIVER,
 	    DDI_INTR_PRI(sc->sc_intr_pri));
 
-
 	cv_init(&sc->sc_fw_cv, NULL, CV_DRIVER, NULL);
 	cv_init(&sc->sc_cmd_cv, NULL, CV_DRIVER, NULL);
 	cv_init(&sc->sc_tx_cv, "tx-ring", CV_DRIVER, NULL);
@@ -647,14 +702,15 @@
 	 * get hardware configurations from eeprom
 	 */
 	err = iwh_eep_load(sc);
-	if (err != 0) {
+	if (err != IWH_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_attach(): "
 		    "failed to load eeprom\n");
 		goto attach_fail9;
 	}
 
 	if (IWH_READ_EEP_SHORT(sc, EEP_VERSION) < 0x011a) {
-		IWH_DBG((IWH_DEBUG_EEPROM, "unsupported eeprom detected"));
+		IWH_DBG((IWH_DEBUG_EEPROM, "iwh_attach(): "
+		    "unsupported eeprom detected\n"));
 		goto attach_fail9;
 	}
 
@@ -696,7 +752,7 @@
 	 * 802.11 module
 	 */
 	ic = &sc->sc_ic;
-	ic->ic_phytype  = IEEE80211_T_OFDM;
+	ic->ic_phytype  = IEEE80211_T_HT;
 	ic->ic_opmode   = IEEE80211_M_STA; /* default to BSS mode */
 	ic->ic_state    = IEEE80211_S_INIT;
 	ic->ic_maxrssi  = 100; /* experimental number */
@@ -709,6 +765,21 @@
 	ic->ic_caps |= IEEE80211_C_WPA;
 
 	/*
+	 * Support QoS/WME
+	 */
+	ic->ic_caps |= IEEE80211_C_WME;
+	ic->ic_wme.wme_update = iwh_wme_update;
+
+	/*
+	 * Support 802.11n/HT
+	 */
+	if (sc->sc_ht_conf.ht_support) {
+		ic->ic_htcaps = IEEE80211_HTC_HT |
+		    IEEE80211_HTC_AMSDU;
+		ic->ic_htcaps |= IEEE80211_HTCAP_MAXAMSDU_7935;
+	}
+
+	/*
 	 * set supported .11b and .11g rates
 	 */
 	ic->ic_sup_rates[IEEE80211_MODE_11B] = iwh_rateset_11b;
@@ -724,6 +795,14 @@
 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ |
 		    IEEE80211_CHAN_PASSIVE;
+
+		if (sc->sc_ht_conf.cap & HT_CAP_SUP_WIDTH) {
+			ic->ic_sup_channels[i].ich_flags |=
+			    IEEE80211_CHAN_HT40;
+		} else {
+			ic->ic_sup_channels[i].ich_flags |=
+			    IEEE80211_CHAN_HT20;
+		}
 	}
 
 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
@@ -742,12 +821,9 @@
 	    ddi_get_instance(dip));
 
 	/*
-	 * Override 80211 default routines
+	 * Overwrite 80211 default configurations.
 	 */
-	sc->sc_newstate = ic->ic_newstate;
-	ic->ic_newstate = iwh_newstate;
-	ic->ic_node_alloc = iwh_node_alloc;
-	ic->ic_node_free = iwh_node_free;
+	iwh_overwrite_ic_default(sc);
 
 	/*
 	 * initialize 802.11 module
@@ -793,7 +869,7 @@
 	 * create relation to GLD
 	 */
 	macp = mac_alloc(MAC_VERSION);
-	if (macp == NULL) {
+	if (NULL == macp) {
 		cmn_err(CE_WARN, "iwh_attach(): "
 		    "failed to do mac_alloc()\n");
 		goto attach_fail15;
@@ -826,9 +902,10 @@
 	(void) snprintf(strbuf, sizeof (strbuf), DRV_NAME_SP"%d", instance);
 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
 	    instance + 1, DDI_NT_NET_WIFI, 0);
-	if (err != DDI_SUCCESS)
+	if (err != DDI_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_attach(): "
 		    "failed to do ddi_create_minor_node()\n");
+	}
 
 	/*
 	 * Notify link is down now
@@ -893,32 +970,41 @@
 	ddi_soft_state_free(iwh_soft_state_p, instance);
 
 attach_fail1:
-	return (err);
+	return (DDI_FAILURE);
 }
 
 int
 iwh_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 {
 	iwh_sc_t *sc;
+	ieee80211com_t	*ic;
 	int err;
 
 	sc = ddi_get_soft_state(iwh_soft_state_p, ddi_get_instance(dip));
 	ASSERT(sc != NULL);
+	ic = &sc->sc_ic;
 
 	switch (cmd) {
 	case DDI_DETACH:
 		break;
+
 	case DDI_SUSPEND:
-		mutex_enter(&sc->sc_glock);
+		mutex_enter(&sc->sc_suspend_lock);
 		sc->sc_flags |= IWH_F_SUSPEND;
-		mutex_exit(&sc->sc_glock);
 
 		if (sc->sc_flags & IWH_F_RUNNING) {
 			iwh_stop(sc);
+			ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+
+			sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
+			sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
 		}
-
-		IWH_DBG((IWH_DEBUG_RESUME, "iwh: suspend\n"));
+		mutex_exit(&sc->sc_suspend_lock);
+
+		IWH_DBG((IWH_DEBUG_RESUME, "iwh_detach(): "
+		    "suspend\n"));
 		return (DDI_SUCCESS);
+
 	default:
 		return (DDI_FAILURE);
 	}
@@ -926,23 +1012,25 @@
 	if (!(sc->sc_flags & IWH_F_ATTACHED)) {
 		return (DDI_FAILURE);
 	}
-	err = mac_disable(sc->sc_ic.ic_mach);
-	if (err != DDI_SUCCESS)
-		return (err);
 
 	/*
 	 * Destroy the mf_thread
 	 */
+	sc->sc_mf_thread_switch = 0;
+
 	mutex_enter(&sc->sc_mt_lock);
-	sc->sc_mf_thread_switch = 0;
 	while (sc->sc_mf_thread != NULL) {
 		if (cv_wait_sig(&sc->sc_mt_cv, &sc->sc_mt_lock) == 0) {
 			break;
 		}
 	}
-
 	mutex_exit(&sc->sc_mt_lock);
 
+	err = mac_disable(sc->sc_ic.ic_mach);
+	if (err != DDI_SUCCESS) {
+		return (err);
+	}
+
 	/*
 	 * stop chipset
 	 */
@@ -967,7 +1055,6 @@
 	iwh_free_shared(sc);
 	mutex_exit(&sc->sc_glock);
 
-
 	(void) ddi_intr_disable(sc->sc_intr_htable[0]);
 	(void) ddi_intr_remove_handler(sc->sc_intr_htable[0]);
 	(void) ddi_intr_free(sc->sc_intr_htable[0]);
@@ -976,7 +1063,6 @@
 	(void) ddi_intr_remove_softint(sc->sc_soft_hdl);
 	sc->sc_soft_hdl = NULL;
 
-
 	/*
 	 * detach from 80211 module
 	 */
@@ -1007,7 +1093,7 @@
 	mutex_destroy(&sc->sc_mt_lock);
 	mutex_destroy(&sc->sc_tx_lock);
 	mutex_destroy(&sc->sc_glock);
-	mutex_destroy(&sc->sc_ucode_lock);
+	mutex_destroy(&sc->sc_suspend_lock);
 }
 
 /*
@@ -1019,7 +1105,7 @@
     uint_t dma_flags, iwh_dma_t *dma_p)
 {
 	caddr_t vaddr;
-	int err;
+	int err = DDI_FAILURE;
 
 	/*
 	 * Allocate handle
@@ -1093,7 +1179,7 @@
 static int
 iwh_alloc_fw_dma(iwh_sc_t *sc)
 {
-	int err = DDI_SUCCESS;
+	int err = DDI_FAILURE;
 	iwh_dma_t *dma_p;
 	char *t;
 
@@ -1110,19 +1196,19 @@
 	    &fw_dma_attr, &iwh_dma_accattr,
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &sc->sc_dma_fw_text);
-
-	dma_p = &sc->sc_dma_fw_text;
-
-	IWH_DBG((IWH_DEBUG_DMA, "text[ncookies:%d addr:%lx size:%lx]\n",
-	    dma_p->ncookies, dma_p->cookie.dmac_address,
-	    dma_p->cookie.dmac_size));
-
 	if (err != DDI_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
 		    "failed to allocate text dma memory.\n");
 		goto fail;
 	}
 
+	dma_p = &sc->sc_dma_fw_text;
+
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+	    "text[ncookies:%d addr:%lx size:%lx]\n",
+	    dma_p->ncookies, dma_p->cookie.dmac_address,
+	    dma_p->cookie.dmac_size));
+
 	(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->textsz));
 
 	/*
@@ -1133,39 +1219,39 @@
 	    &fw_dma_attr, &iwh_dma_accattr,
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &sc->sc_dma_fw_data);
-
-	dma_p = &sc->sc_dma_fw_data;
-
-	IWH_DBG((IWH_DEBUG_DMA, "data[ncookies:%d addr:%lx size:%lx]\n",
-	    dma_p->ncookies, dma_p->cookie.dmac_address,
-	    dma_p->cookie.dmac_size));
-
 	if (err != DDI_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
 		    "failed to allocate data dma memory\n");
 		goto fail;
 	}
 
+	dma_p = &sc->sc_dma_fw_data;
+
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+	    "data[ncookies:%d addr:%lx size:%lx]\n",
+	    dma_p->ncookies, dma_p->cookie.dmac_address,
+	    dma_p->cookie.dmac_size));
+
 	(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->datasz));
 
 	err = iwh_alloc_dma_mem(sc, LE_32(sc->sc_hdr->datasz),
 	    &fw_dma_attr, &iwh_dma_accattr,
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &sc->sc_dma_fw_data_bak);
-
-	dma_p = &sc->sc_dma_fw_data_bak;
-
-	IWH_DBG((IWH_DEBUG_DMA, "data_bak[ncookies:%d addr:%lx "
-	    "size:%lx]\n",
-	    dma_p->ncookies, dma_p->cookie.dmac_address,
-	    dma_p->cookie.dmac_size));
-
 	if (err != DDI_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
 		    "failed to allocate data bakup dma memory\n");
 		goto fail;
 	}
 
+	dma_p = &sc->sc_dma_fw_data_bak;
+
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+	    "data_bak[ncookies:%d addr:%lx "
+	    "size:%lx]\n",
+	    dma_p->ncookies, dma_p->cookie.dmac_address,
+	    dma_p->cookie.dmac_size));
+
 	(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->datasz));
 
 	/*
@@ -1176,20 +1262,20 @@
 	    &fw_dma_attr, &iwh_dma_accattr,
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &sc->sc_dma_fw_init_text);
-
-	dma_p = &sc->sc_dma_fw_init_text;
-
-	IWH_DBG((IWH_DEBUG_DMA, "init_text[ncookies:%d addr:%lx "
-	    "size:%lx]\n",
-	    dma_p->ncookies, dma_p->cookie.dmac_address,
-	    dma_p->cookie.dmac_size));
-
 	if (err != DDI_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
 		    "failed to allocate init text dma memory\n");
 		goto fail;
 	}
 
+	dma_p = &sc->sc_dma_fw_init_text;
+
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+	    "init_text[ncookies:%d addr:%lx "
+	    "size:%lx]\n",
+	    dma_p->ncookies, dma_p->cookie.dmac_address,
+	    dma_p->cookie.dmac_size));
+
 	(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->init_textsz));
 
 	/*
@@ -1200,20 +1286,20 @@
 	    &fw_dma_attr, &iwh_dma_accattr,
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &sc->sc_dma_fw_init_data);
-
-	dma_p = &sc->sc_dma_fw_init_data;
-
-	IWH_DBG((IWH_DEBUG_DMA, "init_data[ncookies:%d addr:%lx "
-	    "size:%lx]\n",
-	    dma_p->ncookies, dma_p->cookie.dmac_address,
-	    dma_p->cookie.dmac_size));
-
 	if (err != DDI_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
 		    "failed to allocate init data dma memory\n");
 		goto fail;
 	}
 
+	dma_p = &sc->sc_dma_fw_init_data;
+
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+	    "init_data[ncookies:%d addr:%lx "
+	    "size:%lx]\n",
+	    dma_p->ncookies, dma_p->cookie.dmac_address,
+	    dma_p->cookie.dmac_size));
+
 	(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->init_datasz));
 
 	sc->sc_boot = t + LE_32(sc->sc_hdr->init_datasz);
@@ -1240,7 +1326,7 @@
 #ifdef	DEBUG
 	iwh_dma_t *dma_p;
 #endif
-	int err = DDI_SUCCESS;
+	int err = DDI_FAILURE;
 
 	/*
 	 * must be aligned on a 4K-page boundary
@@ -1258,7 +1344,8 @@
 #ifdef	DEBUG
 	dma_p = &sc->sc_dma_sh;
 #endif
-	IWH_DBG((IWH_DEBUG_DMA, "sh[ncookies:%d addr:%lx size:%lx]\n",
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_shared(): "
+	    "sh[ncookies:%d addr:%lx size:%lx]\n",
 	    dma_p->ncookies, dma_p->cookie.dmac_address,
 	    dma_p->cookie.dmac_size));
 
@@ -1283,7 +1370,7 @@
 #ifdef	DEBUG
 	iwh_dma_t *dma_p;
 #endif
-	int err = DDI_SUCCESS;
+	int err = DDI_FAILURE;
 
 	/*
 	 * must be aligned on a 4K-page boundary
@@ -1299,7 +1386,8 @@
 #ifdef	DEBUG
 	dma_p = &sc->sc_dma_kw;
 #endif
-	IWH_DBG((IWH_DEBUG_DMA, "kw[ncookies:%d addr:%lx size:%lx]\n",
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_kw(): "
+	    "kw[ncookies:%d addr:%lx size:%lx]\n",
 	    dma_p->ncookies, dma_p->cookie.dmac_address,
 	    dma_p->cookie.dmac_size));
 
@@ -1326,7 +1414,7 @@
 #ifdef	DEBUG
 	iwh_dma_t *dma_p;
 #endif
-	int i, err = DDI_SUCCESS;
+	int i, err = DDI_FAILURE;
 
 	ring = &sc->sc_rxq;
 	ring->cur = 0;
@@ -1339,7 +1427,8 @@
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &ring->dma_desc);
 	if (err != DDI_SUCCESS) {
-		IWH_DBG((IWH_DEBUG_DMA, "dma alloc rx ring desc "
+		IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+		    "dma alloc rx ring desc "
 		    "failed\n"));
 		goto fail;
 	}
@@ -1348,7 +1437,8 @@
 #ifdef	DEBUG
 	dma_p = &ring->dma_desc;
 #endif
-	IWH_DBG((IWH_DEBUG_DMA, "rx bd[ncookies:%d addr:%lx size:%lx]\n",
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+	    "rx bd[ncookies:%d addr:%lx size:%lx]\n",
 	    dma_p->ncookies, dma_p->cookie.dmac_address,
 	    dma_p->cookie.dmac_size));
 
@@ -1362,7 +1452,8 @@
 		    DDI_DMA_READ | DDI_DMA_STREAMING,
 		    &data->dma_data);
 		if (err != DDI_SUCCESS) {
-			IWH_DBG((IWH_DEBUG_DMA, "dma alloc rx ring "
+			IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+			    "dma alloc rx ring "
 			    "buf[%d] failed\n", i));
 			goto fail;
 		}
@@ -1377,7 +1468,8 @@
 #ifdef	DEBUG
 	dma_p = &ring->data[0].dma_data;
 #endif
-	IWH_DBG((IWH_DEBUG_DMA, "rx buffer[0][ncookies:%d addr:%lx "
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+	    "rx buffer[0][ncookies:%d addr:%lx "
 	    "size:%lx]\n",
 	    dma_p->ncookies, dma_p->cookie.dmac_address,
 	    dma_p->cookie.dmac_size));
@@ -1409,7 +1501,8 @@
 	}
 #ifdef DEBUG
 	if (2000 == n) {
-		IWH_DBG((IWH_DEBUG_DMA, "timeout resetting Rx ring\n"));
+		IWH_DBG((IWH_DEBUG_DMA, "iwh_reset_rx_ring(): "
+		    "timeout resetting Rx ring\n"));
 	}
 #endif
 	iwh_mac_access_exit(sc);
@@ -1453,8 +1546,7 @@
 #ifdef	DEBUG
 	iwh_dma_t *dma_p;
 #endif
-	int i, err = DDI_SUCCESS;
-
+	int i, err = DDI_FAILURE;
 	ring->qid = qid;
 	ring->count = TFD_QUEUE_SIZE_MAX;
 	ring->window = slots;
@@ -1470,15 +1562,17 @@
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &ring->dma_desc);
 	if (err != DDI_SUCCESS) {
-		IWH_DBG((IWH_DEBUG_DMA, "dma alloc tx ring desc[%d]"
-		    " failed\n", qid));
+		IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+		    "dma alloc tx ring desc[%d] "
+		    "failed\n", qid));
 		goto fail;
 	}
 
 #ifdef	DEBUG
 	dma_p = &ring->dma_desc;
 #endif
-	IWH_DBG((IWH_DEBUG_DMA, "tx bd[ncookies:%d addr:%lx size:%lx]\n",
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+	    "tx bd[ncookies:%d addr:%lx size:%lx]\n",
 	    dma_p->ncookies, dma_p->cookie.dmac_address,
 	    dma_p->cookie.dmac_size));
 
@@ -1494,7 +1588,8 @@
 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
 	    &ring->dma_cmd);
 	if (err != DDI_SUCCESS) {
-		IWH_DBG((IWH_DEBUG_DMA, "dma alloc tx ring cmd[%d]"
+		IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+		    "dma alloc tx ring cmd[%d]"
 		    " failed\n", qid));
 		goto fail;
 	}
@@ -1502,7 +1597,8 @@
 #ifdef	DEBUG
 	dma_p = &ring->dma_cmd;
 #endif
-	IWH_DBG((IWH_DEBUG_DMA, "tx cmd[ncookies:%d addr:%lx size:%lx]\n",
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+	    "tx cmd[ncookies:%d addr:%lx size:%lx]\n",
 	    dma_p->ncookies, dma_p->cookie.dmac_address,
 	    dma_p->cookie.dmac_size));
 
@@ -1515,7 +1611,8 @@
 	ring->data = kmem_zalloc(sizeof (iwh_tx_data_t) * TFD_QUEUE_SIZE_MAX,
 	    KM_NOSLEEP);
 	if (NULL == ring->data) {
-		IWH_DBG((IWH_DEBUG_DMA, "could not allocate "
+		IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+		    "could not allocate "
 		    "tx data slots\n"));
 		goto fail;
 	}
@@ -1527,7 +1624,8 @@
 		    DDI_DMA_WRITE | DDI_DMA_STREAMING,
 		    &data->dma_data);
 		if (err != DDI_SUCCESS) {
-			IWH_DBG((IWH_DEBUG_DMA, "dma alloc tx "
+			IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+			    "dma alloc tx "
 			    "ring buf[%d] failed\n", i));
 			goto fail;
 		}
@@ -1535,15 +1633,15 @@
 		data->desc = desc_h + i;
 		data->paddr_desc = paddr_desc_h +
 		    _PTRDIFF(data->desc, desc_h);
-		data->cmd = cmd_h +  i; /* (i % slots); */
+		data->cmd = cmd_h +  i;
 		data->paddr_cmd = paddr_cmd_h +
 		    _PTRDIFF(data->cmd, cmd_h);
-		    /* ((i % slots) * sizeof (iwh_cmd_t)); */
 	}
 #ifdef	DEBUG
 	dma_p = &ring->data[0].dma_data;
 #endif
-	IWH_DBG((IWH_DEBUG_DMA, "tx buffer[0][ncookies:%d addr:%lx "
+	IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+	    "tx buffer[0][ncookies:%d addr:%lx "
 	    "size:%lx]\n",
 	    dma_p->ncookies, dma_p->cookie.dmac_address,
 	    dma_p->cookie.dmac_size));
@@ -1551,11 +1649,6 @@
 	return (err);
 
 fail:
-	if (ring->data) {
-		kmem_free(ring->data,
-		    sizeof (iwh_tx_data_t) * TFD_QUEUE_SIZE_MAX);
-	}
-
 	iwh_free_tx_ring(ring);
 
 	return (err);
@@ -1580,10 +1673,15 @@
 		}
 		DELAY(10);
 	}
+
+#ifdef	DEBUG
 	if (200 == n) {
-		IWH_DBG((IWH_DEBUG_DMA, "timeout reset tx ring %d\n",
+		IWH_DBG((IWH_DEBUG_DMA, "iwh_reset_tx_ring(): "
+		    "timeout reset tx ring %d\n",
 		    ring->qid));
 	}
+#endif
+
 	iwh_mac_access_exit(sc);
 
 	/* by pass, if it's quiesce */
@@ -1631,7 +1729,7 @@
 static int
 iwh_ring_init(iwh_sc_t *sc)
 {
-	int i, err = DDI_SUCCESS;
+	int i, err = DDI_FAILURE;
 
 	for (i = 0; i < IWH_NUM_QUEUES; i++) {
 		if (IWH_CMD_QUEUE_NUM == i) {
@@ -1676,19 +1774,20 @@
 	}
 }
 
-/*
- * allocate buffer for a node
- */
-/*ARGSUSED*/
+/* ARGSUSED */
 static ieee80211_node_t *
 iwh_node_alloc(ieee80211com_t *ic)
 {
 	iwh_amrr_t *amrr;
 
 	amrr = kmem_zalloc(sizeof (iwh_amrr_t), KM_SLEEP);
-	if (amrr != NULL) {
-		iwh_amrr_init(amrr);
-	}
+	if (NULL == amrr) {
+		cmn_err(CE_WARN, "iwh_node_alloc(): "
+		    "failed to allocate memory for amrr structure\n");
+		return (NULL);
+	}
+
+	iwh_amrr_init(amrr);
 
 	return (&amrr->in);
 }
@@ -1696,16 +1795,36 @@
 static void
 iwh_node_free(ieee80211_node_t *in)
 {
-	ieee80211com_t *ic = in->in_ic;
-
-	ic->ic_node_cleanup(in);
+	ieee80211com_t *ic;
+
+	if ((NULL == in) ||
+	    (NULL == in->in_ic)) {
+		cmn_err(CE_WARN, "iwh_node_free() "
+		    "Got a NULL point from Net80211 module\n");
+		return;
+	}
+	ic = in->in_ic;
+
+	if (ic->ic_node_cleanup != NULL) {
+		ic->ic_node_cleanup(in);
+	}
+
 	if (in->in_wpa_ie != NULL) {
 		ieee80211_free(in->in_wpa_ie);
 	}
 
+	if (in->in_wme_ie != NULL) {
+		ieee80211_free(in->in_wme_ie);
+	}
+
+	if (in->in_htcap_ie != NULL) {
+		ieee80211_free(in->in_htcap_ie);
+	}
+
 	kmem_free(in, sizeof (iwh_amrr_t));
 }
 
+
 /*
  * change station's state. this function will be invoked by 80211 module
  * when need to change staton's state.
@@ -1713,10 +1832,20 @@
 static int
 iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
 {
-	iwh_sc_t *sc = (iwh_sc_t *)ic;
-	ieee80211_node_t *in = ic->ic_bss;
-	enum ieee80211_state ostate = ic->ic_state;
-	int i, err = IWH_SUCCESS;
+	iwh_sc_t *sc;
+	ieee80211_node_t *in;
+	enum ieee80211_state ostate;
+	iwh_add_sta_t node;
+	iwh_amrr_t *amrr;
+	uint8_t r;
+	int i, err = IWH_FAIL;
+
+	if (NULL == ic) {
+		return (err);
+	}
+	sc = (iwh_sc_t *)ic;
+	in = ic->ic_bss;
+	ostate = ic->ic_state;
 
 	mutex_enter(&sc->sc_glock);
 
@@ -1724,8 +1853,6 @@
 	case IEEE80211_S_SCAN:
 		switch (ostate) {
 		case IEEE80211_S_INIT:
-		{
-			iwh_add_sta_t node;
 			sc->sc_flags |= IWH_F_SCANNING;
 			iwh_set_led(sc, 2, 10, 2);
 
@@ -1737,7 +1864,8 @@
 			sc->sc_config.filter_flags &=
 			    ~LE_32(RXON_FILTER_ASSOC_MSK);
 
-			IWH_DBG((IWH_DEBUG_80211, "config chan %d "
+			IWH_DBG((IWH_DEBUG_80211, "iwh_newstate(): "
+			    "config chan %d "
 			    "flags %x filter_flags %x\n",
 			    LE_16(sc->sc_config.chan),
 			    LE_32(sc->sc_config.flags),
@@ -1746,7 +1874,7 @@
 			err = iwh_cmd(sc, REPLY_RXON, &sc->sc_config,
 			    sizeof (iwh_rxon_cmd_t), 1);
 			if (err != IWH_SUCCESS) {
-				cmn_err(CE_WARN,
+				cmn_err(CE_WARN, "iwh_newstate(): "
 				    "could not clear association\n");
 				sc->sc_flags &= ~IWH_F_SCANNING;
 				mutex_exit(&sc->sc_glock);
@@ -1760,21 +1888,20 @@
 			err = iwh_cmd(sc, REPLY_ADD_STA, &node,
 			    sizeof (node), 1);
 			if (err != IWH_SUCCESS) {
-				cmn_err(CE_WARN, "could not add "
-				    "broadcast node\n");
+				cmn_err(CE_WARN, "iwh_newstate(): "
+				    "could not add broadcast node\n");
 				sc->sc_flags &= ~IWH_F_SCANNING;
 				mutex_exit(&sc->sc_glock);
 				return (err);
 			}
 			break;
-		}
 		case IEEE80211_S_SCAN:
 			mutex_exit(&sc->sc_glock);
 			/* step to next channel before actual FW scan */
 			err = sc->sc_newstate(ic, nstate, arg);
 			mutex_enter(&sc->sc_glock);
 			if ((err != 0) || ((err = iwh_scan(sc)) != 0)) {
-				cmn_err(CE_WARN,
+				cmn_err(CE_WARN, "iwh_newstate(): "
 				    "could not initiate scan\n");
 				sc->sc_flags &= ~IWH_F_SCANNING;
 				ieee80211_cancel_scan(ic);
@@ -1804,7 +1931,7 @@
 		 * channel same to the target AP...
 		 */
 		if ((err = iwh_hw_set_before_auth(sc)) != 0) {
-			IWH_DBG((IWH_DEBUG_80211,
+			IWH_DBG((IWH_DEBUG_80211, "iwh_newstate(): "
 			    "could not send authentication request\n"));
 			mutex_exit(&sc->sc_glock);
 			return (err);
@@ -1822,7 +1949,8 @@
 			break;
 		}
 
-		IWH_DBG((IWH_DEBUG_80211, "iwh: associated."));
+		IWH_DBG((IWH_DEBUG_80211, "iwh_newstate(): "
+		    "associated.\n"));
 
 		err = iwh_run_state_config(sc);
 		if (err != IWH_SUCCESS) {
@@ -1835,23 +1963,42 @@
 		/*
 		 * start automatic rate control
 		 */
-		mutex_enter(&sc->sc_mt_lock);
-		if (IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) {
-			sc->sc_flags |= IWH_F_RATE_AUTO_CTL;
-			/*
-			 * set rate to some reasonable initial value
-			 */
-			i = in->in_rates.ir_nrates - 1;
-			while (i > 0 && IEEE80211_RATE(i) > 72) {
-				i--;
+		if ((in->in_flags & IEEE80211_NODE_HT) &&
+		    (sc->sc_ht_conf.ht_support) &&
+		    (in->in_htrates.rs_nrates > 0) &&
+		    (in->in_htrates.rs_nrates <= IEEE80211_HTRATE_MAXSIZE)) {
+			amrr = (iwh_amrr_t *)in;
+
+			for (i = in->in_htrates.rs_nrates - 1; i > 0; i--) {
+
+				r = in->in_htrates.rs_rates[i] &
+				    IEEE80211_RATE_VAL;
+				if ((r != 0) && (r <= 0xd) &&
+				    (sc->sc_ht_conf.tx_support_mcs[r/8] &
+				    (1 << (r%8)))) {
+					amrr->ht_mcs_idx = r;
+					sc->sc_flags |= IWH_F_RATE_AUTO_CTL;
+					break;
+				}
 			}
-			in->in_txrate = i;
 		} else {
-			sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
+			if (IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) {
+				sc->sc_flags |= IWH_F_RATE_AUTO_CTL;
+
+				/*
+				 * set rate to some reasonable initial value
+				 */
+				i = in->in_rates.ir_nrates - 1;
+				while (i > 0 && IEEE80211_RATE(i) > 72) {
+					i--;
+				}
+				in->in_txrate = i;
+
+			} else {
+				sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
+			}
 		}
 
-		mutex_exit(&sc->sc_mt_lock);
-
 		/*
 		 * set LED on after associated
 		 */
@@ -1906,7 +2053,8 @@
 
 #ifdef	DEBUG
 	if (1000 == n) {
-		IWH_DBG((IWH_DEBUG_PIO, "could not lock memory\n"));
+		IWH_DBG((IWH_DEBUG_PIO, "iwh_mac_access_enter(): "
+		    "could not lock memory\n"));
 	}
 #endif
 }
@@ -1973,7 +2121,7 @@
 static int
 iwh_load_init_firmware(iwh_sc_t *sc)
 {
-	int	err;
+	int	err = IWH_FAIL;
 	clock_t	clk;
 
 	sc->sc_flags &= ~IWH_F_PUT_SEG;
@@ -2042,7 +2190,7 @@
 static int
 iwh_load_run_firmware(iwh_sc_t *sc)
 {
-	int	err;
+	int	err = IWH_FAIL;
 	clock_t	clk;
 
 	sc->sc_flags &= ~IWH_F_PUT_SEG;
@@ -2189,7 +2337,8 @@
 	    sizeof (struct iwh_rx_mpdu_body_size) + len);
 	bcopy(tail, &crc, 4);
 
-	IWH_DBG((IWH_DEBUG_RX, "rx intr: idx=%d phy_len=%x len=%d "
+	IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+	    "rx intr: idx=%d phy_len=%x len=%d "
 	    "rate=%x chan=%d tstamp=%x non_cfg_phy_count=%x "
 	    "cfg_phy_count=%x tail=%x", ring->cur, sizeof (*stat),
 	    len, stat->rate.r.s.rate, stat->channel,
@@ -2197,7 +2346,8 @@
 	    stat->cfg_phy_cnt, LE_32(crc)));
 
 	if ((len < 16) || (len > sc->sc_dmabuf_sz)) {
-		IWH_DBG((IWH_DEBUG_RX, "rx frame oversize\n"));
+		IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+		    "rx frame oversize\n"));
 		return;
 	}
 
@@ -2207,7 +2357,8 @@
 	if ((LE_32(crc) &
 	    (RX_RES_STATUS_NO_CRC32_ERROR | RX_RES_STATUS_NO_RXE_OVERFLOW)) !=
 	    (RX_RES_STATUS_NO_CRC32_ERROR | RX_RES_STATUS_NO_RXE_OVERFLOW)) {
-		IWH_DBG((IWH_DEBUG_RX, "rx crc error tail: %x\n",
+		IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+		    "rx crc error tail: %x\n",
 		    LE_32(crc)));
 		sc->sc_rx_err++;
 		return;
@@ -2218,7 +2369,8 @@
 
 	if (IEEE80211_FC0_SUBTYPE_ASSOC_RESP == *(uint8_t *)wh) {
 		sc->sc_assoc_id = *((uint16_t *)(wh + 1) + 2);
-		IWH_DBG((IWH_DEBUG_RX, "rx : association id = %x\n",
+		IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+		    "rx : association id = %x\n",
 		    sc->sc_assoc_id));
 	}
 
@@ -2240,8 +2392,8 @@
 		(void) ieee80211_input(ic, mp, in, rssi, 0);
 	} else {
 		sc->sc_rx_nobuf++;
-		IWH_DBG((IWH_DEBUG_RX,
-		    "iwh_rx_mpdu_intr(): alloc rx buf failed\n"));
+		IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+		    "alloc rx buf failed\n"));
 	}
 
 	/*
@@ -2259,19 +2411,29 @@
 	ieee80211com_t *ic = &sc->sc_ic;
 	iwh_tx_ring_t *ring = &sc->sc_txq[desc->hdr.qid & 0x3];
 	iwh_tx_stat_t *stat = (iwh_tx_stat_t *)(desc + 1);
-	iwh_amrr_t *amrr = (iwh_amrr_t *)ic->ic_bss;
+	iwh_amrr_t *amrr;
+
+	if (NULL == ic->ic_bss) {
+		return;
+	}
+
+	amrr = (iwh_amrr_t *)ic->ic_bss;
 
 	amrr->txcnt++;
-	IWH_DBG((IWH_DEBUG_RATECTL, "tx: %d cnt\n", amrr->txcnt));
+	IWH_DBG((IWH_DEBUG_RATECTL, "iwh_tx_intr(): "
+	    "tx: %d cnt\n", amrr->txcnt));
 
 	if (stat->ntries > 0) {
 		amrr->retrycnt++;
 		sc->sc_tx_retries++;
-		IWH_DBG((IWH_DEBUG_TX, "tx: %d retries\n",
+		IWH_DBG((IWH_DEBUG_TX, "iwh_tx_intr(): "
+		    "tx: %d retries\n",
 		    sc->sc_tx_retries));
 	}
 
+	mutex_enter(&sc->sc_mt_lock);
 	sc->sc_tx_timer = 0;
+	mutex_exit(&sc->sc_mt_lock);
 
 	mutex_enter(&sc->sc_tx_lock);
 
@@ -2280,7 +2442,7 @@
 		ring->queued = 0;
 	}
 
-	if ((sc->sc_need_reschedule) && (ring->queued <= (ring->count << 3))) {
+	if ((sc->sc_need_reschedule) && (ring->queued <= (ring->count >> 3))) {
 		sc->sc_need_reschedule = 0;
 		mutex_exit(&sc->sc_tx_lock);
 		mac_tx_update(ic->ic_mach);
@@ -2300,14 +2462,20 @@
 		return;
 	}
 
+	if (sc->sc_cmd_accum > 0) {
+		sc->sc_cmd_accum--;
+		return;
+	}
+
 	mutex_enter(&sc->sc_glock);
 
-	sc->sc_flags |= IWH_F_CMD_DONE;
+	sc->sc_cmd_flag = SC_CMD_FLG_DONE;
+
 	cv_signal(&sc->sc_cmd_cv);
 
 	mutex_exit(&sc->sc_glock);
 
-	IWH_DBG((IWH_DEBUG_CMD, "rx cmd: "
+	IWH_DBG((IWH_DEBUG_CMD, "iwh_cmd_intr(): "
 	    "qid=%x idx=%d flags=%x type=0x%x\n",
 	    desc->hdr.qid, desc->hdr.idx, desc->hdr.flags,
 	    desc->hdr.type));
@@ -2328,14 +2496,14 @@
 	/*
 	 * the microcontroller is ready
 	 */
-	IWH_DBG((IWH_DEBUG_FW,
-	    "microcode alive notification minor: %x major: %x type:"
-	    " %x subtype: %x\n",
+	IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
+	    "microcode alive notification minor: %x major: %x type: "
+	    "%x subtype: %x\n",
 	    ar->ucode_minor, ar->ucode_minor, ar->ver_type, ar->ver_subtype));
 
 #ifdef	DEBUG
 	if (LE_32(ar->is_valid) != UCODE_VALID_OK) {
-		IWH_DBG((IWH_DEBUG_FW,
+		IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
 		    "microcontroller initialization failed\n"));
 	}
 #endif
@@ -2344,7 +2512,7 @@
 	 * determine if init alive or runtime alive.
 	 */
 	if (INITIALIZE_SUBTYPE == ar->ver_subtype) {
-		IWH_DBG((IWH_DEBUG_FW,
+		IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
 		    "initialization alive received.\n"));
 
 		(void) memcpy(&sc->sc_card_alive_init, ar,
@@ -2385,7 +2553,8 @@
 
 	} else {	/* runtime alive */
 
-		IWH_DBG((IWH_DEBUG_FW, "runtime alive received.\n"));
+		IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
+		    "runtime alive received.\n"));
 
 		(void) memcpy(&sc->sc_card_alive_run, ar,
 		    sizeof (struct iwh_alive_resp));
@@ -2455,10 +2624,10 @@
 			DELAY(1000);
 		}
 
-		mutex_exit(&sc->sc_glock);
-
 		sc->sc_flags |= IWH_F_FW_INIT;
 		cv_signal(&sc->sc_ucode_cv);
+
+		mutex_exit(&sc->sc_glock);
 	}
 
 }
@@ -2467,29 +2636,21 @@
  * deal with receiving frames, command response
  * and all notifications from ucode.
  */
+/* ARGSUSED */
 static uint_t
-/* LINTED: argument unused in function: unused */
 iwh_rx_softintr(caddr_t arg, caddr_t unused)
 {
-	iwh_sc_t *sc = (iwh_sc_t *)arg;
-	ieee80211com_t *ic = &sc->sc_ic;
+	iwh_sc_t *sc;
+	ieee80211com_t *ic;
 	iwh_rx_desc_t *desc;
 	iwh_rx_data_t *data;
 	uint32_t index;
 
-	mutex_enter(&sc->sc_glock);
-
-	if (sc->sc_rx_softint_pending != 1) {
-		mutex_exit(&sc->sc_glock);
+	if (NULL == arg) {
 		return (DDI_INTR_UNCLAIMED);
 	}
-
-	/*
-	 * disable interrupts
-	 */
-	IWH_WRITE(sc, CSR_INT_MASK, 0);
-
-	mutex_exit(&sc->sc_glock);
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
 
 	/*
 	 * firmware has moved the index of the rx queue, driver get it,
@@ -2501,7 +2662,8 @@
 		data = &sc->sc_rxq.data[sc->sc_rxq.cur];
 		desc = (iwh_rx_desc_t *)data->dma_data.mem_va;
 
-		IWH_DBG((IWH_DEBUG_INTR, "rx notification index = %d"
+		IWH_DBG((IWH_DEBUG_INTR, "iwh_rx_softintr(): "
+		    "rx notification index = %d"
 		    " cur = %d qid=%x idx=%d flags=%x type=%x len=%d\n",
 		    index, sc->sc_rxq.cur, desc->hdr.qid, desc->hdr.idx,
 		    desc->hdr.flags, desc->hdr.type, LE_32(desc->len)));
@@ -2510,10 +2672,8 @@
 		 * a command other than a tx need to be replied
 		 */
 		if (!(desc->hdr.qid & 0x80) &&
-		    (desc->hdr.type != REPLY_RX_PHY_CMD) &&
-		    (desc->hdr.type != REPLY_RX_MPDU_CMD) &&
-		    (desc->hdr.type != REPLY_TX) &&
-		    (desc->hdr.type != REPLY_PHY_CALIBRATION_CMD)) {
+		    (desc->hdr.type != REPLY_SCAN_CMD) &&
+		    (desc->hdr.type != REPLY_TX)) {
 			iwh_cmd_intr(sc, desc);
 		}
 
@@ -2538,7 +2698,8 @@
 		{
 			uint32_t *status = (uint32_t *)(desc + 1);
 
-			IWH_DBG((IWH_DEBUG_RADIO, "state changed to %x\n",
+			IWH_DBG((IWH_DEBUG_RADIO, "iwh_rx_softintr(): "
+			    "state changed to %x\n",
 			    LE_32(*status)));
 
 			if (LE_32(*status) & 1) {
@@ -2556,6 +2717,7 @@
 				sc->sc_flags |=
 				    (IWH_F_HW_ERR_RECOVER | IWH_F_RADIO_OFF);
 			}
+
 			break;
 		}
 
@@ -2564,7 +2726,7 @@
 			iwh_start_scan_t *scan =
 			    (iwh_start_scan_t *)(desc + 1);
 
-			IWH_DBG((IWH_DEBUG_SCAN,
+			IWH_DBG((IWH_DEBUG_SCAN, "iwh_rx_softintr(): "
 			    "scanning channel %d status %x\n",
 			    scan->chan, LE_32(scan->status)));
 
@@ -2574,12 +2736,14 @@
 
 		case SCAN_COMPLETE_NOTIFICATION:
 		{
+#ifdef	DEBUG
 			iwh_stop_scan_t *scan =
 			    (iwh_stop_scan_t *)(desc + 1);
 
-			IWH_DBG((IWH_DEBUG_SCAN,
+			IWH_DBG((IWH_DEBUG_SCAN, "iwh_rx_softintr(): "
 			    "completed channel %d (burst of %d) status %02x\n",
 			    scan->chan, scan->nchan, scan->status));
+#endif
 
 			sc->sc_scan_pending++;
 			break;
@@ -2598,8 +2762,10 @@
 			break;
 
 		case CALIBRATION_COMPLETE_NOTIFICATION:
+			mutex_enter(&sc->sc_glock);
 			sc->sc_flags |= IWH_F_FW_INIT;
 			cv_signal(&sc->sc_ucode_cv);
+			mutex_exit(&sc->sc_glock);
 			break;
 
 		case MISSED_BEACONS_NOTIFICATION:
@@ -2608,7 +2774,7 @@
 			    (struct iwh_beacon_missed *)(desc + 1);
 
 			if ((ic->ic_state == IEEE80211_S_RUN) &&
-			    (LE_32(miss->consecutive) > 10)) {
+			    (LE_32(miss->consecutive) > 50)) {
 				cmn_err(CE_NOTE, "iwh: iwh_rx_softintr(): "
 				    "beacon missed %d/%d\n",
 				    LE_32(miss->consecutive),
@@ -2630,15 +2796,10 @@
 	index = (0 == index) ? RX_QUEUE_SIZE - 1 : index - 1;
 	IWH_WRITE(sc, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, index & (~7));
 
-	mutex_enter(&sc->sc_glock);
-
 	/*
 	 * re-enable interrupts
 	 */
 	IWH_WRITE(sc, CSR_INT_MASK, CSR_INI_SET_MASK);
-	sc->sc_rx_softint_pending = 0;
-
-	mutex_exit(&sc->sc_glock);
 
 	return (DDI_INTR_CLAIMED);
 }
@@ -2646,30 +2807,38 @@
 /*
  * the handle of interrupt
  */
+/* ARGSUSED */
 static uint_t
-/* LINTED: argument unused in function: unused */
 iwh_intr(caddr_t arg, caddr_t unused)
 {
-	iwh_sc_t *sc = (iwh_sc_t *)arg;
+	iwh_sc_t *sc;
 	uint32_t r, rfh;
 
-	mutex_enter(&sc->sc_glock);
+	if (NULL == arg) {
+		return (DDI_INTR_UNCLAIMED);
+	}
+	sc = (iwh_sc_t *)arg;
+
+	mutex_enter(&sc->sc_suspend_lock);
 
 	if (sc->sc_flags & IWH_F_SUSPEND) {
-		mutex_exit(&sc->sc_glock);
+		mutex_exit(&sc->sc_suspend_lock);
 		return (DDI_INTR_UNCLAIMED);
 	}
+
 	r = IWH_READ(sc, CSR_INT);
 	if (0 == r || 0xffffffff == r) {
-		mutex_exit(&sc->sc_glock);
+		mutex_exit(&sc->sc_suspend_lock);
 		return (DDI_INTR_UNCLAIMED);
 	}
 
-	IWH_DBG((IWH_DEBUG_INTR, "interrupt reg %x\n", r));
+	IWH_DBG((IWH_DEBUG_INTR, "iwh_intr(): "
+	    "interrupt reg %x\n", r));
 
 	rfh = IWH_READ(sc, CSR_FH_INT_STATUS);
 
-	IWH_DBG((IWH_DEBUG_INTR, "FH interrupt reg %x\n", rfh));
+	IWH_DBG((IWH_DEBUG_INTR, "iwh_intr(): "
+	    "FH interrupt reg %x\n", rfh));
 
 	/*
 	 * disable interrupts
@@ -2682,20 +2851,17 @@
 	IWH_WRITE(sc, CSR_INT, r);
 	IWH_WRITE(sc, CSR_FH_INT_STATUS, rfh);
 
-	if (NULL == sc->sc_soft_hdl) {
-		mutex_exit(&sc->sc_glock);
-		return (DDI_INTR_CLAIMED);
-	}
-
 	if (r & (BIT_INT_SWERROR | BIT_INT_ERR)) {
-		IWH_DBG((IWH_DEBUG_FW, "fatal firmware error\n"));
-		mutex_exit(&sc->sc_glock);
+		IWH_DBG((IWH_DEBUG_FW, "iwh_intr(): "
+		    "fatal firmware error\n"));
+		mutex_exit(&sc->sc_suspend_lock);
 		iwh_stop(sc);
 		sc->sc_ostate = sc->sc_ic.ic_state;
 
 		/* notify upper layer */
-		if (!IWH_CHK_FAST_RECOVER(sc))
+		if (!IWH_CHK_FAST_RECOVER(sc)) {
 			ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
+		}
 
 		sc->sc_flags |= IWH_F_HW_ERR_RECOVER;
 		return (DDI_INTR_CLAIMED);
@@ -2703,24 +2869,29 @@
 
 	if (r & BIT_INT_RF_KILL) {
 		uint32_t tmp = IWH_READ(sc, CSR_GP_CNTRL);
-		if (tmp & (1 << 27))
+		if (tmp & (1 << 27)) {
 			cmn_err(CE_NOTE, "RF switch: radio on\n");
+		}
 	}
 
 	if ((r & (BIT_INT_FH_RX | BIT_INT_SW_RX)) ||
 	    (rfh & FH_INT_RX_MASK)) {
-		sc->sc_rx_softint_pending = 1;
 		(void) ddi_intr_trigger_softint(sc->sc_soft_hdl, NULL);
+		mutex_exit(&sc->sc_suspend_lock);
+		return (DDI_INTR_CLAIMED);
 	}
 
 	if (r & BIT_INT_FH_TX) {
+		mutex_enter(&sc->sc_glock);
 		sc->sc_flags |= IWH_F_PUT_SEG;
 		cv_signal(&sc->sc_put_seg_cv);
+		mutex_exit(&sc->sc_glock);
 	}
 
 #ifdef	DEBUG
 	if (r & BIT_INT_ALIVE)	{
-		IWH_DBG((IWH_DEBUG_FW, "firmware initialized.\n"));
+		IWH_DBG((IWH_DEBUG_FW, "iwh_intr(): "
+		    "firmware initialized.\n"));
 	}
 #endif
 
@@ -2729,7 +2900,7 @@
 	 */
 	IWH_WRITE(sc, CSR_INT_MASK, CSR_INI_SET_MASK);
 
-	mutex_exit(&sc->sc_glock);
+	mutex_exit(&sc->sc_suspend_lock);
 
 	return (DDI_INTR_CLAIMED);
 }
@@ -2808,14 +2979,15 @@
 static mblk_t *
 iwh_m_tx(void *arg, mblk_t *mp)
 {
-	iwh_sc_t	*sc = (iwh_sc_t *)arg;
-	ieee80211com_t	*ic = &sc->sc_ic;
+	iwh_sc_t	*sc;
+	ieee80211com_t	*ic;
 	mblk_t		*next;
 
-	if (sc->sc_flags & IWH_F_SUSPEND) {
-		freemsgchain(mp);
+	if (NULL == arg) {
 		return (NULL);
 	}
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
 
 	if (ic->ic_state != IEEE80211_S_RUN) {
 		freemsgchain(mp);
@@ -2824,10 +2996,18 @@
 
 	if ((sc->sc_flags & IWH_F_HW_ERR_RECOVER) &&
 	    IWH_CHK_FAST_RECOVER(sc)) {
-		IWH_DBG((IWH_DEBUG_FW, "iwh_m_tx(): hold queue\n"));
+		IWH_DBG((IWH_DEBUG_FW, "iwh_m_tx(): "
+		    "hold queue\n"));
 		return (mp);
 	}
 
+	mutex_enter(&sc->sc_suspend_lock);
+	if (sc->sc_flags & IWH_F_SUSPEND) {
+		mutex_exit(&sc->sc_suspend_lock);
+		freemsgchain(mp);
+		return (NULL);
+	}
+
 	while (mp != NULL) {
 		next = mp->b_next;
 		mp->b_next = NULL;
@@ -2838,6 +3018,10 @@
 		mp = next;
 	}
 
+	if (mutex_owned(&sc->sc_suspend_lock)) {
+		mutex_exit(&sc->sc_suspend_lock);
+	}
+
 	return (mp);
 }
 
@@ -2847,7 +3031,7 @@
 static int
 iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
 {
-	iwh_sc_t *sc = (iwh_sc_t *)ic;
+	iwh_sc_t *sc;
 	iwh_tx_ring_t *ring;
 	iwh_tx_desc_t *desc;
 	iwh_tx_data_t *data;
@@ -2857,20 +3041,23 @@
 	struct ieee80211_frame *wh;
 	struct ieee80211_key *k = NULL;
 	mblk_t *m, *m0;
-	int rate, hdrlen, len, len0, mblen, off, err = IWH_SUCCESS;
+	int hdrlen, len, len0, mblen, off, err = IWH_SUCCESS;
 	uint16_t masks = 0;
-	uint32_t 	s_id = 0;
-
-	ring = &sc->sc_txq[0];
-	data = &ring->data[ring->cur];
-	desc = data->desc;
-	cmd = data->cmd;
-	bzero(desc, sizeof (*desc));
-	bzero(cmd, sizeof (*cmd));
-
-	mutex_enter(&sc->sc_tx_lock);
+	uint32_t rate, s_id = 0;
+	int txq_id = NON_QOS_TXQ;
+	struct ieee80211_qosframe *qwh = NULL;
+	int tid = WME_TID_INVALID;
+
+	if (NULL == ic) {
+		return (IWH_FAIL);
+	}
+	sc = (iwh_sc_t *)ic;
+
+	if (!mutex_owned(&sc->sc_suspend_lock)) {
+		mutex_enter(&sc->sc_suspend_lock);
+	}
+
 	if (sc->sc_flags & IWH_F_SUSPEND) {
-		mutex_exit(&sc->sc_tx_lock);
 		if ((type & IEEE80211_FC0_TYPE_MASK) !=
 		    IEEE80211_FC0_TYPE_DATA) {
 			freemsg(mp);
@@ -2878,23 +3065,8 @@
 		err = IWH_FAIL;
 		goto exit;
 	}
-	if (ring->queued > ring->count - 64) {
-		IWH_DBG((IWH_DEBUG_TX, "iwh_send(): no txbuf\n"));
-
-		sc->sc_need_reschedule = 1;
-		mutex_exit(&sc->sc_tx_lock);
-		if ((type & IEEE80211_FC0_TYPE_MASK) !=
-		    IEEE80211_FC0_TYPE_DATA) {
-			freemsg(mp);
-		}
-		sc->sc_tx_nobuf++;
-		err = IWH_FAIL;
-		goto exit;
-	}
-
-	mutex_exit(&sc->sc_tx_lock);
-
-	hdrlen = sizeof (struct ieee80211_frame);
+
+	hdrlen = ieee80211_hdrspace(ic, mp->b_rptr);
 
 	m = allocb(msgdsize(mp) + 32, BPRI_MED);
 	if (NULL == m) { /* can not alloc buf, drop this package */
@@ -2913,8 +3085,6 @@
 
 	m->b_wptr += off;
 
-	freemsg(mp);
-
 	wh = (struct ieee80211_frame *)m->b_rptr;
 
 	/*
@@ -2924,13 +3094,92 @@
 	if (NULL == in) {
 		cmn_err(CE_WARN, "iwh_send(): "
 		    "failed to find tx node\n");
+		freemsg(mp);
 		freemsg(m);
 		sc->sc_tx_err++;
 		err = IWH_SUCCESS;
 		goto exit;
 	}
 
-	(void) ieee80211_encap(ic, m, in);
+	/*
+	 * Net80211 module encapsulate outbound data frames.
+	 * Add some feilds of 80211 frame.
+	 */
+	if ((type & IEEE80211_FC0_TYPE_MASK) ==
+	    IEEE80211_FC0_TYPE_DATA) {
+		(void) ieee80211_encap(ic, m, in);
+	}
+
+	/*
+	 * Determine TX queue according to traffic ID in frame
+	 * if working in QoS mode.
+	 */
+	if (in->in_flags & IEEE80211_NODE_QOS) {
+
+		if ((type & IEEE80211_FC0_TYPE_MASK) ==
+		    IEEE80211_FC0_TYPE_DATA) {
+
+			if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+				qwh = (struct ieee80211_qosframe *)wh;
+
+				tid = qwh->i_qos[0] & IEEE80211_QOS_TID;
+				txq_id = iwh_wme_tid_to_txq(tid);
+
+				if (txq_id < TXQ_FOR_AC_MIN ||
+				    (txq_id > TXQ_FOR_AC_MAX)) {
+					freemsg(m);
+					freemsg(mp);
+					sc->sc_tx_err++;
+					err = IWH_SUCCESS;
+					goto exit;
+				}
+
+			} else {
+				txq_id = NON_QOS_TXQ;
+			}
+
+		} else if ((type & IEEE80211_FC0_TYPE_MASK) ==
+		    IEEE80211_FC0_TYPE_MGT) {
+			txq_id = QOS_TXQ_FOR_MGT;
+		} else {
+			txq_id = NON_QOS_TXQ;
+		}
+
+	} else {
+		txq_id = NON_QOS_TXQ;
+	}
+
+	ring = &sc->sc_txq[txq_id];
+	data = &ring->data[ring->cur];
+	desc = data->desc;
+	cmd = data->cmd;
+	bzero(desc, sizeof (*desc));
+	bzero(cmd, sizeof (*cmd));
+
+	mutex_enter(&sc->sc_tx_lock);
+
+	/*
+	 * Need reschedule TX if TX buffer is full.
+	 */
+	if (ring->queued > ring->count - IWH_MAX_WIN_SIZE) {
+		IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+		"no txbuf\n"));
+
+		sc->sc_need_reschedule = 1;
+		mutex_exit(&sc->sc_tx_lock);
+
+		freemsg(m);
+		if ((type & IEEE80211_FC0_TYPE_MASK) !=
+		    IEEE80211_FC0_TYPE_DATA) {
+			freemsg(mp);
+		}
+		sc->sc_tx_nobuf++;
+		err = IWH_FAIL;
+		goto exit;
+	}
+	mutex_exit(&sc->sc_tx_lock);
+
+	freemsg(mp);
 
 	cmd->hdr.type = REPLY_TX;
 	cmd->hdr.flags = 0;
@@ -2967,44 +3216,88 @@
 	}
 #endif
 
+	tx->rts_retry_limit = IWH_TX_RTS_RETRY_LIMIT;
+	tx->data_retry_limit = IWH_TX_DATA_RETRY_LIMIT;
+
 	/*
-	 * pickup a rate
+	 * specific TX parameters for management frames
 	 */
 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
 	    IEEE80211_FC0_TYPE_MGT) {
 		/*
 		 * mgmt frames are sent at 1M
 		 */
-		rate = in->in_rates.ir_rates[0];
+		if ((in->in_rates.ir_rates[0] &
+		    IEEE80211_RATE_VAL) != 0) {
+			rate = in->in_rates.ir_rates[0] & IEEE80211_RATE_VAL;
+		} else {
+			rate = 2;
+		}
+
+		tx->tx_flags |= LE_32(TX_CMD_FLG_SEQ_CTL_MSK);
+
+		/*
+		 * tell h/w to set timestamp in probe responses
+		 */
+		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+		    IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+			tx->tx_flags |= LE_32(TX_CMD_FLG_TSF_MSK);
+
+			tx->data_retry_limit = 3;
+			if (tx->data_retry_limit < tx->rts_retry_limit) {
+				tx->rts_retry_limit = tx->data_retry_limit;
+			}
+		}
+
+		if (((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+		    IEEE80211_FC0_SUBTYPE_ASSOC_REQ) ||
+		    ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+		    IEEE80211_FC0_SUBTYPE_REASSOC_REQ)) {
+			tx->timeout.pm_frame_timeout = LE_16(3);
+		} else {
+			tx->timeout.pm_frame_timeout = LE_16(2);
+		}
+
 	} else {
 		/*
-		 * do it here for the software way rate control.
+		 * do it here for the software way rate scaling.
 		 * later for rate scaling in hardware.
-		 * maybe like the following, for management frame:
-		 * tx->initial_rate_index = LINK_QUAL_MAX_RETRY_NUM - 1;
-		 * for data frame:
-		 * tx->tx_flags |= (LE_32(TX_CMD_FLG_STA_RATE_MSK));
-		 * rate = in->in_rates.ir_rates[in->in_txrate];
-		 * tx->initial_rate_index = 1;
 		 *
 		 * now the txrate is determined in tx cmd flags, set to the
-		 * max value 54M for 11g and 11M for 11b.
+		 * max value 54M for 11g and 11M for 11b and 96M for 11n
+		 * originally.
 		 */
-
 		if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
 			rate = ic->ic_fixed_rate;
 		} else {
-			rate = in->in_rates.ir_rates[in->in_txrate];
+			if ((in->in_flags & IEEE80211_NODE_HT) &&
+			    (sc->sc_ht_conf.ht_support)) {
+				iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+				rate = amrr->ht_mcs_idx;
+			} else {
+				if ((in->in_rates.ir_rates[in->in_txrate] &
+				    IEEE80211_RATE_VAL) != 0) {
+					rate = in->in_rates.
+					    ir_rates[in->in_txrate] &
+					    IEEE80211_RATE_VAL;
+				}
+			}
 		}
-	}
-
-	rate &= IEEE80211_RATE_VAL;
-
-	IWH_DBG((IWH_DEBUG_TX, "tx rate[%d of %d] = %x",
+
+		if (tid != WME_TID_INVALID) {
+			tx->tid_tspec = (uint8_t)tid;
+			tx->tx_flags &= LE_32(~TX_CMD_FLG_SEQ_CTL_MSK);
+		} else {
+			tx->tx_flags |= LE_32(TX_CMD_FLG_SEQ_CTL_MSK);
+		}
+
+		tx->timeout.pm_frame_timeout = 0;
+	}
+
+	IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+	    "tx rate[%d of %d] = %x",
 	    in->in_txrate, in->in_rates.ir_nrates, rate));
 
-	tx->tx_flags |= (LE_32(TX_CMD_FLG_SEQ_CTL_MSK));
-
 	len0 = roundup(4 + sizeof (iwh_tx_cmd_t) + hdrlen, 4);
 	if (len0 != (4 + sizeof (iwh_tx_cmd_t) + hdrlen)) {
 		tx->tx_flags |= LE_32(TX_CMD_FLG_MH_PAD_MSK);
@@ -3019,39 +3312,33 @@
 		tx->sta_id = IWH_AP_ID;
 	}
 
-	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
-	    IEEE80211_FC0_TYPE_MGT) {
-		/* tell h/w to set timestamp in probe responses */
-		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
-		    IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
-			tx->tx_flags |= LE_32(TX_CMD_FLG_TSF_MSK);
-		}
-
-		if (((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
-		    IEEE80211_FC0_SUBTYPE_ASSOC_REQ) ||
-		    ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
-		    IEEE80211_FC0_SUBTYPE_REASSOC_REQ)) {
-			tx->timeout.pm_frame_timeout = LE_16(3);
+	if ((in->in_flags & IEEE80211_NODE_HT) &&
+	    (sc->sc_ht_conf.ht_support) &&
+	    ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+	    IEEE80211_FC0_TYPE_DATA)) {
+		if (rate >= HT_2CHAIN_RATE_MIN_IDX) {
+			rate |= LE_32(RATE_MCS_ANT_AB_MSK);
 		} else {
-			tx->timeout.pm_frame_timeout = LE_16(2);
+			rate |= LE_32(RATE_MCS_ANT_B_MSK);
 		}
+
+		rate |= LE_32((1 << RATE_MCS_HT_POS));
+
+		tx->rate.r.rate_n_flags = rate;
+
 	} else {
-		tx->timeout.pm_frame_timeout = 0;
-	}
-
-	if (2 == rate || 4 == rate || 11 == rate || 22 == rate) {
-		masks |= RATE_MCS_CCK_MSK;
-	}
-
-	masks |= RATE_MCS_ANT_B_MSK;
-	tx->rate.r.rate_n_flags = LE_32(iwh_rate_to_plcp(rate) | masks);
-
-	IWH_DBG((IWH_DEBUG_TX, "tx flag = %x",
+		if (2 == rate || 4 == rate || 11 == rate || 22 == rate) {
+			masks |= RATE_MCS_CCK_MSK;
+		}
+
+		masks |= RATE_MCS_ANT_B_MSK;
+		tx->rate.r.rate_n_flags = LE_32(iwh_rate_to_plcp(rate) | masks);
+	}
+
+	IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+	    "tx flag = %x",
 	    tx->tx_flags));
 
-	tx->rts_retry_limit = 60;
-	tx->data_retry_limit = 15;
-
 	tx->stop_time.life_time  = LE_32(0xffffffff);
 
 	tx->len = LE_16(len);
@@ -3066,7 +3353,8 @@
 	m->b_rptr += hdrlen;
 	(void) memcpy(data->dma_data.mem_va, m->b_rptr, len - hdrlen);
 
-	IWH_DBG((IWH_DEBUG_TX, "sending data: qid=%d idx=%d len=%d",
+	IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+	    "sending data: qid=%d idx=%d len=%d",
 	    ring->qid, ring->cur, len));
 
 	/*
@@ -3080,7 +3368,8 @@
 	desc->pa[0].val2 =
 	    ((data->dma_data.cookie.dmac_address & 0xffff0000) >> 16) |
 	    ((len - hdrlen) << 20);
-	IWH_DBG((IWH_DEBUG_TX, "phy addr1 = 0x%x phy addr2 = 0x%x "
+	IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+	    "phy addr1 = 0x%x phy addr2 = 0x%x "
 	    "len1 = 0x%x, len2 = 0x%x val1 = 0x%x val2 = 0x%x",
 	    data->paddr_cmd, data->dma_data.cookie.dmac_address,
 	    len0, len - hdrlen, desc->pa[0].val1, desc->pa[0].val2));
@@ -3118,11 +3407,17 @@
 	ic->ic_stats.is_tx_bytes += len;
 	ic->ic_stats.is_tx_frags++;
 
+	mutex_enter(&sc->sc_mt_lock);
 	if (0 == sc->sc_tx_timer) {
 		sc->sc_tx_timer = 4;
 	}
+	mutex_exit(&sc->sc_mt_lock);
 
 exit:
+	if (mutex_owned(&sc->sc_suspend_lock)) {
+		mutex_exit(&sc->sc_suspend_lock);
+	}
+
 	return (err);
 }
 
@@ -3132,9 +3427,15 @@
 static void
 iwh_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
 {
-	iwh_sc_t	*sc  = (iwh_sc_t *)arg;
-	ieee80211com_t	*ic = &sc->sc_ic;
-	int		err;
+	iwh_sc_t	*sc;
+	ieee80211com_t	*ic;
+	int		err = EINVAL;
+
+	if (NULL == arg) {
+		return;
+	}
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
 
 	err = ieee80211_ioctl(ic, wq, mp);
 	if (ENETRESET == err) {
@@ -3164,8 +3465,13 @@
 iwh_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
     uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm)
 {
-	iwh_sc_t		*sc = (iwh_sc_t *)arg;
-	int			err = 0;
+	iwh_sc_t	*sc;
+	int		err = EINVAL;
+
+	if (NULL == arg) {
+		return (EINVAL);
+	}
+	sc = (iwh_sc_t *)arg;
 
 	err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num,
 	    pr_flags, wldp_length, wldp_buf, perm);
@@ -3177,9 +3483,15 @@
 iwh_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
     uint_t wldp_length, const void *wldp_buf)
 {
-	iwh_sc_t		*sc = (iwh_sc_t *)arg;
-	ieee80211com_t		*ic = &sc->sc_ic;
-	int			err;
+	iwh_sc_t		*sc;
+	ieee80211com_t		*ic;
+	int			err = EINVAL;
+
+	if (NULL == arg) {
+		return (EINVAL);
+	}
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
 
 	err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
 	    wldp_buf);
@@ -3204,10 +3516,16 @@
 static int
 iwh_m_stat(void *arg, uint_t stat, uint64_t *val)
 {
-	iwh_sc_t	*sc  = (iwh_sc_t *)arg;
-	ieee80211com_t	*ic = &sc->sc_ic;
+	iwh_sc_t	*sc;
+	ieee80211com_t	*ic;
 	ieee80211_node_t *in;
 
+	if (NULL == arg) {
+		return (EINVAL);
+	}
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
+
 	mutex_enter(&sc->sc_glock);
 
 	switch (stat) {
@@ -3285,29 +3603,30 @@
 static int
 iwh_m_start(void *arg)
 {
-	iwh_sc_t *sc = (iwh_sc_t *)arg;
-	ieee80211com_t	*ic = &sc->sc_ic;
-	int err;
+	iwh_sc_t *sc;
+	ieee80211com_t	*ic;
+	int err = IWH_FAIL;
+
+	if (NULL == arg) {
+		return (EINVAL);
+	}
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
 
 	err = iwh_init(sc);
-
 	if (err != IWH_SUCCESS) {
 		/*
 		 * The hw init err(eg. RF is OFF). Return Success to make
 		 * the 'plumb' succeed. The iwh_thread() tries to re-init
 		 * background.
 		 */
-		mutex_enter(&sc->sc_glock);
 		sc->sc_flags |= IWH_F_HW_ERR_RECOVER;
-		mutex_exit(&sc->sc_glock);
 		return (IWH_SUCCESS);
 	}
 
 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
 
-	mutex_enter(&sc->sc_glock);
 	sc->sc_flags |= IWH_F_RUNNING;
-	mutex_exit(&sc->sc_glock);
 
 	return (IWH_SUCCESS);
 }
@@ -3318,8 +3637,14 @@
 static void
 iwh_m_stop(void *arg)
 {
-	iwh_sc_t *sc = (iwh_sc_t *)arg;
-	ieee80211com_t	*ic = &sc->sc_ic;
+	iwh_sc_t *sc;
+	ieee80211com_t	*ic;
+
+	if (NULL == arg) {
+		return;
+	}
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
 
 	iwh_stop(sc);
 
@@ -3330,16 +3655,11 @@
 
 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
 
-	mutex_enter(&sc->sc_mt_lock);
-
 	sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
 	sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
-	mutex_exit(&sc->sc_mt_lock);
-	mutex_enter(&sc->sc_glock);
+
 	sc->sc_flags &= ~IWH_F_RUNNING;
 	sc->sc_flags &= ~IWH_F_SCANNING;
-
-	mutex_exit(&sc->sc_glock);
 }
 
 /*
@@ -3348,9 +3668,15 @@
 static int
 iwh_m_unicst(void *arg, const uint8_t *macaddr)
 {
-	iwh_sc_t *sc = (iwh_sc_t *)arg;
-	ieee80211com_t	*ic = &sc->sc_ic;
-	int err;
+	iwh_sc_t *sc;
+	ieee80211com_t	*ic;
+	int err = IWH_SUCCESS;
+
+	if (NULL == arg) {
+		return (EINVAL);
+	}
+	sc = (iwh_sc_t *)arg;
+	ic = &sc->sc_ic;
 
 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
@@ -3364,21 +3690,21 @@
 		}
 	}
 
-	return (IWH_SUCCESS);
+	return (err);
 
 fail:
 	return (err);
 }
 
+/* ARGSUSED */
 static int
-/* LINTED: argument unused in function: arg add m */
 iwh_m_multicst(void *arg, boolean_t add, const uint8_t *m)
 {
 	return (IWH_SUCCESS);
 }
 
+/* ARGSUSED */
 static int
-/* LINTED: argument unused in function: arg on */
 iwh_m_promisc(void *arg, boolean_t on)
 {
 	return (IWH_SUCCESS);
@@ -3398,8 +3724,6 @@
 	int times = 0;
 #endif
 
-	mutex_enter(&sc->sc_mt_lock);
-
 	while (sc->sc_mf_thread_switch) {
 		tmp = IWH_READ(sc, CSR_GP_CNTRL);
 		if (tmp & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) {
@@ -3411,11 +3735,8 @@
 		/*
 		 * If  in SUSPEND or the RF is OFF, do nothing.
 		 */
-		if ((sc->sc_flags & IWH_F_SUSPEND) ||
-		    (sc->sc_flags & IWH_F_RADIO_OFF)) {
-			mutex_exit(&sc->sc_mt_lock);
+		if (sc->sc_flags & IWH_F_RADIO_OFF) {
 			delay(drv_usectohz(100000));
-			mutex_enter(&sc->sc_mt_lock);
 			continue;
 		}
 
@@ -3425,8 +3746,7 @@
 		if (ic->ic_mach &&
 		    (sc->sc_flags & IWH_F_HW_ERR_RECOVER)) {
 
-			IWH_DBG((IWH_DEBUG_FW,
-			    "iwh_thread(): "
+			IWH_DBG((IWH_DEBUG_FW, "iwh_thread(): "
 			    "try to recover fatal hw error: %d\n", times++));
 
 			iwh_stop(sc);
@@ -3436,10 +3756,8 @@
 				bcopy(&sc->sc_config, &sc->sc_config_save,
 				    sizeof (sc->sc_config));
 			} else {
-				mutex_exit(&sc->sc_mt_lock);
 				ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
 				delay(drv_usectohz(2000000 + n*500000));
-				mutex_enter(&sc->sc_mt_lock);
 			}
 
 			err = iwh_init(sc);
@@ -3460,43 +3778,22 @@
 			    iwh_fast_recover(sc) != IWH_SUCCESS) {
 				sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
 
-				mutex_exit(&sc->sc_mt_lock);
 				delay(drv_usectohz(2000000));
-				if (sc->sc_ostate != IEEE80211_S_INIT)
+				if (sc->sc_ostate != IEEE80211_S_INIT) {
 					ieee80211_new_state(ic,
 					    IEEE80211_S_SCAN, 0);
-				mutex_enter(&sc->sc_mt_lock);
+				}
 			}
 		}
 
-		if (ic->ic_mach && (sc->sc_flags & IWH_F_LAZY_RESUME)) {
-			IWH_DBG((IWH_DEBUG_RESUME,
-			    "iwh_thread(): "
-			    "lazy resume\n"));
-			sc->sc_flags &= ~IWH_F_LAZY_RESUME;
-			mutex_exit(&sc->sc_mt_lock);
-			/*
-			 * NB: under WPA mode, this call hangs (door problem?)
-			 * when called in iwh_attach() and iwh_detach() while
-			 * system is in the procedure of CPR. To be safe, let
-			 * the thread do this.
-			 */
-			ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
-			mutex_enter(&sc->sc_mt_lock);
-		}
-
 		if (ic->ic_mach &&
 		    (sc->sc_flags & IWH_F_SCANNING) && sc->sc_scan_pending) {
-			IWH_DBG((IWH_DEBUG_SCAN,
-			    "iwh_thread(): "
+			IWH_DBG((IWH_DEBUG_SCAN, "iwh_thread(): "
 			    "wait for probe response\n"));
 
 			sc->sc_scan_pending--;
-			mutex_exit(&sc->sc_mt_lock);
 			delay(drv_usectohz(200000));
-			if (sc->sc_flags & IWH_F_SCANNING)
-				ieee80211_next_scan(ic);
-			mutex_enter(&sc->sc_mt_lock);
+			ieee80211_next_scan(ic);
 		}
 
 		/*
@@ -3505,15 +3802,14 @@
 		if (ic->ic_mach &&
 		    (sc->sc_flags & IWH_F_RATE_AUTO_CTL)) {
 			clk = ddi_get_lbolt();
-			if (clk > sc->sc_clk + drv_usectohz(500000)) {
+			if (clk > sc->sc_clk + drv_usectohz(1000000)) {
 				iwh_amrr_timeout(sc);
 			}
 		}
 
-		mutex_exit(&sc->sc_mt_lock);
 		delay(drv_usectohz(100000));
+
 		mutex_enter(&sc->sc_mt_lock);
-
 		if (sc->sc_tx_timer) {
 			timeout++;
 			if (10 == timeout) {
@@ -3521,16 +3817,17 @@
 				if (0 == sc->sc_tx_timer) {
 					sc->sc_flags |= IWH_F_HW_ERR_RECOVER;
 					sc->sc_ostate = IEEE80211_S_RUN;
-					IWH_DBG((IWH_DEBUG_FW,
-					    "iwh_thread(): try to recover from"
-					    " 'send fail\n"));
+					IWH_DBG((IWH_DEBUG_FW, "iwh_thread(): "
+					    "try to recover from "
+					    "send fail\n"));
 				}
 				timeout = 0;
 			}
 		}
-
-	}
-
+		mutex_exit(&sc->sc_mt_lock);
+	}
+
+	mutex_enter(&sc->sc_mt_lock);
 	sc->sc_mf_thread = NULL;
 	cv_signal(&sc->sc_mt_cv);
 	mutex_exit(&sc->sc_mt_lock);
@@ -3550,7 +3847,8 @@
 	ASSERT(size <= sizeof (cmd->data));
 	ASSERT(mutex_owned(&sc->sc_glock));
 
-	IWH_DBG((IWH_DEBUG_CMD, "iwh_cmd() code[%d]", code));
+	IWH_DBG((IWH_DEBUG_CMD, "iwh_cmd() "
+	    "code[%d]", code));
 	desc = ring->data[ring->cur].desc;
 	cmd = ring->data[ring->cur].cmd;
 
@@ -3566,6 +3864,10 @@
 	    (uint32_t)(ring->data[ring->cur].paddr_cmd & 0xffffffff);
 	desc->pa[0].val1 = ((4 + size) << 4) & 0xfff0;
 
+	if (async) {
+		sc->sc_cmd_accum++;
+	}
+
 	/*
 	 * kick cmd ring XXX
 	 */
@@ -3582,18 +3884,20 @@
 		return (IWH_SUCCESS);
 	} else {
 		clock_t clk;
-		sc->sc_flags &= ~IWH_F_CMD_DONE;
+
 		clk = ddi_get_lbolt() + drv_usectohz(2000000);
-		while (!(sc->sc_flags & IWH_F_CMD_DONE)) {
+		while (sc->sc_cmd_flag != SC_CMD_FLG_DONE) {
 			if (cv_timedwait(&sc->sc_cmd_cv,
 			    &sc->sc_glock, clk) < 0) {
 				break;
 			}
 		}
 
-		if (sc->sc_flags & IWH_F_CMD_DONE) {
+		if (SC_CMD_FLG_DONE == sc->sc_cmd_flag) {
+			sc->sc_cmd_flag = SC_CMD_FLG_NONE;
 			return (IWH_SUCCESS);
 		} else {
+			sc->sc_cmd_flag = SC_CMD_FLG_NONE;
 			return (IWH_FAIL);
 		}
 	}
@@ -3623,11 +3927,7 @@
 {
 	ieee80211com_t *ic = &sc->sc_ic;
 	ieee80211_node_t *in = ic->ic_bss;
-	iwh_add_sta_t node;
-	iwh_link_quality_cmd_t link_quality;
-	struct ieee80211_rateset rs;
-	uint16_t masks = 0, rate;
-	int i, err;
+	int err = IWH_FAIL;
 
 	/*
 	 * update adapter's configuration according
@@ -3635,16 +3935,24 @@
 	 */
 	IEEE80211_ADDR_COPY(sc->sc_config.bssid, in->in_bssid);
 	sc->sc_config.chan = LE_16(ieee80211_chan2ieee(ic, in->in_chan));
-	if (IEEE80211_MODE_11B == ic->ic_curmode) {
-		sc->sc_config.cck_basic_rates  = 0x03;
-		sc->sc_config.ofdm_basic_rates = 0;
-	} else if ((in->in_chan != IEEE80211_CHAN_ANYC) &&
-	    (IEEE80211_IS_CHAN_5GHZ(in->in_chan))) {
-		sc->sc_config.cck_basic_rates  = 0;
-		sc->sc_config.ofdm_basic_rates = 0x15;
-	} else { /* assume 802.11b/g */
-		sc->sc_config.cck_basic_rates  = 0x0f;
-		sc->sc_config.ofdm_basic_rates = 0xff;
+
+	if (ic->ic_curmode != IEEE80211_MODE_11NG) {
+
+		sc->sc_config.ofdm_ht_triple_stream_basic_rates = 0;
+		sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0;
+		sc->sc_config.ofdm_ht_single_stream_basic_rates = 0;
+
+		if (IEEE80211_MODE_11B == ic->ic_curmode) {
+			sc->sc_config.cck_basic_rates  = 0x03;
+			sc->sc_config.ofdm_basic_rates = 0;
+		} else if ((in->in_chan != IEEE80211_CHAN_ANYC) &&
+		    (IEEE80211_IS_CHAN_5GHZ(in->in_chan))) {
+			sc->sc_config.cck_basic_rates  = 0;
+			sc->sc_config.ofdm_basic_rates = 0x15;
+		} else { /* assume 802.11b/g */
+			sc->sc_config.cck_basic_rates  = 0x0f;
+			sc->sc_config.ofdm_basic_rates = 0xff;
+		}
 	}
 
 	sc->sc_config.flags &= ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK |
@@ -3662,7 +3970,8 @@
 		sc->sc_config.flags &= LE_32(~RXON_FLG_SHORT_PREAMBLE_MSK);
 	}
 
-	IWH_DBG((IWH_DEBUG_80211, "config chan %d flags %x "
+	IWH_DBG((IWH_DEBUG_80211, "iwh_hw_set_before_auth(): "
+	    "config chan %d flags %x "
 	    "filter_flags %x  cck %x ofdm %x"
 	    " bssid:%02x:%02x:%02x:%02x:%02x:%2x\n",
 	    LE_16(sc->sc_config.chan), LE_32(sc->sc_config.flags),
@@ -3682,61 +3991,26 @@
 
 	err = iwh_tx_power_table(sc, 1);
 	if (err != IWH_SUCCESS) {
-		cmn_err(CE_WARN, "iwh_config(): "
-		    "failed to set tx power table.\n");
 		return (err);
 	}
 
 	/*
 	 * add default AP node
 	 */
-	(void) memset(&node, 0, sizeof (node));
-	IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
-	node.mode = 0;
-	node.sta.sta_id = IWH_AP_ID;
-	node.station_flags = 0;
-	err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+	err = iwh_add_ap_sta(sc);
 	if (err != IWH_SUCCESS) {
-		cmn_err(CE_WARN, "iwh_hw_set_before_auth(): "
-		    "failed to add BSS node\n");
 		return (err);
 	}
 
 	/*
-	 * TX_LINK_QUALITY cmd
+	 * set up retry rate table for AP node
 	 */
-	(void) memset(&link_quality, 0, sizeof (link_quality));
-	rs = ic->ic_sup_rates[ieee80211_chan2mode(ic, ic->ic_curchan)];
-	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
-		if (i < rs.ir_nrates) {
-			rate = rs.ir_rates[rs.ir_nrates - i];
-		} else {
-			rate = 2;
-		}
-
-		if (2 == rate || 4 == rate || 11 == rate || 22 == rate) {
-			masks |= RATE_MCS_CCK_MSK;
-		}
-		masks |= RATE_MCS_ANT_B_MSK;
-		masks &= ~RATE_MCS_ANT_A_MSK;
-		link_quality.rate_n_flags[i] =
-		    LE_32(iwh_rate_to_plcp(rate) | masks);
-	}
-
-	link_quality.general_params.single_stream_ant_msk = 2;
-	link_quality.general_params.dual_stream_ant_msk = 3;
-	link_quality.agg_params.agg_dis_start_th = 3;
-	link_quality.agg_params.agg_time_limit = LE_16(4000);
-	link_quality.sta_id = IWH_AP_ID;
-	err = iwh_cmd(sc, REPLY_TX_LINK_QUALITY_CMD, &link_quality,
-	    sizeof (link_quality), 1);
+	err = iwh_ap_lq(sc);
 	if (err != IWH_SUCCESS) {
-		cmn_err(CE_WARN, "iwh_hw_set_before_auth(): "
-		    "failed to config link quality table\n");
 		return (err);
 	}
 
-	return (IWH_SUCCESS);
+	return (err);
 }
 
 /*
@@ -3796,7 +4070,8 @@
 	if (ic->ic_des_esslen) {
 		bcopy(ic->ic_des_essid, essid, ic->ic_des_esslen);
 		essid[ic->ic_des_esslen] = '\0';
-		IWH_DBG((IWH_DEBUG_SCAN, "directed scan %s\n", essid));
+		IWH_DBG((IWH_DEBUG_SCAN, "iwh_scan(): "
+		    "directed scan %s\n", essid));
 
 		bcopy(ic->ic_des_essid, hdr->direct_scan[0].ssid,
 		    ic->ic_des_esslen);
@@ -3826,7 +4101,8 @@
 	if (in->in_esslen) {
 		bcopy(in->in_essid, essid, in->in_esslen);
 		essid[in->in_esslen] = '\0';
-		IWH_DBG((IWH_DEBUG_SCAN, "probe with ESSID %s\n",
+		IWH_DBG((IWH_DEBUG_SCAN, "iwh_scan(): "
+		    "probe with ESSID %s\n",
 		    essid));
 	}
 	*frm++ = IEEE80211_ELEMID_SSID;
@@ -3936,7 +4212,7 @@
 	iwh_rem_sta_t	rm_sta;
 	const uint8_t bcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 	iwh_link_quality_cmd_t link_quality;
-	int i, err;
+	int i, err = IWH_FAIL;
 	uint16_t masks = 0;
 
 	/*
@@ -3975,12 +4251,14 @@
 	IEEE80211_ADDR_COPY(sc->sc_config.wlap_bssid, ic->ic_macaddr);
 	sc->sc_config.chan = LE_16(ieee80211_chan2ieee(ic, ic->ic_curchan));
 	sc->sc_config.flags = LE_32(RXON_FLG_BAND_24G_MSK);
+	sc->sc_config.flags &= LE_32(~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
+	    RXON_FLG_CHANNEL_MODE_PURE_40_MSK));
 
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
 		sc->sc_config.dev_type = RXON_DEV_TYPE_ESS;
-		sc->sc_config.filter_flags |=
-		    LE_32(RXON_FILTER_DIS_DECRYPT_MSK |
+		sc->sc_config.filter_flags |= LE_32(RXON_FILTER_ACCEPT_GRP_MSK |
+		    RXON_FILTER_DIS_DECRYPT_MSK |
 		    RXON_FILTER_DIS_GRP_DECRYPT_MSK);
 		break;
 
@@ -4005,16 +4283,43 @@
 		break;
 	}
 
+	/*
+	 * Support all CCK rates.
+	 */
 	sc->sc_config.cck_basic_rates  = 0x0f;
+
+	/*
+	 * Support all OFDM rates.
+	 */
 	sc->sc_config.ofdm_basic_rates = 0xff;
 
 	/*
-	 * set antenna
+	 * Determine HT supported rates.
 	 */
-	sc->sc_config.rx_chain = LE_16(RXON_RX_CHAIN_DRIVER_FORCE_MSK |
-	    (0x7 << RXON_RX_CHAIN_VALID_POS) |
-	    (0x2 << RXON_RX_CHAIN_FORCE_SEL_POS) |
-	    (0x2 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS));
+	switch (sc->sc_ht_conf.rx_stream_count) {
+	case 3:
+		sc->sc_config.ofdm_ht_triple_stream_basic_rates = 0xff;
+		sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0xff;
+		sc->sc_config.ofdm_ht_single_stream_basic_rates = 0xff;
+		break;
+	case 2:
+		sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0xff;
+		sc->sc_config.ofdm_ht_single_stream_basic_rates = 0xff;
+		break;
+	case 1:
+		sc->sc_config.ofdm_ht_single_stream_basic_rates = 0xff;
+		break;
+	default:
+		cmn_err(CE_WARN, "iwh_config(): "
+		    "RX stream count %d is not in suitable range\n",
+		    sc->sc_ht_conf.rx_stream_count);
+		return (IWH_FAIL);
+	}
+
+	/*
+	 * set RX chains/antennas.
+	 */
+	iwh_config_rxon_chain(sc);
 
 	err = iwh_cmd(sc, REPLY_RXON, &sc->sc_config,
 	    sizeof (iwh_rxon_cmd_t), 0);
@@ -4039,12 +4344,10 @@
 	}
 
 	/*
-	 * configure TX pwoer table
+	 * configure TX power table
 	 */
 	err = iwh_tx_power_table(sc, 0);
 	if (err != IWH_SUCCESS) {
-		cmn_err(CE_WARN, "iwh_config(): "
-		    "failed to set tx power table.\n");
 		return (err);
 	}
 
@@ -4089,7 +4392,7 @@
 		return (err);
 	}
 
-	return (IWH_SUCCESS);
+	return (err);
 }
 
 /*
@@ -4106,8 +4409,9 @@
 	iwh_sc_t *sc;
 
 	sc = ddi_get_soft_state(iwh_soft_state_p, ddi_get_instance(dip));
-	if (sc == NULL)
+	if (sc == NULL) {
 		return (DDI_FAILURE);
+	}
 
 #ifdef DEBUG
 	/* by pass any messages, if it's quiesce */
@@ -4153,7 +4457,7 @@
 
 #ifdef	DEBUG
 	if (2000 == n) {
-		IWH_DBG((IWH_DEBUG_HW,
+		IWH_DBG((IWH_DEBUG_HW, "iwh_stop_master(): "
 		    "timeout waiting for master stop\n"));
 	}
 #endif
@@ -4334,13 +4638,15 @@
 	eep_gp = IWH_READ(sc, CSR_EEPROM_GP);
 	if ((eep_gp & CSR_EEPROM_GP_VALID_MSK) ==
 	    CSR_EEPROM_GP_BAD_SIGNATURE) {
-		IWH_DBG((IWH_DEBUG_EEPROM, "not find eeprom\n"));
+		IWH_DBG((IWH_DEBUG_EEPROM, "iwh_eep_load(): "
+		    "not find eeprom\n"));
 		return (IWH_FAIL);
 	}
 
 	rr = iwh_eep_sem_down(sc);
 	if (rr != 0) {
-		IWH_DBG((IWH_DEBUG_EEPROM, "driver failed to own EEPROM\n"));
+		IWH_DBG((IWH_DEBUG_EEPROM, "iwh_eep_load(): "
+		    "driver failed to own EEPROM\n"));
 		return (IWH_FAIL);
 	}
 
@@ -4358,7 +4664,7 @@
 		}
 
 		if (!(rv & 1)) {
-			IWH_DBG((IWH_DEBUG_EEPROM,
+			IWH_DBG((IWH_DEBUG_EEPROM, "iwh_eep_load(): "
 			    "time out when read eeprome\n"));
 			iwh_eep_sem_up(sc);
 			return (IWH_FAIL);
@@ -4381,7 +4687,8 @@
 
 	IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->sc_eep_map[EEP_MAC_ADDRESS]);
 
-	IWH_DBG((IWH_DEBUG_EEPROM, "mac:%2x:%2x:%2x:%2x:%2x:%2x\n",
+	IWH_DBG((IWH_DEBUG_EEPROM, "iwh_get_mac_from_eep(): "
+	    "mac:%2x:%2x:%2x:%2x:%2x:%2x\n",
 	    ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
 	    ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]));
 }
@@ -4392,7 +4699,7 @@
 static int
 iwh_init(iwh_sc_t *sc)
 {
-	int n, err;
+	int err = IWH_FAIL;
 	clock_t clk;
 
 	/*
@@ -4416,36 +4723,24 @@
 	    sc->sc_dma_fw_data.mem_va,
 	    sc->sc_dma_fw_data.alength);
 
-	for (n = 0; n < 2; n++) {
-		/* load firmware init segment into NIC */
-		err = iwh_load_init_firmware(sc);
-		if (err != IWH_SUCCESS) {
-			cmn_err(CE_WARN, "iwh_init(): "
-			    "failed to setup init firmware\n");
-			continue;
-		}
-
-		/*
-		 * now press "execute" start running
-		 */
-		IWH_WRITE(sc, CSR_RESET, 0);
-		break;
-	}
-
-	mutex_exit(&sc->sc_glock);
-
-	if (2 == n) {
+	/* load firmware init segment into NIC */
+	err = iwh_load_init_firmware(sc);
+	if (err != IWH_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_init(): "
-		    "failed to load init firmware\n");
+		    "failed to setup init firmware\n");
+		mutex_exit(&sc->sc_glock);
 		return (IWH_FAIL);
 	}
 
-	mutex_enter(&sc->sc_ucode_lock);
+	/*
+	 * now press "execute" start running
+	 */
+	IWH_WRITE(sc, CSR_RESET, 0);
 
 	clk = ddi_get_lbolt() + drv_usectohz(1000000);
 	while (!(sc->sc_flags & IWH_F_FW_INIT)) {
 		if (cv_timedwait(&sc->sc_ucode_cv,
-		    &sc->sc_ucode_lock, clk) < 0) {
+		    &sc->sc_glock, clk) < 0) {
 			break;
 		}
 	}
@@ -4453,11 +4748,11 @@
 	if (!(sc->sc_flags & IWH_F_FW_INIT)) {
 		cmn_err(CE_WARN, "iwh_init(): "
 		    "failed to process init alive.\n");
-		mutex_exit(&sc->sc_ucode_lock);
+		mutex_exit(&sc->sc_glock);
 		return (IWH_FAIL);
 	}
 
-	mutex_exit(&sc->sc_ucode_lock);
+	mutex_exit(&sc->sc_glock);
 
 	/*
 	 * stop chipset for initializing chipset again
@@ -4473,38 +4768,26 @@
 		return (IWH_FAIL);
 	}
 
-	for (n = 0; n < 2; n++) {
-		/*
-		 * load firmware run segment into NIC
-		 */
-		err = iwh_load_run_firmware(sc);
-		if (err != IWH_SUCCESS) {
-			cmn_err(CE_WARN, "iwh_init(): "
-			    "failed to setup run firmware\n");
-			continue;
-		}
-
-		/*
-		 * now press "execute" start running
-		 */
-		IWH_WRITE(sc, CSR_RESET, 0);
-		break;
-	}
-
-	mutex_exit(&sc->sc_glock);
-
-	if (2 == n) {
+	/*
+	 * load firmware run segment into NIC
+	 */
+	err = iwh_load_run_firmware(sc);
+	if (err != IWH_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_init(): "
-		    "failed to load run firmware\n");
+		    "failed to setup run firmware\n");
+		mutex_exit(&sc->sc_glock);
 		return (IWH_FAIL);
 	}
 
-	mutex_enter(&sc->sc_ucode_lock);
+	/*
+	 * now press "execute" start running
+	 */
+	IWH_WRITE(sc, CSR_RESET, 0);
 
 	clk = ddi_get_lbolt() + drv_usectohz(1000000);
 	while (!(sc->sc_flags & IWH_F_FW_INIT)) {
 		if (cv_timedwait(&sc->sc_ucode_cv,
-		    &sc->sc_ucode_lock, clk) < 0) {
+		    &sc->sc_glock, clk) < 0) {
 			break;
 		}
 	}
@@ -4512,11 +4795,13 @@
 	if (!(sc->sc_flags & IWH_F_FW_INIT)) {
 		cmn_err(CE_WARN, "iwh_init(): "
 		    "failed to process runtime alive.\n");
-		mutex_exit(&sc->sc_ucode_lock);
+		mutex_exit(&sc->sc_glock);
 		return (IWH_FAIL);
 	}
 
-	mutex_exit(&sc->sc_ucode_lock);
+	mutex_exit(&sc->sc_glock);
+
+	DELAY(1000);
 
 	mutex_enter(&sc->sc_glock);
 	sc->sc_flags &= ~IWH_F_FW_INIT;
@@ -4550,8 +4835,9 @@
 	int i;
 
 	/* by pass if it's quiesced */
-	if (!(sc->sc_flags & IWH_F_QUIESCED))
+	if (!(sc->sc_flags & IWH_F_QUIESCED)) {
 		mutex_enter(&sc->sc_glock);
+	}
 
 	IWH_WRITE(sc, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
 	/*
@@ -4581,13 +4867,17 @@
 
 	iwh_stop_master(sc);
 
+	mutex_enter(&sc->sc_mt_lock);
 	sc->sc_tx_timer = 0;
+	mutex_exit(&sc->sc_mt_lock);
+
 	tmp = IWH_READ(sc, CSR_RESET);
 	IWH_WRITE(sc, CSR_RESET, tmp | CSR_RESET_REG_FLAG_SW_RESET);
 
 	/* by pass if it's quiesced */
-	if (!(sc->sc_flags & IWH_F_QUIESCED))
+	if (!(sc->sc_flags & IWH_F_QUIESCED)) {
 		mutex_exit(&sc->sc_glock);
+	}
 }
 
 /*
@@ -4602,7 +4892,9 @@
 #define	is_failure(amrr)	\
 	((amrr)->retrycnt > (amrr)->txcnt / 3)
 #define	is_enough(amrr)		\
-	((amrr)->txcnt > 100)
+	((amrr)->txcnt > 200)
+#define	not_very_few(amrr)	\
+	((amrr)->txcnt > 40)
 #define	is_min_rate(in)		\
 	(0 == (in)->in_txrate)
 #define	is_max_rate(in)		\
@@ -4624,6 +4916,7 @@
 	amrr->recovery = 0;
 	amrr->txcnt = amrr->retrycnt = 0;
 	amrr->success_threshold = IWH_AMRR_MIN_SUCCESS_THRESHOLD;
+	amrr->ht_mcs_idx = 0;	/* 6Mbps */
 }
 
 static void
@@ -4631,7 +4924,8 @@
 {
 	ieee80211com_t *ic = &sc->sc_ic;
 
-	IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_timeout() enter\n"));
+	IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_timeout(): "
+	    "enter\n"));
 
 	if (IEEE80211_M_STA == ic->ic_opmode) {
 		iwh_amrr_ratectl(NULL, ic->ic_bss);
@@ -4642,8 +4936,120 @@
 	sc->sc_clk = ddi_get_lbolt();
 }
 
+static int
+iwh_is_max_rate(ieee80211_node_t *in)
+{
+	int i;
+	iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+	uint8_t r = (uint8_t)amrr->ht_mcs_idx;
+	ieee80211com_t *ic = in->in_ic;
+	iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+	if (in->in_flags & IEEE80211_NODE_HT) {
+		for (i = in->in_htrates.rs_nrates - 1; i >= 0; i--) {
+			r = in->in_htrates.rs_rates[i] &
+			    IEEE80211_RATE_VAL;
+			if (sc->sc_ht_conf.tx_support_mcs[r/8] &
+			    (1 << (r%8))) {
+				break;
+			}
+		}
+
+		return (r == (uint8_t)amrr->ht_mcs_idx);
+	} else {
+		return (is_max_rate(in));
+	}
+}
+
+static int
+iwh_is_min_rate(ieee80211_node_t *in)
+{
+	int i;
+	uint8_t r = 0;
+	iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+	ieee80211com_t *ic = in->in_ic;
+	iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+	if (in->in_flags & IEEE80211_NODE_HT) {
+		for (i = 0; i < in->in_htrates.rs_nrates; i++) {
+			r = in->in_htrates.rs_rates[i] &
+			    IEEE80211_RATE_VAL;
+			if (sc->sc_ht_conf.tx_support_mcs[r/8] &
+			    (1 << (r%8))) {
+				break;
+			}
+		}
+
+		return (r == (uint8_t)amrr->ht_mcs_idx);
+	} else {
+		return (is_min_rate(in));
+	}
+}
+
 static void
-/* LINTED: argument unused in function: arg */
+iwh_increase_rate(ieee80211_node_t *in)
+{
+	int i;
+	uint8_t r;
+	iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+	ieee80211com_t *ic = in->in_ic;
+	iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+	if (in->in_flags & IEEE80211_NODE_HT) {
+again:
+		amrr->ht_mcs_idx++;
+
+		for (i = 0; i < in->in_htrates.rs_nrates; i++) {
+			r = in->in_htrates.rs_rates[i] &
+			    IEEE80211_RATE_VAL;
+			if ((r == (uint8_t)amrr->ht_mcs_idx) &&
+			    (sc->sc_ht_conf.tx_support_mcs[r/8] &
+			    (1 << (r%8)))) {
+				break;
+			}
+		}
+
+		if (i >= in->in_htrates.rs_nrates) {
+			goto again;
+		}
+	} else {
+		increase_rate(in);
+	}
+}
+
+static void
+iwh_decrease_rate(ieee80211_node_t *in)
+{
+	int i;
+	uint8_t r;
+	iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+	ieee80211com_t *ic = in->in_ic;
+	iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+	if (in->in_flags & IEEE80211_NODE_HT) {
+again:
+		amrr->ht_mcs_idx--;
+
+		for (i = 0; i < in->in_htrates.rs_nrates; i++) {
+			r = in->in_htrates.rs_rates[i] &
+			    IEEE80211_RATE_VAL;
+			if ((r == (uint8_t)amrr->ht_mcs_idx) &&
+			    (sc->sc_ht_conf.tx_support_mcs[r/8] &
+			    (1 << (r%8)))) {
+				break;
+			}
+		}
+
+		if (i >= in->in_htrates.rs_nrates) {
+			goto again;
+		}
+	} else {
+		decrease_rate(in);
+	}
+}
+
+/* ARGSUSED */
+static void
 iwh_amrr_ratectl(void *arg, ieee80211_node_t *in)
 {
 	iwh_amrr_t *amrr = (iwh_amrr_t *)in;
@@ -4652,20 +5058,22 @@
 	if (is_success(amrr) && is_enough(amrr)) {
 		amrr->success++;
 		if (amrr->success >= amrr->success_threshold &&
-		    !is_max_rate(in)) {
+		    !iwh_is_max_rate(in)) {
 			amrr->recovery = 1;
 			amrr->success = 0;
-			increase_rate(in);
-			IWH_DBG((IWH_DEBUG_RATECTL,
-			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)\n",
-			    in->in_txrate, amrr->txcnt, amrr->retrycnt));
+			iwh_increase_rate(in);
+			IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_ratectl(): "
+			    "AMRR increasing rate %d "
+			    "(txcnt=%d retrycnt=%d), mcs_idx=%d\n",
+			    in->in_txrate, amrr->txcnt,
+			    amrr->retrycnt, amrr->ht_mcs_idx));
 			need_change = 1;
 		} else {
 			amrr->recovery = 0;
 		}
-	} else if (is_failure(amrr)) {
+	} else if (not_very_few(amrr) && is_failure(amrr)) {
 		amrr->success = 0;
-		if (!is_min_rate(in)) {
+		if (!iwh_is_min_rate(in)) {
 			if (amrr->recovery) {
 				amrr->success_threshold++;
 				if (amrr->success_threshold >
@@ -4677,10 +5085,12 @@
 				amrr->success_threshold =
 				    IWH_AMRR_MIN_SUCCESS_THRESHOLD;
 			}
-			decrease_rate(in);
-			IWH_DBG((IWH_DEBUG_RATECTL,
-			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)\n",
-			    in->in_txrate, amrr->txcnt, amrr->retrycnt));
+			iwh_decrease_rate(in);
+			IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_ratectl(): "
+			    "AMRR decreasing rate %d "
+			    "(txcnt=%d retrycnt=%d), mcs_idx=%d\n",
+			    in->in_txrate, amrr->txcnt,
+			    amrr->retrycnt, amrr->ht_mcs_idx));
 			need_change = 1;
 		}
 		amrr->recovery = 0;	/* paper is incorrect */
@@ -4788,7 +5198,7 @@
 	uint32_t	i;
 	iwh_wimax_coex_cmd_t	w_cmd;
 	iwh_calibration_crystal_cmd_t	c_cmd;
-	uint32_t	rv;
+	uint32_t	rv = IWH_FAIL;
 
 	/*
 	 * initialize SCD related registers to make TX work.
@@ -4980,7 +5390,7 @@
 iwh_tx_power_table(iwh_sc_t *sc, int async)
 {
 	iwh_tx_power_table_cmd_t txpower;
-	int i, err;
+	int i, err = IWH_FAIL;
 
 	(void) memset(&txpower, 0, sizeof (txpower));
 
@@ -5015,7 +5425,7 @@
 		return (err);
 	}
 
-	return (IWH_SUCCESS);
+	return (err);
 }
 
 static void
@@ -5042,7 +5452,7 @@
 }
 
 /*
- * a section of intialization
+ * common section of intialization
  */
 static int
 iwh_init_common(iwh_sc_t *sc)
@@ -5076,7 +5486,7 @@
 	IWH_WRITE(sc, FH_MEM_RCSR_CHNL0_CONFIG_REG,
 	    FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
 	    FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
-	    IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
+	    IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K |
 	    (RX_QUEUE_SIZE_LOG <<
 	    FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT));
 	iwh_mac_access_exit(sc);
@@ -5132,7 +5542,7 @@
 iwh_fast_recover(iwh_sc_t *sc)
 {
 	ieee80211com_t *ic = &sc->sc_ic;
-	int err;
+	int err = IWH_FAIL;
 
 	mutex_enter(&sc->sc_glock);
 
@@ -5143,7 +5553,7 @@
 	sc->sc_config.assoc_id = 0;
 	sc->sc_config.filter_flags &= ~LE_32(RXON_FILTER_ASSOC_MSK);
 
-	if ((err = iwh_hw_set_before_auth(sc)) != 0) {
+	if ((err = iwh_hw_set_before_auth(sc)) != IWH_SUCCESS) {
 		cmn_err(CE_WARN, "iwh_fast_recover(): "
 		    "could not setup authentication\n");
 		mutex_exit(&sc->sc_glock);
@@ -5169,7 +5579,8 @@
 	sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
 
 	/* start queue */
-	IWH_DBG((IWH_DEBUG_FW, "iwh_fast_recover(): resume xmit\n"));
+	IWH_DBG((IWH_DEBUG_FW, "iwh_fast_recover(): "
+	    "resume xmit\n"));
 	mac_tx_update(ic->ic_mach);
 
 	return (IWH_SUCCESS);
@@ -5180,17 +5591,12 @@
 {
 	struct ieee80211com *ic = &sc->sc_ic;
 	ieee80211_node_t *in = ic->ic_bss;
-	int err = IWH_SUCCESS;
+	uint32_t ht_protec = (uint32_t)(-1);
+	int err = IWH_FAIL;
 
 	/*
 	 * update adapter's configuration
 	 */
-	if (sc->sc_assoc_id != in->in_associd) {
-		cmn_err(CE_WARN,
-		    "associate ID mismatch: expected %d, "
-		    "got %d\n",
-		    in->in_associd, sc->sc_assoc_id);
-	}
 	sc->sc_config.assoc_id = in->in_associd & 0x3fff;
 
 	/*
@@ -5211,6 +5617,25 @@
 		    LE_32(RXON_FLG_SHORT_PREAMBLE_MSK);
 	}
 
+	if (in->in_flags & IEEE80211_NODE_HT) {
+		ht_protec = in->in_htopmode;
+		if (ht_protec > 3) {
+			cmn_err(CE_WARN, "iwh_run_state_config(): "
+			    "HT protection mode is not correct.\n");
+			return (IWH_FAIL);
+		} else if (NO_HT_PROT == ht_protec) {
+			ht_protec = sc->sc_ht_conf.ht_protection;
+		}
+
+		sc->sc_config.flags |=
+		    LE_32(ht_protec << RXON_FLG_HT_OPERATING_MODE_POS);
+	}
+
+	/*
+	 * set RX chains/antennas.
+	 */
+	iwh_config_rxon_chain(sc);
+
 	sc->sc_config.filter_flags |=
 	    LE_32(RXON_FILTER_ASSOC_MSK);
 
@@ -5219,7 +5644,8 @@
 		    LE_32(RXON_FILTER_BCON_AWARE_MSK);
 	}
 
-	IWH_DBG((IWH_DEBUG_80211, "config chan %d flags %x"
+	IWH_DBG((IWH_DEBUG_80211, "iwh_run_state_config(): "
+	    "config chan %d flags %x"
 	    " filter_flags %x\n",
 	    sc->sc_config.chan, sc->sc_config.flags,
 	    sc->sc_config.filter_flags));
@@ -5237,10 +5663,716 @@
 	 */
 	err = iwh_tx_power_table(sc, 1);
 	if (err != IWH_SUCCESS) {
-		cmn_err(CE_WARN, "iwh_run_state_config(): "
-		    "failed to set tx power table.\n");
+		return (err);
+	}
+
+	/*
+	 * Not need to update retry rate table for AP node
+	 */
+	err = iwh_qosparam_to_hw(sc, 1);
+	if (err != IWH_SUCCESS) {
 		return (err);
 	}
 
+	return (err);
+}
+
+/*
+ * This function is only for compatibility with Net80211 module.
+ * iwh_qosparam_to_hw() is the actual function updating EDCA
+ * parameters to hardware.
+ */
+/* ARGSUSED */
+static int
+iwh_wme_update(ieee80211com_t *ic)
+{
+	return (0);
+}
+
+static int
+iwh_wme_to_qos_ac(int wme_ac)
+{
+	int qos_ac = QOS_AC_INVALID;
+
+	if (wme_ac < WME_AC_BE || wme_ac > WME_AC_VO) {
+		cmn_err(CE_WARN, "iwh_wme_to_qos_ac(): "
+		    "WME AC index is not in suitable range.\n");
+		return (qos_ac);
+	}
+
+	switch (wme_ac) {
+	case WME_AC_BE:
+		qos_ac = QOS_AC_BK;
+		break;
+	case WME_AC_BK:
+		qos_ac = QOS_AC_BE;
+		break;
+	case WME_AC_VI:
+		qos_ac = QOS_AC_VI;
+		break;
+	case WME_AC_VO:
+		qos_ac = QOS_AC_VO;
+		break;
+	}
+
+	return (qos_ac);
+}
+
+static uint16_t
+iwh_cw_e_to_cw(uint8_t cw_e)
+{
+	uint16_t cw = 1;
+
+	while (cw_e > 0) {
+		cw <<= 1;
+		cw_e--;
+	}
+
+	cw -= 1;
+	return (cw);
+}
+
+static int
+iwh_wmeparam_check(struct wmeParams *wmeparam)
+{
+	int i;
+
+	for (i = 0; i < WME_NUM_AC; i++) {
+
+		if ((wmeparam[i].wmep_logcwmax > QOS_CW_RANGE_MAX) ||
+		    (wmeparam[i].wmep_logcwmin >= wmeparam[i].wmep_logcwmax)) {
+			cmn_err(CE_WARN, "iwh_wmeparam_check(): "
+			    "Contention window is not in suitable range.\n");
+			return (IWH_FAIL);
+		}
+
+		if ((wmeparam[i].wmep_aifsn < QOS_AIFSN_MIN) ||
+		    (wmeparam[i].wmep_aifsn > QOS_AIFSN_MAX)) {
+			cmn_err(CE_WARN, "iwh_wmeparam_check(): "
+			    "Arbitration interframe space number"
+			    "is not in suitable range.\n");
+			return (IWH_FAIL);
+		}
+	}
+
 	return (IWH_SUCCESS);
 }
+
+/*
+ * This function updates EDCA parameters into hardware.
+ * FIFO0-background, FIFO1-best effort, FIFO2-viedo, FIFO3-voice.
+ */
+static int
+iwh_qosparam_to_hw(iwh_sc_t *sc, int async)
+{
+	ieee80211com_t *ic = &sc->sc_ic;
+	ieee80211_node_t *in = ic->ic_bss;
+	struct wmeParams *wmeparam;
+	iwh_qos_param_cmd_t qosparam_cmd;
+	int i, j;
+	int err = IWH_FAIL;
+
+	if ((in->in_flags & IEEE80211_NODE_QOS) &&
+	    (IEEE80211_M_STA == ic->ic_opmode)) {
+		wmeparam = ic->ic_wme.wme_chanParams.cap_wmeParams;
+	} else {
+		return (IWH_SUCCESS);
+	}
+
+	(void) memset(&qosparam_cmd, 0, sizeof (qosparam_cmd));
+
+	err = iwh_wmeparam_check(wmeparam);
+	if (err != IWH_SUCCESS) {
+		return (err);
+	}
+
+	if (in->in_flags & IEEE80211_NODE_QOS) {
+		qosparam_cmd.flags |= QOS_PARAM_FLG_UPDATE_EDCA;
+	}
+
+	if (in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT)) {
+		qosparam_cmd.flags |= QOS_PARAM_FLG_TGN;
+	}
+
+	for (i = 0; i < WME_NUM_AC; i++) {
+
+		j = iwh_wme_to_qos_ac(i);
+		if (j < QOS_AC_BK || j > QOS_AC_VO) {
+			return (IWH_FAIL);
+		}
+
+		qosparam_cmd.ac[j].cw_min =
+		    iwh_cw_e_to_cw(wmeparam[i].wmep_logcwmin);
+		qosparam_cmd.ac[j].cw_max =
+		    iwh_cw_e_to_cw(wmeparam[i].wmep_logcwmax);
+		qosparam_cmd.ac[j].aifsn =
+		    wmeparam[i].wmep_aifsn;
+		qosparam_cmd.ac[j].txop =
+		    (uint16_t)(wmeparam[i].wmep_txopLimit * 32);
+	}
+
+	err = iwh_cmd(sc, REPLY_QOS_PARAM, &qosparam_cmd,
+	    sizeof (qosparam_cmd), async);
+	if (err != IWH_SUCCESS) {
+		cmn_err(CE_WARN, "iwh_qosparam_to_hw(): "
+		    "failed to update QoS parameters into hardware.\n");
+		return (err);
+	}
+
+#ifdef	DEBUG
+	IWH_DBG((IWH_DEBUG_QOS, "iwh_qosparam_to_hw(): "
+	    "EDCA parameters are as follows:\n"));
+
+	IWH_DBG((IWH_DEBUG_QOS, "BK parameters are: "
+	    "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+	    qosparam_cmd.ac[0].cw_min, qosparam_cmd.ac[0].cw_max,
+	    qosparam_cmd.ac[0].aifsn, qosparam_cmd.ac[0].txop));
+
+	IWH_DBG((IWH_DEBUG_QOS, "BE parameters are: "
+	    "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+	    qosparam_cmd.ac[1].cw_min, qosparam_cmd.ac[1].cw_max,
+	    qosparam_cmd.ac[1].aifsn, qosparam_cmd.ac[1].txop));
+
+	IWH_DBG((IWH_DEBUG_QOS, "VI parameters are: "
+	    "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+	    qosparam_cmd.ac[2].cw_min, qosparam_cmd.ac[2].cw_max,
+	    qosparam_cmd.ac[2].aifsn, qosparam_cmd.ac[2].txop));
+
+	IWH_DBG((IWH_DEBUG_QOS, "VO parameters are: "
+	    "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+	    qosparam_cmd.ac[3].cw_min, qosparam_cmd.ac[3].cw_max,
+	    qosparam_cmd.ac[3].aifsn, qosparam_cmd.ac[3].txop));
+#endif
+	return (err);
+}
+
+static inline int
+iwh_wme_tid_qos_ac(int tid)
+{
+	switch (tid) {
+	case 1:
+	case 2:
+		return (QOS_AC_BK);
+	case 0:
+	case 3:
+		return (QOS_AC_BE);
+	case 4:
+	case 5:
+		return (QOS_AC_VI);
+	case 6:
+	case 7:
+		return (QOS_AC_VO);
+	}
+
+	return (QOS_AC_BE);
+}
+
+static inline int
+iwh_qos_ac_to_txq(int qos_ac)
+{
+	switch (qos_ac) {
+	case QOS_AC_BK:
+		return (QOS_AC_BK_TO_TXQ);
+	case QOS_AC_BE:
+		return (QOS_AC_BE_TO_TXQ);
+	case QOS_AC_VI:
+		return (QOS_AC_VI_TO_TXQ);
+	case QOS_AC_VO:
+		return (QOS_AC_VO_TO_TXQ);
+	}
+
+	return (QOS_AC_BE_TO_TXQ);
+}
+
+static int
+iwh_wme_tid_to_txq(int tid)
+{
+	int queue_n = TXQ_FOR_AC_INVALID;
+	int qos_ac;
+
+	if (tid < WME_TID_MIN ||
+	    tid > WME_TID_MAX) {
+		cmn_err(CE_WARN, "wme_tid_to_txq(): "
+		    "TID is not in suitable range.\n");
+		return (queue_n);
+	}
+
+	qos_ac = iwh_wme_tid_qos_ac(tid);
+	queue_n = iwh_qos_ac_to_txq(qos_ac);
+
+	return (queue_n);
+}
+
+/*
+ * This function is used for intializing HT relevant configurations.
+ */
+static void
+iwh_init_ht_conf(iwh_sc_t *sc)
+{
+	(void) memset(&sc->sc_ht_conf, 0, sizeof (iwh_ht_conf_t));
+
+	if ((0x4235 == sc->sc_dev_id) ||
+	    (0x4236 == sc->sc_dev_id) ||
+	    (0x423a == sc->sc_dev_id)) {
+		sc->sc_ht_conf.ht_support = 1;
+
+		sc->sc_ht_conf.valid_chains = 3;
+		sc->sc_ht_conf.tx_stream_count = 2;
+		sc->sc_ht_conf.rx_stream_count = 2;
+
+		sc->sc_ht_conf.tx_support_mcs[0] = 0xff;
+		sc->sc_ht_conf.tx_support_mcs[1] = 0xff;
+		sc->sc_ht_conf.rx_support_mcs[0] = 0xff;
+		sc->sc_ht_conf.rx_support_mcs[1] = 0xff;
+	} else {
+		sc->sc_ht_conf.ht_support = 1;
+
+		sc->sc_ht_conf.valid_chains = 2;
+		sc->sc_ht_conf.tx_stream_count = 1;
+		sc->sc_ht_conf.rx_stream_count = 2;
+
+		sc->sc_ht_conf.tx_support_mcs[0] = 0xff;
+		sc->sc_ht_conf.rx_support_mcs[0] = 0xff;
+		sc->sc_ht_conf.rx_support_mcs[1] = 0xff;
+	}
+
+	if (sc->sc_ht_conf.ht_support) {
+		sc->sc_ht_conf.cap |= HT_CAP_GRN_FLD;
+		sc->sc_ht_conf.cap |= HT_CAP_SGI_20;
+		sc->sc_ht_conf.cap |= HT_CAP_MAX_AMSDU;
+		/* should disable MIMO */
+		sc->sc_ht_conf.cap |= HT_CAP_MIMO_PS;
+
+		sc->sc_ht_conf.ampdu_p.factor = HT_RX_AMPDU_FACTOR;
+		sc->sc_ht_conf.ampdu_p.density = HT_MPDU_DENSITY;
+
+		sc->sc_ht_conf.ht_protection = HT_PROT_CHAN_NON_HT;
+	}
+}
+
+/*
+ * This function overwrites default ieee80211_rateset_11n struc.
+ */
+static void
+iwh_overwrite_11n_rateset(iwh_sc_t *sc)
+{
+	uint8_t *ht_rs = sc->sc_ht_conf.rx_support_mcs;
+	int mcs_idx, mcs_count = 0;
+	int i, j;
+
+	for (i = 0; i < HT_RATESET_NUM; i++) {
+		for (j = 0; j < 8; j++) {
+			if (ht_rs[i] & (1 << j)) {
+				mcs_idx = i * 8 + j;
+				if (mcs_idx >= IEEE80211_HTRATE_MAXSIZE) {
+					break;
+				}
+
+				ieee80211_rateset_11n.rs_rates[mcs_idx] =
+				    (uint8_t)mcs_idx;
+				mcs_count++;
+			}
+		}
+	}
+
+	ieee80211_rateset_11n.rs_nrates = (uint8_t)mcs_count;
+
+#ifdef	DEBUG
+	IWH_DBG((IWH_DEBUG_HTRATE, "iwh_overwrite_11n_rateset(): "
+	    "HT rates supported by this station is as follows:\n"));
+
+	for (i = 0; i < ieee80211_rateset_11n.rs_nrates; i++) {
+		IWH_DBG((IWH_DEBUG_HTRATE, "Rate %d is %d\n",
+		    i, ieee80211_rateset_11n.rs_rates[i]));
+	}
+#endif
+}
+
+/*
+ * This function overwrites default configurations of
+ * ieee80211com structure in Net80211 module.
+ */
+static void
+iwh_overwrite_ic_default(iwh_sc_t *sc)
+{
+	ieee80211com_t *ic = &sc->sc_ic;
+
+	sc->sc_newstate = ic->ic_newstate;
+	ic->ic_newstate = iwh_newstate;
+	ic->ic_node_alloc = iwh_node_alloc;
+	ic->ic_node_free = iwh_node_free;
+
+	if (sc->sc_ht_conf.ht_support) {
+		sc->sc_recv_action = ic->ic_recv_action;
+		ic->ic_recv_action = iwh_recv_action;
+		sc->sc_send_action = ic->ic_send_action;
+		ic->ic_send_action = iwh_send_action;
+
+		ic->ic_ampdu_rxmax = sc->sc_ht_conf.ampdu_p.factor;
+		ic->ic_ampdu_density = sc->sc_ht_conf.ampdu_p.density;
+		ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
+	}
+}
+
+/*
+ * This function sets "RX chain selection" feild
+ * in RXON command during plumb driver.
+ */
+static void
+iwh_config_rxon_chain(iwh_sc_t *sc)
+{
+	ieee80211com_t *ic = &sc->sc_ic;
+	ieee80211_node_t *in = ic->ic_bss;
+
+	if (3 == sc->sc_ht_conf.valid_chains) {
+		sc->sc_config.rx_chain = LE_16((RXON_RX_CHAIN_A_MSK |
+		    RXON_RX_CHAIN_B_MSK | RXON_RX_CHAIN_C_MSK) <<
+		    RXON_RX_CHAIN_VALID_POS);
+
+		sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+		    RXON_RX_CHAIN_B_MSK | RXON_RX_CHAIN_C_MSK) <<
+		    RXON_RX_CHAIN_FORCE_SEL_POS);
+
+		sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+		    RXON_RX_CHAIN_B_MSK | RXON_RX_CHAIN_C_MSK) <<
+		    RXON_RX_CHAIN_FORCE_MIMO_SEL_POS);
+	} else {
+		sc->sc_config.rx_chain = LE_16((RXON_RX_CHAIN_A_MSK |
+		    RXON_RX_CHAIN_B_MSK) << RXON_RX_CHAIN_VALID_POS);
+
+		sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+		    RXON_RX_CHAIN_B_MSK) << RXON_RX_CHAIN_FORCE_SEL_POS);
+
+		sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+		    RXON_RX_CHAIN_B_MSK) <<
+		    RXON_RX_CHAIN_FORCE_MIMO_SEL_POS);
+	}
+
+	sc->sc_config.rx_chain |= LE_16(RXON_RX_CHAIN_DRIVER_FORCE_MSK);
+
+	if ((in != NULL) &&
+	    (in->in_flags & IEEE80211_NODE_HT) &&
+	    sc->sc_ht_conf.ht_support) {
+		if (3 == sc->sc_ht_conf.valid_chains) {
+			sc->sc_config.rx_chain |= LE_16(3 <<
+			    RXON_RX_CHAIN_CNT_POS);
+			sc->sc_config.rx_chain |= LE_16(3 <<
+			    RXON_RX_CHAIN_MIMO_CNT_POS);
+		} else {
+			sc->sc_config.rx_chain |= LE_16(2 <<
+			    RXON_RX_CHAIN_CNT_POS);
+			sc->sc_config.rx_chain |= LE_16(2 <<
+			    RXON_RX_CHAIN_MIMO_CNT_POS);
+		}
+
+		sc->sc_config.rx_chain |= LE_16(1 <<
+		    RXON_RX_CHAIN_MIMO_FORCE_POS);
+	}
+
+	IWH_DBG((IWH_DEBUG_RXON, "iwh_config_rxon_chain(): "
+	    "rxon->rx_chain = %x\n", sc->sc_config.rx_chain));
+}
+
+/*
+ * This function adds AP station into hardware.
+ */
+static int
+iwh_add_ap_sta(iwh_sc_t *sc)
+{
+	ieee80211com_t *ic = &sc->sc_ic;
+	ieee80211_node_t *in = ic->ic_bss;
+	iwh_add_sta_t node;
+	uint32_t ampdu_factor, ampdu_density;
+	int err = IWH_FAIL;
+
+	/*
+	 * Add AP node into hardware.
+	 */
+	(void) memset(&node, 0, sizeof (node));
+	IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+	node.mode = STA_MODE_ADD_MSK;
+	node.sta.sta_id = IWH_AP_ID;
+
+	if (sc->sc_ht_conf.ht_support &&
+	    (in->in_htcap_ie != NULL) &&
+	    (in->in_htcap != 0) &&
+	    (in->in_htparam != 0)) {
+
+		if (((in->in_htcap & HT_CAP_MIMO_PS) >> 2)
+		    == HT_CAP_MIMO_PS_DYNAMIC) {
+			node.station_flags |= LE_32(STA_FLG_RTS_MIMO_PROT);
+		}
+
+		ampdu_factor = in->in_htparam & HT_RX_AMPDU_FACTOR_MSK;
+		node.station_flags |=
+		    LE_32(ampdu_factor << STA_FLG_MAX_AMPDU_POS);
+
+		ampdu_density = (in->in_htparam & HT_MPDU_DENSITY_MSK) >>
+		    HT_MPDU_DENSITY_POS;
+		node.station_flags |=
+		    LE_32(ampdu_density << STA_FLG_AMPDU_DENSITY_POS);
+
+		if (in->in_htcap & LE_16(HT_CAP_SUP_WIDTH)) {
+			node.station_flags |=
+			    LE_32(STA_FLG_FAT_EN);
+		}
+	}
+
+	err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+	if (err != IWH_SUCCESS) {
+		cmn_err(CE_WARN, "iwh_add_ap_lq(): "
+		    "failed to add AP node\n");
+		return (err);
+	}
+
+	return (err);
+}
+
+/*
+ * Each station in the Shirley Peak's internal station table has
+ * its own table of 16 TX rates and modulation modes for retrying
+ * TX when an ACK is not received. This function replaces the entire
+ * table for one station.Station must already be in Shirley Peak's
+ * station talbe.
+ */
+static int
+iwh_ap_lq(iwh_sc_t *sc)
+{
+	ieee80211com_t *ic = &sc->sc_ic;
+	ieee80211_node_t *in = ic->ic_bss;
+	iwh_link_quality_cmd_t link_quality;
+	const struct ieee80211_rateset *rs_sup = NULL;
+	uint32_t masks = 0, rate;
+	int i, err = IWH_FAIL;
+
+	/*
+	 * TX_LINK_QUALITY cmd
+	 */
+	(void) memset(&link_quality, 0, sizeof (link_quality));
+	if (in->in_chan == IEEE80211_CHAN_ANYC)	/* skip null node */
+		return (err);
+	rs_sup = ieee80211_get_suprates(ic, in->in_chan);
+
+	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+		if (i < rs_sup->ir_nrates) {
+			rate = rs_sup->ir_rates[rs_sup->ir_nrates - i] &
+			    IEEE80211_RATE_VAL;
+		} else {
+			rate = 2;
+		}
+
+		if (2 == rate || 4 == rate ||
+		    11 == rate || 22 == rate) {
+			masks |= LE_32(RATE_MCS_CCK_MSK);
+		}
+
+		masks |= LE_32(RATE_MCS_ANT_B_MSK);
+
+		link_quality.rate_n_flags[i] =
+		    LE_32(iwh_rate_to_plcp(rate) | masks);
+	}
+
+	link_quality.general_params.single_stream_ant_msk = LINK_QUAL_ANT_B_MSK;
+	link_quality.general_params.dual_stream_ant_msk = LINK_QUAL_ANT_MSK;
+	link_quality.agg_params.agg_dis_start_th = 3;
+	link_quality.agg_params.agg_time_limit = LE_16(4000);
+	link_quality.sta_id = IWH_AP_ID;
+	err = iwh_cmd(sc, REPLY_TX_LINK_QUALITY_CMD, &link_quality,
+	    sizeof (link_quality), 1);
+	if (err != IWH_SUCCESS) {
+		cmn_err(CE_WARN, "iwh_ap_lq(): "
+		    "failed to config link quality table\n");
+		return (err);
+	}
+
+#ifdef	DEBUG
+	IWH_DBG((IWH_DEBUG_HWRATE, "iwh_ap_lq(): "
+	    "Rates in HW are as follows:\n"));
+
+	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+		IWH_DBG((IWH_DEBUG_HWRATE,
+		    "Rate %d in HW is %x\n", i, link_quality.rate_n_flags[i]));
+	}
+#endif
+
+	return (err);
+}
+
+/*
+ * When block ACK agreement has been set up between station and AP,
+ * Net80211 module will call this function to inform hardware about
+ * informations of this BA agreement.
+ * When AP wants to delete BA agreement that was originated by it,
+ * Net80211 modele will call this function to clean up relevant
+ * information in hardware.
+ */
+static void
+iwh_recv_action(struct ieee80211_node *in,
+    const uint8_t *frm, const uint8_t *efrm)
+{
+	struct ieee80211com *ic;
+	iwh_sc_t *sc;
+	const struct ieee80211_action *ia;
+	uint16_t baparamset, baseqctl;
+	uint32_t tid, ssn;
+	iwh_add_sta_t node;
+	int err = IWH_FAIL;
+
+	if ((NULL == in) || (NULL == frm)) {
+		return;
+	}
+
+	ic = in->in_ic;
+	if (NULL == ic) {
+		return;
+	}
+
+	sc = (iwh_sc_t *)ic;
+
+	sc->sc_recv_action(in, frm, efrm);
+
+	ia = (const struct ieee80211_action *)frm;
+	if (ia->ia_category != IEEE80211_ACTION_CAT_BA) {
+		return;
+	}
+
+	switch (ia->ia_action) {
+	case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+		baparamset = *(uint16_t *)(frm + 3);
+		baseqctl = *(uint16_t *)(frm + 7);
+
+		tid = MS(baparamset, IEEE80211_BAPS_TID);
+		ssn = MS(baseqctl, IEEE80211_BASEQ_START);
+
+		(void) memset(&node, 0, sizeof (node));
+		IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+		node.mode = STA_MODE_MODIFY_MSK;
+		node.sta.sta_id = IWH_AP_ID;
+
+		node.station_flags_msk = 0;
+		node.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
+		node.add_immediate_ba_tid = (uint8_t)tid;
+		node.add_immediate_ba_ssn = LE_16(ssn);
+
+		mutex_enter(&sc->sc_glock);
+		err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+		if (err != IWH_SUCCESS) {
+			cmn_err(CE_WARN, "iwh_recv_action(): "
+			    "failed to setup RX block ACK\n");
+			mutex_exit(&sc->sc_glock);
+			return;
+		}
+		mutex_exit(&sc->sc_glock);
+
+		IWH_DBG((IWH_DEBUG_BA, "iwh_recv_action(): "
+		    "RX block ACK "
+		    "was setup on TID %d and SSN is %d.\n", tid, ssn));
+
+		return;
+
+	case IEEE80211_ACTION_BA_DELBA:
+		baparamset = *(uint16_t *)(frm + 2);
+
+		if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
+			return;
+		}
+
+		tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+
+		(void) memset(&node, 0, sizeof (node));
+		IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+		node.mode = STA_MODE_MODIFY_MSK;
+		node.sta.sta_id = IWH_AP_ID;
+
+		node.station_flags_msk = 0;
+		node.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+		node.add_immediate_ba_tid = (uint8_t)tid;
+
+		mutex_enter(&sc->sc_glock);
+		err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+		if (err != IWH_SUCCESS) {
+			cmn_err(CE_WARN, "iwh_recv_action(): "
+			    "failed to delete RX block ACK\n");
+			mutex_exit(&sc->sc_glock);
+			return;
+		}
+		mutex_exit(&sc->sc_glock);
+
+		IWH_DBG((IWH_DEBUG_BA, "iwh_recv_action(): "
+		    "RX block ACK "
+		    "was deleted on TID %d.\n", tid));
+
+		return;
+	}
+}
+
+/*
+ * When local station wants to delete BA agreement that was originated by AP,
+ * Net80211 module will call this function to clean up relevant information
+ * in hardware.
+ */
+static int
+iwh_send_action(struct ieee80211_node *in,
+    int category, int action, uint16_t args[4])
+{
+	struct ieee80211com *ic;
+	iwh_sc_t *sc;
+	uint32_t tid;
+	iwh_add_sta_t node;
+	int ret = EIO;
+	int err = IWH_FAIL;
+
+
+	if (NULL == in) {
+		return (ret);
+	}
+
+	ic = in->in_ic;
+	if (NULL == ic) {
+		return (ret);
+	}
+
+	sc = (iwh_sc_t *)ic;
+
+	ret = sc->sc_send_action(in, category, action, args);
+
+	if (category != IEEE80211_ACTION_CAT_BA) {
+		return (ret);
+	}
+
+	switch (action) {
+	case IEEE80211_ACTION_BA_DELBA:
+		if (IEEE80211_DELBAPS_INIT == args[1]) {
+			return (ret);
+		}
+
+		tid = args[0];
+
+		(void) memset(&node, 0, sizeof (node));
+		IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+		node.mode = STA_MODE_MODIFY_MSK;
+		node.sta.sta_id = IWH_AP_ID;
+
+		node.station_flags_msk = 0;
+		node.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+		node.add_immediate_ba_tid = (uint8_t)tid;
+
+		mutex_enter(&sc->sc_glock);
+		err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+		if (err != IWH_SUCCESS) {
+			cmn_err(CE_WARN, "iwh_send_action(): "
+			    "failed to delete RX balock ACK\n");
+			mutex_exit(&sc->sc_glock);
+			return (EIO);
+		}
+		mutex_exit(&sc->sc_glock);
+
+		IWH_DBG((IWH_DEBUG_BA, "iwh_send_action(): "
+		    "RX block ACK "
+		    "was deleted on TID %d.\n", tid));
+
+		break;
+	}
+
+	return (ret);
+}
--- a/usr/src/uts/common/io/iwh/iwh_hw.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/iwh/iwh_hw.h	Thu Aug 06 13:05:38 2009 +0800
@@ -1,10 +1,10 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
- * Copyright (c) 2008, Intel Corporation
+ * Copyright (c) 2009, Intel Corporation
  * All rights reserved.
  */
 
@@ -18,7 +18,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU Geeral Public License as
@@ -43,7 +43,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -621,6 +621,12 @@
 #define	FH_RCSR_RX_CONFIG_RDRBD_ENABLE_VAL	(0x20000000)
 
 #define	IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K	(0x00000000)
+#define	IWH_TX_RTS_RETRY_LIMIT		(60)
+#define	IWH_TX_DATA_RETRY_LIMIT		(15)
+
+#define	IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K	(0x00010000)
+#define	IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K	(0x00020000)
+#define	IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K	(0x00030000)
 
 /*
  * RCSR channel 0 config register values
@@ -1248,110 +1254,146 @@
 /*
  * QoS  definitions
  */
-#define	CW_MIN_OFDM	15
-#define	CW_MAX_OFDM	1023
-#define	CW_MIN_CCK	31
-#define	CW_MAX_CCK	1023
+
+#define	AC_NUM		(4)	/* the number of access category */
 
-#define	QOS_TX0_CW_MIN_OFDM	CW_MIN_OFDM
-#define	QOS_TX1_CW_MIN_OFDM	CW_MIN_OFDM
-#define	QOS_TX2_CW_MIN_OFDM	((CW_MIN_OFDM + 1) / 2 - 1)
-#define	QOS_TX3_CW_MIN_OFDM	((CW_MIN_OFDM + 1) / 4 - 1)
+/*
+ * index of every AC in firmware
+ */
+#define	QOS_AC_BK	(0)
+#define	QOS_AC_BE	(1)
+#define	QOS_AC_VI	(2)
+#define	QOS_AC_VO	(3)
+#define	QOS_AC_INVALID	(-1)
 
-#define	QOS_TX0_CW_MIN_CCK	CW_MIN_CCK
-#define	QOS_TX1_CW_MIN_CCK	CW_MIN_CCK
-#define	QOS_TX2_CW_MIN_CCK	((CW_MIN_CCK + 1) / 2 - 1)
-#define	QOS_TX3_CW_MIN_CCK	((CW_MIN_CCK + 1) / 4 - 1)
+#define	QOS_CW_RANGE_MIN	(0)	/* exponential of 2 */
+#define	QOS_CW_RANGE_MAX	(15)	/* exponential of 2 */
+#define	QOS_TXOP_MIN		(0)	/* unit of 32 microsecond */
+#define	QOS_TXOP_MAX		(255)	/* unit of 32 microsecond */
+#define	QOS_AIFSN_MIN		(2)
+#define	QOS_AIFSN_MAX		(15)	/* undefined */
 
-#define	QOS_TX0_CW_MAX_OFDM	CW_MAX_OFDM
-#define	QOS_TX1_CW_MAX_OFDM	CW_MAX_OFDM
-#define	QOS_TX2_CW_MAX_OFDM	CW_MIN_OFDM
-#define	QOS_TX3_CW_MAX_OFDM	((CW_MIN_OFDM + 1) / 2 - 1)
-
-#define	QOS_TX0_CW_MAX_CCK	CW_MAX_CCK
-#define	QOS_TX1_CW_MAX_CCK	CW_MAX_CCK
-#define	QOS_TX2_CW_MAX_CCK	CW_MIN_CCK
-#define	QOS_TX3_CW_MAX_CCK	((CW_MIN_CCK + 1) / 2 - 1)
+/*
+ * masks for flags of QoS parameter command
+ */
+#define	QOS_PARAM_FLG_UPDATE_EDCA	(0x01)
+#define	QOS_PARAM_FLG_TGN		(0x02)
 
-#define	QOS_TX0_AIFS	(3)
-#define	QOS_TX1_AIFS	(7)
-#define	QOS_TX2_AIFS	(2)
-#define	QOS_TX3_AIFS	(2)
+/*
+ * index of TX queue for every AC
+ */
+#define	QOS_AC_BK_TO_TXQ	(3)
+#define	QOS_AC_BE_TO_TXQ	(2)
+#define	QOS_AC_VI_TO_TXQ	(1)
+#define	QOS_AC_VO_TO_TXQ	(0)
+#define	TXQ_FOR_AC_MIN		(0)
+#define	TXQ_FOR_AC_MAX		(3)
+#define	TXQ_FOR_AC_INVALID	(-1)
+#define	NON_QOS_TXQ		QOS_AC_BE_TO_TXQ
+#define	QOS_TXQ_FOR_MGT		QOS_AC_VO_TO_TXQ
 
-#define	QOS_TX0_ACM	0
-#define	QOS_TX1_ACM	0
-#define	QOS_TX2_ACM	0
-#define	QOS_TX3_ACM	0
+#define	WME_TID_MIN	(0)
+#define	WME_TID_MAX	(7)
+#define	WME_TID_INVALID	(-1)
 
-#define	QOS_TX0_TXOP_LIMIT_CCK	0
-#define	QOS_TX1_TXOP_LIMIT_CCK	0
-#define	QOS_TX2_TXOP_LIMIT_CCK	6016
-#define	QOS_TX3_TXOP_LIMIT_CCK	3264
+/*
+ * HT definitions
+ */
 
-#define	QOS_TX0_TXOP_LIMIT_OFDM	0
-#define	QOS_TX1_TXOP_LIMIT_OFDM	0
-#define	QOS_TX2_TXOP_LIMIT_OFDM	3008
-#define	QOS_TX3_TXOP_LIMIT_OFDM	1504
+/*
+ * HT capabilities masks
+ */
+#define	HT_CAP_SUP_WIDTH	(0x0002)
+#define	HT_CAP_MIMO_PS		(0x000c)
+#define	HT_CAP_GRN_FLD		(0x0010)
+#define	HT_CAP_SGI_20		(0x0020)
+#define	HT_CAP_SGI_40		(0x0040)
+#define	HT_CAP_DELAY_BA		(0x0400)
+#define	HT_CAP_MAX_AMSDU	(0x0800)
+#define	HT_CAP_MCS_TX_DEFINED	(0x01)
+#define	HT_CAP_MCS_TX_RX_DIFF	(0x02)
+#define	HT_CAP_MCS_TX_STREAMS	(0x0c)
+#define	HT_CAP_MCS_TX_UEQM	(0x10)
 
-#define	DEF_TX0_CW_MIN_OFDM	CW_MIN_OFDM
-#define	DEF_TX1_CW_MIN_OFDM	CW_MIN_OFDM
-#define	DEF_TX2_CW_MIN_OFDM	CW_MIN_OFDM
-#define	DEF_TX3_CW_MIN_OFDM	CW_MIN_OFDM
+#define	HT_CAP_MIMO_PS_STATIC	(0)
+#define	HT_CAP_MIMO_PS_DYNAMIC	(1)
+#define	HT_CAP_MIMO_PS_INVALID	(2)
+#define	HT_CAP_MIMO_PS_NONE	(3)
 
-#define	DEF_TX0_CW_MIN_CCK	CW_MIN_CCK
-#define	DEF_TX1_CW_MIN_CCK	CW_MIN_CCK
-#define	DEF_TX2_CW_MIN_CCK	CW_MIN_CCK
-#define	DEF_TX3_CW_MIN_CCK	CW_MIN_CCK
+#define	HT_RX_AMPDU_FACTOR_8K	(0x0)
+#define	HT_RX_AMPDU_FACTOR_16K	(0x1)
+#define	HT_RX_AMPDU_FACTOR_32K	(0x2)
+#define	HT_RX_AMPDU_FACTOR_64K	(0x3)
+#define	HT_RX_AMPDU_FACTOR	HT_RX_AMPDU_FACTOR_8K
+#define	HT_RX_AMPDU_FACTOR_MSK	(0x3)
 
-#define	DEF_TX0_CW_MAX_OFDM	CW_MAX_OFDM
-#define	DEF_TX1_CW_MAX_OFDM	CW_MAX_OFDM
-#define	DEF_TX2_CW_MAX_OFDM	CW_MAX_OFDM
-#define	DEF_TX3_CW_MAX_OFDM	CW_MAX_OFDM
+#define	HT_MPDU_DENSITY_4USEC	(0x5)
+#define	HT_MPDU_DENSITY_8USEC	(0x6)
+#define	HT_MPDU_DENSITY		HT_MPDU_DENSITY_4USEC
+#define	HT_MPDU_DENSITY_MSK	(0x1c)
+#define	HT_MPDU_DENSITY_POS	(2)
 
-#define	DEF_TX0_CW_MAX_CCK	CW_MAX_CCK
-#define	DEF_TX1_CW_MAX_CCK	CW_MAX_CCK
-#define	DEF_TX2_CW_MAX_CCK	CW_MAX_CCK
-#define	DEF_TX3_CW_MAX_CCK	CW_MAX_CCK
+#define	HT_RATESET_NUM		(16)
+#define	HT_1CHAIN_RATE_MIN_IDX	(0x0)
+#define	HT_1CHAIN_RATE_MAX_IDX	(0x7)
+#define	HT_2CHAIN_RATE_MIN_IDX	(0x8)
+#define	HT_2CHAIN_RATE_MAX_IDX	(0xf)
+
+struct iwh_ampdu_param {
+	uint8_t	factor;
+	uint8_t	density;
+};
 
-#define	DEF_TX0_AIFS		(2)
-#define	DEF_TX1_AIFS		(2)
-#define	DEF_TX2_AIFS		(2)
-#define	DEF_TX3_AIFS		(2)
-
-#define	DEF_TX0_ACM		(0)
-#define	DEF_TX1_ACM		(0)
-#define	DEF_TX2_ACM		(0)
-#define	DEF_TX3_ACM		(0)
+typedef	struct iwh_ht_conf {
+	uint8_t			ht_support;
+	uint16_t		cap;
+	struct iwh_ampdu_param	ampdu_p;
+	uint8_t			tx_support_mcs[HT_RATESET_NUM];
+	uint8_t			rx_support_mcs[HT_RATESET_NUM];
+	uint8_t			valid_chains;
+	uint8_t			tx_stream_count;
+	uint8_t			rx_stream_count;
+	uint8_t			ht_protection;
+} iwh_ht_conf_t;
 
-#define	DEF_TX0_TXOP_LIMIT_CCK	(0)
-#define	DEF_TX1_TXOP_LIMIT_CCK	(0)
-#define	DEF_TX2_TXOP_LIMIT_CCK	(0)
-#define	DEF_TX3_TXOP_LIMIT_CCK	(0)
+#define	NO_HT_PROT		(0)
+#define	HT_PROT_CHAN_NON_HT	(1)
+#define	HT_PROT_FAT		(2)
+#define	HT_PROT_ASSOC_NON_HT	(3)
 
-#define	DEF_TX0_TXOP_LIMIT_OFDM	(0)
-#define	DEF_TX1_TXOP_LIMIT_OFDM	(0)
-#define	DEF_TX2_TXOP_LIMIT_OFDM	(0)
-#define	DEF_TX3_TXOP_LIMIT_OFDM	(0)
+/*
+ * HT flags for RXON command.
+ */
+#define	RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK	0x400000
+#define	RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK	0x000000
+#define	RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK	0x400000
 
-#define	QOS_QOS_SETS		(3)
-#define	QOS_PARAM_SET_ACTIVE	(0)
-#define	QOS_PARAM_SET_DEF_CCK	(1)
-#define	QOS_PARAM_SET_DEF_OFDM	(2)
+#define	RXON_FLG_HT_OPERATING_MODE_POS		(23)
+#define	RXON_FLG_HT_PROT_MSK			0x800000
+#define	RXON_FLG_FAT_PROT_MSK			0x1000000
 
-#define	CTRL_QOS_NO_ACK			(0x0020)
-#define	DCT_FLAG_EXT_QOS_ENABLED	(0x10)
+#define	RXON_FLG_CHANNEL_MODE_POS		(25)
+#define	RXON_FLG_CHANNEL_MODE_MSK		0x06000000
+#define	RXON_FLG_CHANNEL_MODE_LEGACY_MSK	0x00000000
+#define	RXON_FLG_CHANNEL_MODE_PURE_40_MSK	0x02000000
+#define	RXON_FLG_CHANNEL_MODE_MIXED_MSK		0x04000000
 
-#define	IWH_TX_QUEUE_AC0		(0)
-#define	IWH_TX_QUEUE_AC1		(1)
-#define	IWH_TX_QUEUE_AC2		(2)
-#define	IWH_TX_QUEUE_AC3		(3)
-#define	IWH_TX_QUEUE_HCCA_1		(5)
-#define	IWH_TX_QUEUE_HCCA_2    	(6)
-
-#define	U32_PAD(n)	((4-(n%4))%4)
-
-#define	AC_BE_TID_MASK 0x9	/* TID 0 and 3 */
-#define	AC_BK_TID_MASK 0x6	/* TID 1 and 2 */
+#define	RXON_RX_CHAIN_DRIVER_FORCE_MSK		(0x1<<0)
+#define	RXON_RX_CHAIN_VALID_MSK			(0x7<<1)
+#define	RXON_RX_CHAIN_VALID_POS			(1)
+#define	RXON_RX_CHAIN_FORCE_SEL_MSK		(0x7<<4)
+#define	RXON_RX_CHAIN_FORCE_SEL_POS		(4)
+#define	RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK	(0x7<<7)
+#define	RXON_RX_CHAIN_FORCE_MIMO_SEL_POS	(7)
+#define	RXON_RX_CHAIN_CNT_MSK			(0x3<<10)
+#define	RXON_RX_CHAIN_CNT_POS			(10)
+#define	RXON_RX_CHAIN_MIMO_CNT_MSK		(0x3<<12)
+#define	RXON_RX_CHAIN_MIMO_CNT_POS		(12)
+#define	RXON_RX_CHAIN_MIMO_FORCE_MSK		(0x1<<14)
+#define	RXON_RX_CHAIN_MIMO_FORCE_POS		(14)
+#define	RXON_RX_CHAIN_A_MSK			(1)
+#define	RXON_RX_CHAIN_B_MSK			(2)
+#define	RXON_RX_CHAIN_C_MSK			(4)
 
 /*
  * Generic queue structure
@@ -1442,40 +1484,32 @@
 	struct iwh_tx_power_db	db;
 } iwh_tx_power_table_cmd_t;
 
-
-
 /*
- * HT flags
+ * Hardware rate scaling set by iwh_ap_lq function.
+ * Given a particular initial rate and mode, the driver uses the
+ * following formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM]
+ * rate table in the Link Quality command:
+ *
+ * 1) If using High-throughput(HT)(SISO or MIMO) initial rate:
+ *    a) Use this same initial rate for first 3 entries.
+ *    b) Find next lower available rate using same mode(SISO or MIMO),
+ *	 use for next 3 entries. If no lower rate available, switch to
+ *	 legacy mode(no FAT channel, no MIMO, no short guard interval).
+ *    c) If using MIMO, set command's mimo_delimeter to number of
+ *	 entries using MIMO(3 or 6).
+ *    d) After trying 2 HT rates, switch to legacy mode(no FAT channel,
+ *	 no MIMO, no short qguard interval), at the next lower bit rate
+ *	 (e.g. if second HT bit rate was 54, try 48 legacy),and follow
+ *   legacy procedure for remaining table entries.
+ *
+ * 2) If using legacy initial rate:
+ *    a) Use the initial rate for only one entry.
+ *    b) For each following entry, reduce the rate to next lower available
+ *	 rate, until reaching the lowest available rate.
+ *    c) When reducing rate, also switch antenna selection.
+ *    b) Once lowest available rate is reached, repreat this rate until
+ *   rate table is filled(16 entries),switching antenna each entry.
  */
-#define	RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK	0x400000
-#define	RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK	0x000000
-#define	RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK	0x400000
-
-#define	RXON_FLG_HT_OPERATING_MODE_POS		(23)
-#define	RXON_FLG_HT_PROT_MSK			0x800000
-#define	RXON_FLG_FAT_PROT_MSK			0x1000000
-
-#define	RXON_FLG_CHANNEL_MODE_POS		(25)
-#define	RXON_FLG_CHANNEL_MODE_MSK		0x06000000
-#define	RXON_FLG_CHANNEL_MODE_LEGACY_MSK	0x00000000
-#define	RXON_FLG_CHANNEL_MODE_PURE_40_MSK	0x02000000
-#define	RXON_FLG_CHANNEL_MODE_MIXED_MSK		0x04000000
-
-#define	RXON_RX_CHAIN_DRIVER_FORCE_MSK		(0x1<<0)
-#define	RXON_RX_CHAIN_VALID_MSK			(0x7<<1)
-#define	RXON_RX_CHAIN_VALID_POS			(1)
-#define	RXON_RX_CHAIN_FORCE_SEL_MSK		(0x7<<4)
-#define	RXON_RX_CHAIN_FORCE_SEL_POS		(4)
-#define	RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK	(0x7<<7)
-#define	RXON_RX_CHAIN_FORCE_MIMO_SEL_POS	(7)
-#define	RXON_RX_CHAIN_CNT_MSK			(0x3<<10)
-#define	RXON_RX_CHAIN_CNT_POS			(10)
-#define	RXON_RX_CHAIN_MIMO_CNT_MSK		(0x3<<12)
-#define	RXON_RX_CHAIN_MIMO_CNT_POS		(12)
-#define	RXON_RX_CHAIN_MIMO_FORCE_MSK		(0x1<<14)
-#define	RXON_RX_CHAIN_MIMO_FORCE_POS		(14)
-
-#define	MCS_DUP_6M_PLCP (0x20)
 
 /*
  * OFDM HT rate masks
@@ -2204,20 +2238,6 @@
 } iwh_assoc_t;
 
 /*
- * structure for command IWH_CMD_SET_WME
- */
-typedef struct iwh_wme_setup {
-	uint32_t	flags;
-	struct {
-		uint16_t	cwmin;
-		uint16_t	cwmax;
-		uint8_t		aifsn;
-		uint8_t		reserved;
-		uint16_t	txop;
-	} ac[WME_NUM_AC];
-} iwh_wme_setup_t;
-
-/*
  * structure for command IWH_CMD_TSF
  */
 typedef struct iwh_cmd_tsf {
@@ -2233,6 +2253,20 @@
 /*
  * structure for IWH_CMD_ADD_NODE
  */
+#define	STA_MODE_ADD_MSK		(0)
+#define	STA_MODE_MODIFY_MSK		(1)
+
+#define	STA_FLG_RTS_MIMO_PROT		(1 << 17)
+#define	STA_FLG_MAX_AMPDU_POS		(19)
+#define	STA_FLG_AMPDU_DENSITY_POS	(23)
+#define	STA_FLG_FAT_EN			(1 << 21)
+
+#define	STA_MODIFY_KEY_MASK		(0x01)
+#define	STA_MODIFY_TID_DISABLE_TX	(0x02)
+#define	STA_MODIFY_TX_RATE_MSK		(0x04)
+#define	STA_MODIFY_ADDBA_TID_MSK	(0x08)
+#define	STA_MODIFY_DELBA_TID_MSK	(0x10)
+
 struct	sta_id_modify {
 	uint8_t		addr[6];
 	uint16_t	reserved1;
@@ -2459,6 +2493,30 @@
 	uint32_t	kill_cts_mask;
 } iwh_bt_cmd_t;
 
+typedef struct iwh_wme_param {
+	uint8_t		aifsn;
+	uint8_t		cwmin_e;
+	uint8_t		cwmax_e;
+	uint16_t	txop;
+} iwh_wme_param_t;
+/*
+ * QoS parameter command (REPLY_QOS_PARAM = 0x13)
+ * FIFO0-background, FIFO1-best effort, FIFO2-video, FIFO3-voice
+ */
+
+struct iwh_edca_param {
+	uint16_t	cw_min;
+	uint16_t	cw_max;
+	uint8_t		aifsn;
+	uint8_t		reserved;
+	uint16_t	txop;
+};
+
+typedef struct iwh_qos_param_cmd {
+	uint32_t	flags;
+	struct iwh_edca_param	ac[AC_NUM];
+} iwh_qos_param_cmd_t;
+
 /*
  * firmware image header
  */
--- a/usr/src/uts/common/io/iwh/iwh_var.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/iwh/iwh_var.h	Thu Aug 06 13:05:38 2009 +0800
@@ -4,7 +4,7 @@
  */
 
 /*
- * Copyright (c) 2008, Intel Corporation
+ * Copyright (c) 2009, Intel Corporation
  * All rights reserved.
  */
 
@@ -87,12 +87,13 @@
 
 
 typedef struct iwh_amrr {
-	ieee80211_node_t in;	/* must be the first */
-	int	txcnt;
-	int	retrycnt;
-	int	success;
-	int	success_threshold;
-	int	recovery;
+	ieee80211_node_t in;
+	uint32_t	txcnt;
+	uint32_t	retrycnt;
+	uint32_t	success;
+	uint32_t	success_threshold;
+	int		recovery;
+	volatile uint32_t	ht_mcs_idx;
 } iwh_amrr_t;
 
 struct	iwh_phy_rx {
@@ -113,12 +114,18 @@
 	dev_info_t		*sc_dip;
 	int			(*sc_newstate)(struct ieee80211com *,
 	    enum ieee80211_state, int);
+	void			(*sc_recv_action)(ieee80211_node_t *,
+				    const uint8_t *, const uint8_t *);
+	int			(*sc_send_action)(ieee80211_node_t *,
+				    int, int, uint16_t[4]);
+	volatile uint32_t	sc_cmd_flag;
+	volatile uint32_t	sc_cmd_accum;
 
 	enum ieee80211_state	sc_ostate;
 	kmutex_t		sc_glock;
 	kmutex_t		sc_mt_lock;
 	kmutex_t		sc_tx_lock;
-	kmutex_t		sc_ucode_lock;
+	kmutex_t		sc_suspend_lock;
 	kcondvar_t		sc_mt_cv;
 	kcondvar_t		sc_tx_cv;
 	kcondvar_t		sc_cmd_cv;
@@ -177,6 +184,8 @@
 
 	struct iwh_alive_resp	sc_card_alive_run;
 	struct iwh_init_alive_resp	sc_card_alive_init;
+	iwh_ht_conf_t		sc_ht_conf;
+	uint16_t		sc_dev_id;
 
 	uint32_t		sc_tx_timer;
 	uint32_t		sc_scan_pending;
@@ -197,6 +206,10 @@
 	uint32_t		sc_tx_retries;
 } iwh_sc_t;
 
+#define	SC_CMD_FLG_NONE		(0)
+#define	SC_CMD_FLG_PENDING	(1)
+#define	SC_CMD_FLG_DONE		(2)
+
 #define	IWH_F_ATTACHED		(1 << 0)
 #define	IWH_F_CMD_DONE		(1 << 1)
 #define	IWH_F_FW_INIT		(1 << 2)
@@ -214,6 +227,33 @@
 
 #define	IWH_SUCCESS		0
 #define	IWH_FAIL		EIO
+
+/*
+ * Interaction steps for 802.11e/n between net80211 module
+ * and iwh driver:
+ * -- setup link with 802.11n AP: net80211 module is responsible
+ *    for setup link with 802.11n AP. iwh driver monitors current
+ *    state and make relevant configurations according work mode.
+ * -- QoS parameter updating: net80211 module is responsible for
+ *    extract EDCA parameters from the fram of AP, iwh driver get
+ *    these parameters and make relevant configuration to HW.
+ * -- TX queue management: iwh driver place a frame into suitable
+ *    TX queue according to frame type and user priority extracted
+ *    from frame head.
+ * -- MIMO: iwh driver make relevant configurations in TX and RX
+ *    direction according to AP mode from net80211 module.
+ * -- Link aggregation: AMSDU is implemented by net80211 module and
+ *    AMPDU is implemented by both iwh driver and net80211 module.
+ *    iwh driver distinguish frames in one AMPDU frame and net80211
+ *    module is responsible reordering every frame.
+ * -- Block ACK: net80211 module is responsible for setup agreement
+ *    with AP and iwh driver is responsible for realistic ACK.
+ * -- Rate scaling: This function is implemented independently by
+ *    iwh driver.
+ * -- HT protection: This feature is also implemented by iwh driver
+ *    no interaction with net80211 module.
+ */
+
 #ifdef __cplusplus
 }
 #endif
--- a/usr/src/uts/common/io/mac/plugins/mac_wifi.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/mac/plugins/mac_wifi.c	Thu Aug 06 13:05:38 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -62,7 +62,7 @@
 
 static struct modlmisc mac_wifi_modlmisc = {
 	&mod_miscops,
-	"WiFi MAC plugin"
+	"WiFi MAC plugin 1.4"
 };
 
 static struct modlinkage mac_wifi_modlinkage = {
@@ -200,7 +200,7 @@
 	 * Fill in the fixed parts of the ieee80211_frame.
 	 */
 	wh = (struct ieee80211_frame *)mp->b_rptr;
-	mp->b_wptr += sizeof (struct ieee80211_frame);
+	mp->b_wptr += sizeof (struct ieee80211_frame) + wdp->wd_qospad;
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
 
 	switch (wdp->wd_opmode) {
@@ -227,6 +227,13 @@
 		break;
 	}
 
+	if (wdp->wd_qospad) {
+		struct ieee80211_qosframe *qwh =
+		    (struct ieee80211_qosframe *)wh;
+		qwh->i_qos[1] = 0;
+		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+	}
+
 	switch (wdp->wd_secalloc) {
 	case WIFI_SEC_WEP:
 		/*
@@ -282,6 +289,14 @@
 	llcp = mp->b_rptr + sizeof (struct ieee80211_frame);
 
 	/*
+	 * Generally, QoS data field takes 2 bytes, but some special hardware,
+	 * such as Atheros, will need the 802.11 header padded to a 32-bit
+	 * boundary for 4-address and QoS frames, at this time, it's 4 bytes.
+	 */
+	if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
+		llcp += wdp->wd_qospad;
+
+	/*
 	 * When we receive frames from other hosts, the hardware will have
 	 * already performed WEP decryption, and thus there will not be a WEP
 	 * portion.  However, when we receive a loopback copy of our own
--- a/usr/src/uts/common/io/net80211/net80211.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211.c	Thu Aug 06 13:05:38 2009 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -58,6 +58,9 @@
 	"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 */
 };
 
 #define	IEEE80211_DPRINT(_level, _fmt)	do {	\
@@ -122,6 +125,12 @@
 	wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
 	wd.wd_opmode = ic->ic_opmode;
 	IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
+	wd.wd_qospad = 0;
+	if (in->in_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) {
+		wd.wd_qospad = 2;
+		if (ic->ic_flags & IEEE80211_F_DATAPAD)
+			wd.wd_qospad = roundup(wd.wd_qospad, sizeof (uint32_t));
+	}
 	(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
 	mac_tx_update(ic->ic_mach);
 	ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_mac_update"
@@ -380,6 +389,9 @@
 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
 		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO_A */
 		IEEE80211_CHAN_108G,	/* 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) */
 	};
 	struct ieee80211_channel *ch;
 	uint32_t modeflags;
@@ -465,6 +477,7 @@
 		ic->ic_bss->in_rates = ic->ic_sup_rates[mode];
 	ic->ic_curmode = mode;
 	ieee80211_reset_erp(ic);	/* reset ERP state */
+	ieee80211_wme_initparams(ic);	/* reset WME stat */
 
 	return (0);
 }
@@ -475,35 +488,74 @@
  * where multiple operating modes are possible (e.g. 11g+11b).
  * In those cases we defer to the current operating mode when set.
  */
+/* ARGSUSED */
 enum ieee80211_phymode
 ieee80211_chan2mode(ieee80211com_t *ic, 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_T(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)) {
+	else if (IEEE80211_IS_CHAN_ANYG(chan))
+		return (IEEE80211_MODE_11G);
+	else if (IEEE80211_IS_CHAN_B(chan))
+		return (IEEE80211_MODE_11B);
+	else if (IEEE80211_IS_CHAN_FHSS(chan))
 		return (IEEE80211_MODE_FH);
-	} else if (chan->ich_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) {
-		/*
-		 * This assumes all 11g channels are also usable
-		 * for 11b, which is currently true.
-		 */
-		if (ic->ic_curmode == IEEE80211_MODE_TURBO_G)
-			return (IEEE80211_MODE_TURBO_G);
-		if (ic->ic_curmode == IEEE80211_MODE_11B)
-			return (IEEE80211_MODE_11B);
-		return (IEEE80211_MODE_11G);
-	} else {
-		return (IEEE80211_MODE_11B);
+
+	/* NB: should not get here */
+	ieee80211_err("cannot map channel to mode; freq %u flags 0x%x\n",
+	    chan->ich_freq, chan->ich_flags);
+
+	return (IEEE80211_MODE_11B);
+}
+
+const struct ieee80211_rateset *
+ieee80211_get_suprates(ieee80211com_t *ic, struct ieee80211_channel *c)
+{
+	if (IEEE80211_IS_CHAN_HTA(c))
+		return (&ic->ic_sup_rates[IEEE80211_MODE_11A]);
+	if (IEEE80211_IS_CHAN_HTG(c)) {
+		return (&ic->ic_sup_rates[IEEE80211_MODE_11G]);
 	}
+	return (&ic->ic_sup_rates[ieee80211_chan2mode(ic, c)]);
+}
+
+/*
+ * 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(ieee80211com_t *ic, int freq, int flags)
+{
+	struct ieee80211_channel *c;
+	int i;
+
+	flags &= IEEE80211_CHAN_ALLTURBO;
+	/* brute force search */
+	for (i = 0; i < IEEE80211_CHAN_MAX; i++) {
+		c = &ic->ic_sup_channels[i];
+		if (c->ich_freq == freq &&
+		    (c->ich_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+			return (c);
+	}
+	return (NULL);
 }
 
 /*
  * Return the size of the 802.11 header for a management or data frame.
  */
 int
-ieee80211_hdrspace(const void *data)
+ieee80211_hdrsize(const void *data)
 {
 	const struct ieee80211_frame *wh = data;
 	int size = sizeof (struct ieee80211_frame);
@@ -513,11 +565,60 @@
 	    IEEE80211_FC0_TYPE_CTL);
 	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 (uint16_t);
 
 	return (size);
 }
 
 /*
+ * Return the space occupied by the 802.11 header and any
+ * padding required by the driver.  This works for a
+ * management or data frame.
+ */
+int
+ieee80211_hdrspace(ieee80211com_t *ic, const void *data)
+{
+	int size = ieee80211_hdrsize(data);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		size = roundup(size, sizeof (uint32_t));
+	return (size);
+}
+
+/*
+ * Like ieee80211_hdrsize, but handles any type of frame.
+ */
+int
+ieee80211_anyhdrsize(const void *data)
+{
+	const struct ieee80211_frame *wh = data;
+
+	if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+		switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
+		case IEEE80211_FC0_SUBTYPE_CTS:
+		case IEEE80211_FC0_SUBTYPE_ACK:
+			return (sizeof (struct ieee80211_frame_ack));
+		case IEEE80211_FC0_SUBTYPE_BAR:
+			return (sizeof (struct ieee80211_frame_bar));
+		}
+		return (sizeof (struct ieee80211_frame_min));
+	} else
+		return (ieee80211_hdrsize(data));
+}
+
+/*
+ * Like ieee80211_hdrspace, but handles any type of frame.
+ */
+int
+ieee80211_anyhdrspace(ieee80211com_t *ic, const void *data)
+{
+	int size = ieee80211_anyhdrsize(data);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		size = roundup(size, sizeof (uint32_t));
+	return (size);
+}
+
+/*
  * Allocate and setup a management frame of the specified
  * size.  We return the mblk and a pointer to the start
  * of the contiguous data area that's been reserved based
@@ -567,6 +668,7 @@
 	ieee80211_notify(ic, EVENT_DISASSOC);	/* notify WPA service */
 }
 
+
 /*
  * Get 802.11 kstats defined in ieee802.11(5)
  *
@@ -674,6 +776,12 @@
 				ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A;
 			if (IEEE80211_IS_CHAN_108G(ch))
 				ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G;
+			if (IEEE80211_IS_CHAN_ST(ch))
+				ic->ic_modecaps |= 1 << IEEE80211_MODE_STURBO_A;
+			if (IEEE80211_IS_CHAN_HTA(ch))
+				ic->ic_modecaps |= 1 << IEEE80211_MODE_11NA;
+			if (IEEE80211_IS_CHAN_HTG(ch))
+				ic->ic_modecaps |= 1 << IEEE80211_MODE_11NG;
 			if (ic->ic_curchan == NULL) {
 				/* arbitrarily pick the first channel */
 				ic->ic_curchan = &ic->ic_sup_channels[i];
@@ -686,6 +794,8 @@
 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
 	(void) ieee80211_setmode(ic, ic->ic_curmode);
 
+	if (ic->ic_caps & IEEE80211_C_WME)	/* enable if capable */
+		ic->ic_flags |= IEEE80211_F_WME;
 	if (ic->ic_caps & IEEE80211_C_BURST)
 		ic->ic_flags |= IEEE80211_F_BURST;
 	ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
@@ -698,6 +808,7 @@
 	ieee80211_node_attach(ic);
 	ieee80211_proto_attach(ic);
 	ieee80211_crypto_attach(ic);
+	ieee80211_ht_attach(ic);
 
 	ic->ic_watchdog_timer = 0;
 }
@@ -717,6 +828,7 @@
 	if (ic->ic_opt_ie != NULL)
 		ieee80211_free(ic->ic_opt_ie);
 
+	ieee80211_ht_detach(ic);
 	ieee80211_node_detach(ic);
 	ieee80211_crypto_detach(ic);
 
@@ -726,7 +838,7 @@
 
 static struct modlmisc	i_wifi_modlmisc = {
 	&mod_miscops,
-	"IEEE80211 Kernel Module v1.3"
+	"IEEE80211 Kernel Module v2.0"
 };
 
 static struct modlinkage	i_wifi_modlinkage = {
--- a/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c	Thu Aug 06 13:05:38 2009 +0800
@@ -1,11 +1,11 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -118,10 +118,11 @@
 static int
 ccmp_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
 {
+	struct ccmp_ctx *ctx = k->wk_private;
 	uint8_t *ivp;
 	int hdrlen;
 
-	hdrlen = ieee80211_hdrspace(mp->b_rptr);
+	hdrlen = ieee80211_hdrspace(ctx->cc_ic, mp->b_rptr);
 	/*
 	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
 	 */
@@ -156,7 +157,6 @@
 static int
 ccmp_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
 {
-	struct ieee80211_frame tmp;
 	uint8_t *ivp;
 	uint64_t pn;
 
@@ -194,8 +194,7 @@
 	/*
 	 * Copy up 802.11 header and strip crypto bits.
 	 */
-	bcopy(mp->b_rptr, &tmp, hdrlen);
-	bcopy(&tmp, mp->b_rptr + ccmp.ic_header, hdrlen);
+	(void) memmove(mp->b_rptr + ccmp.ic_header, mp->b_rptr, hdrlen);
 	mp->b_rptr += ccmp.ic_header;
 	mp->b_wptr -= ccmp.ic_trailer;
 
@@ -406,9 +405,18 @@
 	 * initial block as we know whether or not we have
 	 * a QOS frame.
 	 */
-	*(uint16_t *)&aad[24] = 0;
-	b0[1] = 0;
-	aad[1] = 22;
+	if (IEEE80211_QOS_HAS_SEQ(wh)) {
+		struct ieee80211_qosframe *qwh =
+		    (struct ieee80211_qosframe *)wh;
+		aad[24] = qwh->i_qos[0] & 0x0f;	/* just priority bits */
+		aad[25] = 0;
+		b0[1] = aad[24];
+		aad[1] = 22 + 2;
+	} else {
+		*(uint16_t *)&aad[24] = 0;
+		b0[1] = 0;
+		aad[1] = 22;
+	}
 	*(uint16_t *)&aad[26] = 0;
 	*(uint32_t *)&aad[28] = 0;
 }
--- a/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c	Thu Aug 06 13:05:38 2009 +0800
@@ -5,7 +5,7 @@
 
 /*
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -147,7 +147,7 @@
 	if (ic->ic_flags & IEEE80211_F_COUNTERM)
 		return (0);
 
-	hdrlen = ieee80211_hdrspace(mp->b_rptr);
+	hdrlen = ieee80211_hdrspace(ic, mp->b_rptr);
 	/*
 	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
 	 */
@@ -194,7 +194,6 @@
 {
 	struct tkip_ctx *ctx = k->wk_private;
 	struct ieee80211com *ic = ctx->tc_ic;
-	struct ieee80211_frame tmp;
 	uint8_t *ivp;
 	uint64_t pn;
 
@@ -242,8 +241,7 @@
 	/*
 	 * Copy up 802.11 header and strip crypto bits.
 	 */
-	bcopy(mp->b_rptr, &tmp, hdrlen);
-	bcopy(&tmp, mp->b_rptr + tkip.ic_header, hdrlen);
+	(void) memmove(mp->b_rptr + tkip.ic_header, mp->b_rptr, hdrlen);
 	mp->b_rptr += tkip.ic_header;
 	mp->b_wptr -= tkip.ic_trailer;
 
@@ -262,7 +260,7 @@
 		int hdrlen;
 		uint8_t *mic;
 
-		hdrlen = ieee80211_hdrspace(mp->b_rptr);
+		hdrlen = ieee80211_hdrspace(ctx->tc_ic, mp->b_rptr);
 		mic = mp->b_wptr;
 		mp->b_wptr += tkip.ic_miclen;
 
@@ -287,7 +285,7 @@
 	struct tkip_ctx *ctx = k->wk_private;
 
 	if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
-		int hdrlen = ieee80211_hdrspace(mp->b_rptr);
+		int hdrlen = ieee80211_hdrspace(ctx->tc_ic, mp->b_rptr);
 		uint8_t mic[IEEE80211_WEP_MICLEN];
 		uint8_t mic0[IEEE80211_WEP_MICLEN];
 
@@ -640,7 +638,12 @@
 		break;
 	}
 
-	hdr[12] = 0; /* QoS not supported */
+	if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+		const struct ieee80211_qosframe *qwh =
+		    (const struct ieee80211_qosframe *)wh;
+		hdr[12] = qwh->i_qos[0] & IEEE80211_QOS_TID;
+	} else
+		hdr[12] = 0;
 	hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
 }
 
--- a/usr/src/uts/common/io/net80211/net80211_crypto_wep.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_wep.c	Thu Aug 06 13:05:38 2009 +0800
@@ -1,11 +1,11 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,8 +35,6 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * IEEE 802.11 WEP crypto support.
  */
@@ -135,7 +133,7 @@
 
 	if (mp == NULL)
 		return (0);
-	hdrlen = ieee80211_hdrspace(wh);
+	hdrlen = ieee80211_hdrspace(ctx->wc_ic, wh);
 
 	ivp = (uint8_t *)wh;
 	ivp += hdrlen;
@@ -182,10 +180,6 @@
 static int
 wep_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
 {
-	struct ieee80211_frame *wh, whbuf;
-
-	wh = (struct ieee80211_frame *)mp->b_rptr;
-
 	/*
 	 * Check if the device handled the decrypt in hardware.
 	 * If so we just strip the header; otherwise we need to
@@ -200,9 +194,8 @@
 	/*
 	 * Copy up 802.11 header and strip crypto bits.
 	 */
-	bcopy(wh, &whbuf, sizeof (whbuf));
+	(void) memmove(mp->b_rptr + wep.ic_header, mp->b_rptr, hdrlen);
 	mp->b_rptr += wep.ic_header;
-	bcopy(&whbuf, mp->b_rptr, hdrlen);
 	mp->b_wptr -= wep.ic_trailer;
 
 	return (1);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/net80211/net80211_ht.c	Thu Aug 06 13:05:38 2009 +0800
@@ -0,0 +1,1900 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * IEEE 802.11n protocol support.
+ */
+#include <sys/mac_provider.h>
+#include <sys/strsun.h>
+#include <sys/byteorder.h>
+
+#include "net80211_impl.h"
+
+/* define here, used throughout file */
+#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
+#define	SM(_v, _f)	(((_v) << _f##_S) & _f)
+
+/* 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 */
+};
+
+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 }
+	};
+
+#define	IEEE80211_AMPDU_AGE
+
+#define	IEEE80211_AGGR_TIMEOUT	250		/* msecs */
+#define	IEEE80211_AGGR_MINRETRY	(10 * hz)	/* ticks */
+#define	IEEE80211_AGGR_MAXTRIES	3
+
+/*
+ * 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.
+ */
+#define	FF_LLC_SIZE	\
+	(sizeof (struct ether_header) + sizeof (struct ieee80211_llc))
+mblk_t *
+ieee80211_decap_amsdu(struct ieee80211_node *in, mblk_t *mp)
+{
+	struct ieee80211com *ic = in->in_ic;
+	struct ether_header *eh;
+	struct ieee80211_frame *wh;
+	int framelen, hdrspace;
+	mblk_t *m0;
+
+	/* all msdu has same ieee80211_frame header */
+	wh = (struct ieee80211_frame *)mp->b_rptr;
+	hdrspace = ieee80211_hdrspace(ic, wh);
+	mp->b_rptr += hdrspace;	/* A-MSDU subframe follows */
+
+	for (;;) {
+		/*
+		 * 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;
+		 */
+		if (MBLKL(mp) < FF_LLC_SIZE) {
+			ieee80211_err("too short, decap failed\n");
+			goto out;
+		}
+		/*
+		 * Decap frames, encapsulate to 802.11 frame then deliver.
+		 * 802.3 header is first (struct ether_header)
+		 * 802.2 header follows (struct ieee80211_llc)
+		 * data, msdu = llc + data
+		 */
+		eh = (struct ether_header *)mp->b_rptr;
+						/* 802.2 header follows */
+		framelen = ntohs(eh->ether_type);	/* llc + data */
+		m0 = allocb(hdrspace + framelen, BPRI_MED);
+		if (m0 == NULL) {
+			ieee80211_err("decap_msdu(): can't alloc mblk\n");
+			goto out;
+		}
+		(void) memcpy(m0->b_wptr, (uint8_t *)wh, hdrspace);
+		m0->b_wptr += hdrspace;
+		(void) memcpy(m0->b_wptr,
+		    mp->b_rptr + sizeof (struct ether_header), framelen);
+		m0->b_wptr += framelen;
+
+		ic->ic_stats.is_rx_frags++;
+		ic->ic_stats.is_rx_bytes += MBLKL(m0);
+		IEEE80211_UNLOCK(ic);
+		mac_rx(ic->ic_mach, NULL, m0);	/* deliver to mac */
+		IEEE80211_LOCK(ic);
+
+		framelen += sizeof (struct ether_header);
+		if (MBLKL(mp) == framelen)	/* last, no padding */
+			goto out;
+		/*
+		 * Remove frame contents; each intermediate frame
+		 * is required to be aligned to a 4-byte boundary.
+		 */
+		mp->b_rptr += roundup(framelen, 4);	/* padding */
+	}
+
+out:
+	freemsg(mp);
+	return (NULL);	/* none delivered by caller */
+}
+#undef FF_LLC_SIZE
+
+/*
+ * 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)
+{
+	(void) memset(rap, 0, sizeof (*rap));
+	rap->rxa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
+	    : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX);
+	rap->rxa_start = (uint16_t)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)
+{
+	mblk_t *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 -= MBLKL(m);
+			freemsg(m);
+			if (--rap->rxa_qframes == 0)
+				break;
+		}
+	}
+	ASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0);
+}
+
+/*
+ * 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 void
+ampdu_dispatch(struct ieee80211_node *in, mblk_t *m)
+{
+	m->b_flag |= M_AMPDU;	/* bypass normal processing */
+	/* NB: rssi and rstamp are ignored w/ M_AMPDU set */
+	(void) ieee80211_input(in->in_ic, m, in, 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 *in)
+{
+	mblk_t *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 -= MBLKL(m);
+		rap->rxa_qframes--;
+
+		ampdu_dispatch(in, 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;
+			}
+		}
+		ASSERT(n == 0);
+	}
+	/*
+	 * Adjust the start of the BA window to
+	 * reflect the frames just dispatched.
+	 */
+	rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
+}
+
+#ifdef IEEE80211_AMPDU_AGE
+/*
+ * Dispatch all frames in the A-MPDU re-order queue.
+ */
+static void
+ampdu_rx_flush(struct ieee80211_node *in, struct ieee80211_rx_ampdu *rap)
+{
+	mblk_t *m;
+	int i;
+
+	ieee80211_dbg(IEEE80211_MSG_HT,
+	    "ampdu_rx_flush(%d)\n",
+	    rap->rxa_wnd);
+
+	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 -= MBLKL(m);
+		rap->rxa_qframes--;
+
+		ampdu_dispatch(in, 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 *in,
+	struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
+{
+	mblk_t *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 -= MBLKL(m);
+			rap->rxa_qframes--;
+
+			ampdu_dispatch(in, 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;
+			}
+		}
+		if (n != 0) {
+			ieee80211_dbg(IEEE80211_MSG_HT,
+			    "ampdu_rx_flush_upto(): "
+			    "lost %d frames, qframes %d off %d "
+			    "BA win <%d:%d> winstart %d\n",
+			    n, rap->rxa_qframes, i, rap->rxa_start,
+			    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+			    winstart);
+		}
+	}
+	/*
+	 * 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 *in, mblk_t *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 ieee80211_qosframe *wh;
+	struct ieee80211_rx_ampdu *rap;
+	ieee80211_seq rxseq;
+	uint8_t tid;
+	int off;
+
+	ASSERT(in->in_flags & IEEE80211_NODE_HT);
+
+	/* NB: m_len known to be sufficient */
+	wh = (struct ieee80211_qosframe *)m->b_rptr;
+	ASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA);
+
+	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 = &in->in_rx_ampdu[tid];
+	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+		/*
+		 * No ADDBA request yet, don't touch.
+		 */
+		return (PROCESS);
+	}
+	rxseq = LE_16(*(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.
+			 */
+			ASSERT(rap->rxa_m[0] == NULL);	/* [0] is m */
+			ampdu_dispatch(in, m);
+			ampdu_rx_dispatch(rap, in);
+			ieee80211_dbg(IEEE80211_MSG_HT,
+			    "ieee80211_ampdu_reorder(%u), CONSUMED ...\n",
+			    rap->rxa_qframes);
+			return (CONSUMED);
+		} else {
+			/*
+			 * In order; advance window and notify
+			 * caller to dispatch directly.
+			 */
+			rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
+			ieee80211_dbg(IEEE80211_MSG_HT,
+			    "ieee80211_ampdu_reorder(%u), PROCESS ...\n",
+			    rap->rxa_start);
+			return (PROCESS);
+		}
+	}
+	ieee80211_dbg(IEEE80211_MSG_HT,
+	    "ieee80211_ampdu_reorder(%u, %u), out of order ...\n",
+	    rxseq, rap->rxa_start);
+	/*
+	 * 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) {
+#ifdef IEEE80211_AMPDU_AGE
+		/*
+		 * Common case (hopefully): in the BA window.
+		 * Sec 9.10.7.6 a) (D2.04 p.118 line 47)
+		 * --
+		 * 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.
+		 */
+		clock_t ticks;
+
+		ticks = ddi_get_lbolt();
+		if (rap->rxa_qframes != 0) {
+			/* honor batimeout? */
+			if (ticks - rap->rxa_age > drv_usectohz(500*1000)) {
+				/*
+				 * Too long since we received the first
+				 * frame; flush the reorder buffer.
+				 */
+				if (rap->rxa_qframes != 0) {
+					ampdu_rx_flush(in, 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 += MBLKL(m);
+		} else {
+			ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
+			    "a-mpdu duplicate "
+			    "seqno %u tid %u BA win <%u:%u>\n",
+			    rxseq, tid, rap->rxa_start,
+			    IEEE80211_SEQ_ADD(rap->rxa_start,
+			    rap->rxa_wnd - 1));
+			freemsg(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_dbg(IEEE80211_MSG_HT,
+		    "move BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
+		    rap->rxa_start,
+		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd - 1),
+		    rap->rxa_qframes, rxseq, tid);
+
+		/*
+		 * 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(in, 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_dbg(IEEE80211_MSG_HT, "MSDU"
+		    "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
+		    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)" : "");
+		freemsg(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 *in, mblk_t *m0)
+{
+	struct ieee80211_frame_bar *wh;
+	struct ieee80211_rx_ampdu *rap;
+	ieee80211_seq rxseq;
+	int tid, off;
+
+	wh = (struct ieee80211_frame_bar *)m0->b_rptr;
+	/* check basic BAR */
+	tid = MS(LE_16(wh->i_ctl), IEEE80211_BAR_TID);
+	rap = &in->in_rx_ampdu[tid];
+	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+		/*
+		 * No ADDBA request yet, don't touch.
+		 */
+		ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
+		    "BAR no BA stream, tid %u\n", tid);
+		return;
+	}
+	rxseq = LE_16(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_dbg(IEEE80211_MSG_HT,
+		    "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
+		    rap->rxa_start,
+		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+		    rap->rxa_qframes, rxseq, tid);
+
+		ampdu_rx_flush_upto(in, 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_dbg(IEEE80211_MSG_HT, "BAR "
+		    "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
+		    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)" : "");
+	}
+}
+
+/*
+ * 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 *in, const uint8_t *htcap)
+{
+	struct ieee80211_tx_ampdu *tap;
+	int ac;
+
+	if (in->in_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(in);
+	}
+	ieee80211_parse_htcap(in, htcap);
+	for (ac = 0; ac < WME_NUM_AC; ac++) {
+		tap = &in->in_tx_ampdu[ac];
+		tap->txa_ac = (uint8_t)ac;
+		/* NB: further initialization deferred */
+	}
+	in->in_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 *in)
+{
+	struct ieee80211com *ic = in->in_ic;
+	int i;
+
+	ASSERT(in->in_flags & IEEE80211_NODE_HT);
+
+	/* optimize this */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		struct ieee80211_tx_ampdu *tap = &in->in_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(in, &in->in_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(&in->in_rx_ampdu[i]);
+
+	in->in_htcap = 0;
+	in->in_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->ich_freq,
+	    (c->ich_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->ich_freq,
+		    chan->ich_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 *in)
+{
+	struct ieee80211com *ic = in->in_ic;
+	struct ieee80211_tx_ampdu *tap;
+	int ac;
+
+	ASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT);
+
+	/* 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).
+	 */
+	in->in_chan = ieee80211_ht_adjust_channel(ic, in->in_chan,
+	    ic->ic_flags_ext);
+
+	in->in_htcap = 0;
+	if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+		in->in_htcap |= IEEE80211_HTCAP_SHORTGI20;
+	if (IEEE80211_IS_CHAN_HT40(in->in_chan)) {
+		in->in_htcap |= IEEE80211_HTCAP_CHWIDTH40;
+		in->in_chw = 40;
+		if (IEEE80211_IS_CHAN_HT40U(in->in_chan))
+			in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
+		else if (IEEE80211_IS_CHAN_HT40D(in->in_chan))
+			in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
+		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+			in->in_htcap |= IEEE80211_HTCAP_SHORTGI40;
+	} else {
+		in->in_chw = 20;
+		in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE;
+	}
+	in->in_htctlchan = ieee80211_chan2ieee(ic, in->in_chan);
+
+	in->in_htopmode = 0;		/* need protection state */
+	in->in_htstbc = 0;		/* need info */
+
+	for (ac = 0; ac < WME_NUM_AC; ac++) {
+		tap = &in->in_tx_ampdu[ac];
+		tap->txa_ac = (uint8_t)ac;
+	}
+	/* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */
+	in->in_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_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
+	    "HT bss occupancy change: %d sta, %d ht, "
+	    "%d ht40%s, HT protmode now 0x%x\n",
+	    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);
+}
+
+/*
+ * 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_curchan) &&
+	    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 *in)
+{
+	struct ieee80211com *ic = in->in_ic;
+
+	IEEE80211_LOCK_ASSERT(ic);
+
+	if (in->in_flags & IEEE80211_NODE_HT) {
+		ic->ic_ht_sta_assoc++;
+		if (in->in_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 *in)
+{
+	struct ieee80211com *ic = in->in_ic;
+
+	IEEE80211_LOCK_ASSERT(ic);
+
+	if (in->in_flags & IEEE80211_NODE_HT) {
+		ic->ic_ht_sta_assoc--;
+		if (in->in_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 = (uint8_t)protmode;
+		htinfo_notify(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 *in, const uint8_t *ie)
+{
+	struct ieee80211com *ic = in->in_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.
+		 */
+		in->in_flags |= IEEE80211_NODE_HTCOMPAT;
+		ie += 4;
+	} else
+		in->in_flags &= ~IEEE80211_NODE_HTCOMPAT;
+
+	in->in_htcap = *(uint16_t *)(ie +
+	    offsetof(struct ieee80211_ie_htcap, hc_cap));
+	in->in_htparam = ie[offsetof(struct ieee80211_ie_htcap, hc_param)];
+	/* needed or will ieee80211_parse_htinfo always be called? */
+	in->in_chw = (in->in_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 *in, const uint8_t *ie)
+{
+	struct ieee80211com *ic = in->in_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;
+	in->in_htctlchan = htinfo->hi_ctrlchannel;
+	in->in_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
+	w = *(uint16_t *)(&htinfo->hi_byte2);
+	in->in_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
+	w = *(uint16_t *)(&htinfo->hi_byte45);
+	in->in_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 (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
+			htflags = IEEE80211_CHAN_HT40U;
+		else if (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
+			htflags = IEEE80211_CHAN_HT40D;
+	}
+	chanflags = (in->in_chan->ich_flags &~ IEEE80211_CHAN_HT) | htflags;
+	if (chanflags != in->in_chan->ich_flags) {
+		c = ieee80211_find_channel(ic,
+		    in->in_chan->ich_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, in->in_chan, IEEE80211_CHAN_HT20);
+			ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
+			    "no HT40 channel (freq %u), falling back to HT20\n",
+			    in->in_chan->ich_freq);
+			/* stat */
+		}
+		if (c != NULL && c != in->in_chan) {
+			ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
+			    "switch station to HT%d channel %u/0x%x\n",
+			    IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
+			    c->ich_freq, c->ich_flags);
+			in->in_chan = c;
+		}
+		/* NB: caller responsible for forcing any channel change */
+	}
+	/* update node's tx channel width */
+	in->in_chw = IEEE80211_IS_CHAN_HT40(in->in_chan)? 40 : 20;
+}
+
+/*
+ * Install received HT rate set by parsing the HT cap ie.
+ */
+int
+ieee80211_setup_htrates(struct ieee80211_node *in, const uint8_t *ie, int flags)
+{
+	const struct ieee80211_ie_htcap *htcap;
+	struct ieee80211_htrateset *rs;
+	int i;
+
+	rs = &in->in_htrates;
+	(void) 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 (ieee80211_isclr(htcap->hc_mcsset, i))
+				continue;
+			if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
+				ieee80211_dbg(
+				    IEEE80211_MSG_XRATE | IEEE80211_MSG_HT,
+				    "WARNING, HT rate set too large; only "
+				    "using %u rates\n",
+				    IEEE80211_HTRATE_MAXSIZE);
+				break;
+			}
+			rs->rs_rates[rs->rs_nrates++] = (uint8_t)i;
+		}
+	}
+	return (ieee80211_fix_rate(in, (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 *in, 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 = &in->in_htrates;
+	if (rs->rs_nrates == 0) {
+		ieee80211_dbg(IEEE80211_MSG_XRATE | IEEE80211_MSG_HT,
+		    "WARNING, empty HT rate set\n");
+		return;
+	}
+	for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
+		if (ieee80211_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;
+
+	tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+	tap->txa_attempts++;
+}
+
+static void
+addba_start_timeout(struct ieee80211_tx_ampdu *tap)
+{
+	tap->txa_timer = timeout(addba_timeout, (void *)tap,
+	    drv_usectohz(IEEE80211_AGGR_TIMEOUT * 1000));
+	tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
+	tap->txa_lastrequest = ddi_get_lbolt();
+}
+
+static void
+addba_stop_timeout(struct ieee80211_tx_ampdu *tap)
+{
+	if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
+		if (tap->txa_timer != NULL) {
+			(void) untimeout(tap->txa_timer);
+			tap->txa_timer = NULL;
+		}
+		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.
+ */
+/* ARGSUSED */
+static int
+ieee80211_addba_request(struct ieee80211_node *in,
+    struct ieee80211_tx_ampdu *tap,
+    int dialogtoken, int baparamset, int batimeout)
+{
+	int bufsiz;
+
+	tap->txa_token = (uint8_t)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((uint16_t)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.
+ */
+/* ARGSUSED */
+static int
+ieee80211_addba_response(struct ieee80211_node *in,
+    struct ieee80211_tx_ampdu *tap,
+    int status, int baparamset, int batimeout)
+{
+	int bufsiz;
+
+	addba_stop_timeout(tap);
+	if (status == IEEE80211_STATUS_SUCCESS) {
+		bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+		/* override our request? */
+		tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
+		    : min((uint16_t)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.
+ */
+/* ARGSUSED */
+static void
+ieee80211_addba_stop(struct ieee80211_node *in, struct ieee80211_tx_ampdu *tap)
+{
+	addba_stop_timeout(tap);
+	if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
+		/* clear aggregation queue */
+		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 *in,
+	const uint8_t *frm, const uint8_t *efrm)
+{
+	struct ieee80211com *ic = in->in_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 = *(uint16_t *)(frm+3);
+			batimeout = *(uint16_t *)(frm+5);
+			baseqctl = *(uint16_t *)(frm+7);
+
+			tid = MS(baparamset, IEEE80211_BAPS_TID);
+			bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "recv ADDBA request: dialogtoken %u "
+			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
+			    "baseqctl %d:%d\n",
+			    dialogtoken, baparamset, tid, bufsiz, batimeout,
+			    MS(baseqctl, IEEE80211_BASEQ_START),
+			    MS(baseqctl, IEEE80211_BASEQ_FRAG));
+
+			rap = &in->in_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 ((in->in_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_dbg(
+				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+				    "reject ADDBA request: %s\n",
+				    in->in_flags & IEEE80211_NODE_AMPDU_RX ?
+				    "administratively disabled" :
+				    "not negotiated for station");
+				args[1] = IEEE80211_STATUS_UNSPECIFIED;
+			}
+			/* 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(in, IEEE80211_ACTION_CAT_BA,
+			    IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
+			return;
+
+		case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+			dialogtoken = frm[2];
+			code = *(uint16_t *)(frm+3);
+			baparamset = *(uint16_t *)(frm+5);
+			tid = MS(baparamset, IEEE80211_BAPS_TID);
+			bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+			batimeout = *(uint16_t *)(frm+7);
+
+			ac = TID_TO_WME_AC(tid);
+			tap = &in->in_tx_ampdu[ac];
+			if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+				ieee80211_err("ADDBA response"
+				    "no pending ADDBA, tid %d dialogtoken %u "
+				    "code %d\n", tid, dialogtoken, code);
+				return;
+			}
+			if (dialogtoken != tap->txa_token) {
+				ieee80211_err("ADDBA response"
+				    "dialogtoken mismatch: waiting for %d, "
+				    "received %d, tid %d code %d\n",
+				    tap->txa_token, dialogtoken, tid, code);
+				return;
+			}
+
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "recv ADDBA response: dialogtoken %u code %d "
+			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d\n",
+			    dialogtoken, code, baparamset, tid, bufsiz,
+			    batimeout);
+			ic->ic_addba_response(in, tap,
+			    code, baparamset, batimeout);
+			return;
+
+		case IEEE80211_ACTION_BA_DELBA:
+			baparamset = *(uint16_t *)(frm+2);
+			code = *(uint16_t *)(frm+4);
+
+			tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
+			    "code %d\n", baparamset, tid,
+			    MS(baparamset, IEEE80211_DELBAPS_INIT), code);
+
+			if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
+				ac = TID_TO_WME_AC(tid);
+				tap = &in->in_tx_ampdu[ac];
+				ic->ic_addba_stop(in, tap);
+			} else {
+				rap = &in->in_rx_ampdu[tid];
+				ampdu_rx_stop(rap);
+			}
+			return;
+		}
+		break;
+	}
+	ieee80211_recv_action(in, 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).
+ */
+/* ARGSUSED */
+void
+ieee80211_recv_action(struct ieee80211_node *in,
+    const uint8_t *frm, const uint8_t *efrm)
+{
+	const struct ieee80211_action *ia;
+	int chw;
+
+	ia = (const struct ieee80211_action *) frm;
+	switch (ia->ia_category) {
+	case IEEE80211_ACTION_CAT_BA:
+		ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+		    "BA action %d not implemented\n",
+		    ia->ia_action);
+		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 != in->in_chw) {
+				in->in_chw = (uint8_t)chw;
+				in->in_flags |= IEEE80211_NODE_CHWUPDATE;
+			}
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "HT txchwidth, width %d (%s)\n",
+			    chw,
+			    in->in_flags & IEEE80211_NODE_CHWUPDATE ?
+			    "new" : "no change");
+			break;
+		case IEEE80211_ACTION_HT_MIMOPWRSAVE:
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "HT MIMO PS\n");
+			break;
+		default:
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "HT action %d not implemented\n",
+			    ia->ia_action);
+			break;
+		}
+		break;
+	default:
+		ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+		    "category %d not implemented\n",
+		    ia->ia_category);
+		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 *in,
+    struct ieee80211_tx_ampdu *tap)
+{
+	struct ieee80211com *ic = in->in_ic;
+	uint16_t args[4];
+	int tid, dialogtoken;
+	static int tokens = 0;	/* tokens */
+	clock_t ticks;
+
+	ticks = ddi_get_lbolt();
+	if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
+		/* do deferred setup of state */
+		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);
+	}
+	/* hack for not doing proper locking */
+	tap->txa_flags &= ~IEEE80211_AGGR_NAK;
+
+	dialogtoken = (tokens+1) % 63;		/* algorithm */
+
+	tid = WME_AC_TO_TID(tap->txa_ac);
+	args[0] = (uint16_t)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(in, tap, dialogtoken, args[1], args[2])) {
+		/* unable to setup state, don't make request */
+		ieee80211_dbg(IEEE80211_MSG_HT,
+		    "could not setup BA stream for AC %d\n",
+		    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(in, 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 *in, struct ieee80211_tx_ampdu *tap)
+{
+	struct ieee80211com *ic = in->in_ic;
+	uint16_t args[4];
+
+	if (IEEE80211_AMPDU_RUNNING(tap)) {
+		ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+		    "stop BA stream for AC %d\n", tap->txa_ac);
+
+		ic->ic_addba_stop(in, tap);
+		args[0] = WME_AC_TO_TID(tap->txa_ac);
+		args[1] = IEEE80211_DELBAPS_INIT;
+		args[2] = 1;				/* reason code */
+		(void) ieee80211_send_action(in, IEEE80211_ACTION_CAT_BA,
+		    IEEE80211_ACTION_BA_DELBA, args);
+	} else {
+		ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+		    "BA stream for AC %d not running\n",
+		    tap->txa_ac);
+	}
+}
+
+/*
+ * 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 *in,
+    const struct ieee80211_tx_ampdu *tap)
+{
+#define	ADDSHORT(frm, v) do {			\
+        _NOTE(CONSTCOND)                        \
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+        _NOTE(CONSTCOND)                        \
+} while (0)
+	struct ieee80211com *ic = in->in_ic;
+	struct ieee80211_frame_min *wh;
+	mblk_t *m;
+	uint8_t *frm;
+	uint16_t barctl, barseqctl;
+	int tid;
+
+
+	m = ieee80211_getmgtframe(&frm, sizeof (struct ieee80211_ba_request));
+	if (m == NULL)
+		return (ENOMEM);
+
+	wh = (struct ieee80211_frame_min *)m->b_rptr;
+	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, in->in_macaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
+
+	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->b_wptr = frm;
+
+	ieee80211_dbg(IEEE80211_MSG_DEBUG,
+	    "send bar frame (tid %u start %u) on channel %u\n",
+	    tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+	(void) (*ic->ic_xmit)(ic, m, IEEE80211_FC0_TYPE_CTL);	/* MGT? */
+
+	return (0);
+#undef ADDSHORT
+}
+
+/*
+ * 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 *in,
+    int category, int action, uint16_t args[4])
+{
+#define	ADDSHORT(frm, v) do {			\
+        _NOTE(CONSTCOND)                        \
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+        _NOTE(CONSTCOND)                        \
+} while (0)
+	struct ieee80211com *ic = in->in_ic;
+	mblk_t *m;
+	uint8_t *frm;
+	uint16_t baparamset;
+	int ret;
+
+	ASSERT(in != NULL);
+
+	m = ieee80211_getmgtframe(&frm,
+	    sizeof (uint16_t)	/* action+category */
+	    /* may action payload */
+	    + sizeof (struct ieee80211_action_ba_addbaresponse));
+	if (m == NULL)
+		return (ENOMEM);
+
+	*frm++ = (uint8_t)category;
+	*frm++ = (uint8_t)action;
+	switch (category) {
+	case IEEE80211_ACTION_CAT_BA:
+		switch (action) {
+		case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "send ADDBA request: dialogtoken %d "
+			    "baparamset 0x%x (tid %d) "
+			    "batimeout 0x%x baseqctl 0x%x\n",
+			    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_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "send ADDBA response: dialogtoken %d status %d "
+			    "baparamset 0x%x (tid %d) batimeout %d\n",
+			    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:
+			baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
+			    | SM(args[1], IEEE80211_DELBAPS_INIT);
+			ADDSHORT(frm, baparamset);
+			ADDSHORT(frm, args[2]);	/* reason code */
+
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "send DELBA action: tid %d, initiator %d "
+			    "reason %d\n",
+			    args[0], args[1], args[2]);
+			break;
+		default:
+			goto badaction;
+		}
+		break;
+	case IEEE80211_ACTION_CAT_HT:
+		switch (action) {
+		case IEEE80211_ACTION_HT_TXCHWIDTH:
+			ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+			    "send HT txchwidth: width %d\n",
+			    IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ? 40 : 20);
+			*frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ?
+			    IEEE80211_A_HT_TXCHWIDTH_2040 :
+			    IEEE80211_A_HT_TXCHWIDTH_20;
+			break;
+		default:
+			goto badaction;
+		}
+		break;
+	default:
+	badaction:
+		ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+		    "unsupported category %d action %d\n",
+		    category, action);
+		return (EINVAL);
+		/* NOTREACHED */
+	}
+	m->b_wptr = frm;
+
+	ret = ieee80211_mgmt_output(ic, in, m, IEEE80211_FC0_SUBTYPE_ACTION, 0);
+
+	return (ret);
+#undef ADDSHORT
+}
+
+/*
+ * 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) {
+			/* NB: this assumes a particular implementation */
+			ieee80211_setbit(frm, r);
+		}
+	}
+}
+
+/*
+ * Add body of an HTCAP information element.
+ */
+static uint8_t *
+ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *in)
+{
+#define	ADDSHORT(frm, v) do {			\
+        _NOTE(CONSTCOND)                        \
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+        _NOTE(CONSTCOND)                        \
+} while (0)
+	struct ieee80211com *ic = in->in_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 (locally constraint) */
+		rxmax = MS(in->in_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
+		density = MS(in->in_htparam, IEEE80211_HTCAP_MPDUDENSITY);
+	} else {
+		/* override 20/40 use based on current channel */
+		if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+			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 */
+	(void) memset(frm, 0, sizeof (struct ieee80211_ie_htcap) -
+	    offsetof(struct ieee80211_ie_htcap, hc_mcsset));
+
+	/* supported MCS set */
+	/*
+	 * it would better to get the rate set from in_htrates
+	 * so we can restrict it but for sta mode in_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 *in)
+{
+	frm[0] = IEEE80211_ELEMID_HTCAP;
+	frm[1] = sizeof (struct ieee80211_ie_htcap) - 2;
+	return (ieee80211_add_htcap_body(frm + 2, in));
+}
+
+/*
+ * 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 *in)
+{
+	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, in));
+}
+
+/*
+ * 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 */
+			ieee80211_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;
+
+	/* only update on channel change */
+	ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_curchan);
+	ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
+	if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
+		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+	else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan))
+		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+	else	/* LINTED */
+		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
+	if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+		ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+	/* protection mode */
+	ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
+
+	/* 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 *in)
+{
+	struct ieee80211com *ic = in->in_ic;
+
+	/* pre-zero remainder of ie */
+	(void) memset(frm, 0, sizeof (struct ieee80211_ie_htinfo) - 2);
+
+	/* primary/control channel center */
+	*frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
+
+	frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
+	if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
+		frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+	else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan))
+		frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+	else	/* LINTED */
+		frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
+	if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+		frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+	frm[1] = ic->ic_curhtprotmode;
+
+	frm += 5;
+
+	/* basic MCS set */
+	ieee80211_set_basic_htrates(frm, &in->in_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 *in)
+{
+	frm[0] = IEEE80211_ELEMID_HTINFO;
+	frm[1] = sizeof (struct ieee80211_ie_htinfo) - 2;
+
+	return (ieee80211_add_htinfo_body(frm + 2, in));
+}
+
+/*
+ * 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 *in)
+{
+	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, in));
+}
+
+void
+ieee80211_ht_attach(struct ieee80211com *ic)
+{
+	/* 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;
+
+	/* 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.
+		 * 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;
+		/* 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;
+	}
+
+#define	ieee80211_isset16(a, i)	((a) & (1 << (i)))
+	/* fill default rate sets for 11NA/11NG if driver has no specified */
+	if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NA) &&
+	    ic->ic_sup_rates[IEEE80211_MODE_11NA].ir_nrates == 0) {
+		ic->ic_sup_rates[IEEE80211_MODE_11NA] =
+		    ic->ic_sup_rates[IEEE80211_MODE_11A];
+	}
+
+	if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NG) &&
+	    ic->ic_sup_rates[IEEE80211_MODE_11NG].ir_nrates == 0) {
+		ic->ic_sup_rates[IEEE80211_MODE_11NG] =
+		    ic->ic_sup_rates[IEEE80211_MODE_11G];
+	}
+#undef ieee80211_isset16
+}
+
+/* ARGSUSED */
+void
+ieee80211_ht_detach(struct ieee80211com *ic)
+{
+}
+
+/* ARGSUSED */
+static void
+ht_announce(struct ieee80211com *ic, int mode,
+	const struct ieee80211_htrateset *rs)
+{
+	int i, rate;
+
+	ieee80211_dbg(IEEE80211_MSG_HT, "%s MCS: \n",
+	    ieee80211_phymode_name[mode]);
+	for (i = 0; i < rs->rs_nrates; i++) {
+		rate = ieee80211_htrates[rs->rs_rates[i]];
+		ieee80211_dbg(IEEE80211_MSG_HT, "%s%d%sMbps\n",
+		    (i != 0 ? " " : ""),
+		    rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
+	}
+}
+
+void
+ieee80211_ht_announce(struct ieee80211com *ic)
+{
+	if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NA))
+		ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n);
+	if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NG))
+		ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n);
+}
+
+/* ARGSUSED */
+const struct ieee80211_htrateset *
+ieee80211_get_suphtrates(struct ieee80211com *ic,
+	const struct ieee80211_channel *c)
+{
+	return (&ieee80211_rateset_11n);
+}
--- a/usr/src/uts/common/io/net80211/net80211_impl.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_impl.h	Thu Aug 06 13:05:38 2009 +0800
@@ -1,11 +1,11 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -133,12 +133,15 @@
 	(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_T | 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_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN |	\
+	IEEE80211_CHAN_HT)
 #define	IEEE80211_CHAN_ALLTURBO	\
-	(IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO)
+	(IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
 
 #define	IEEE80211_IS_CHAN_FHSS(_c)	\
 	(((_c)->ich_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
@@ -154,8 +157,11 @@
 	(IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c))
 #define	IEEE80211_IS_CHAN_T(_c)		\
 	(((_c)->ich_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+		/* IEEE80211_IS_CHAN_108A */
 #define	IEEE80211_IS_CHAN_108G(_c)	\
 	(((_c)->ich_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
+#define	IEEE80211_IS_CHAN_ST(_c)	\
+	(((_c)->ich_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)
 
 #define	IEEE80211_IS_CHAN_OFDM(_c)	\
 	((_c)->ich_flags & IEEE80211_CHAN_OFDM)
@@ -166,6 +172,45 @@
 #define	IEEE80211_IS_CHAN_PASSIVE(_c)	\
 	((_c)->ich_flags & IEEE80211_CHAN_PASSIVE)
 
+#define	IEEE80211_IS_CHAN_STURBO(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_STURBO)
+#define	IEEE80211_IS_CHAN_DTURBO(_c) \
+	(((_c)->ich_flags & \
+	(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) == IEEE80211_CHAN_TURBO)
+#define	IEEE80211_IS_CHAN_HALF(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_HALF)
+#define	IEEE80211_IS_CHAN_QUARTER(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_QUARTER)
+#define	IEEE80211_IS_CHAN_FULL(_c) \
+	((_c)->ich_flags & (IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HALF))
+#define	IEEE80211_IS_CHAN_GSM(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_GSM)
+
+#define	IEEE80211_IS_CHAN_HT(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_HT)
+#define	IEEE80211_IS_CHAN_HT20(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_HT20)
+#define	IEEE80211_IS_CHAN_HT40(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_HT40)
+#define	IEEE80211_IS_CHAN_HT40U(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_HT40U)
+#define	IEEE80211_IS_CHAN_HT40D(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_HT40D)
+#define	IEEE80211_IS_CHAN_HTA(_c) \
+	(IEEE80211_IS_CHAN_5GHZ(_c) && \
+	((_c)->ich_flags & IEEE80211_CHAN_HT))
+#define	IEEE80211_IS_CHAN_HTG(_c) \
+	(IEEE80211_IS_CHAN_2GHZ(_c) && \
+	((_c)->ich_flags & IEEE80211_CHAN_HT))
+#define	IEEE80211_IS_CHAN_DFS(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_DFS)
+#define	IEEE80211_IS_CHAN_NOADHOC(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_NOADHOC)
+#define	IEEE80211_IS_CHAN_NOHOSTAP(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_NOHOSTAP)
+#define	IEEE80211_IS_CHAN_11D(_c) \
+	((_c)->ich_flags & IEEE80211_CHAN_11D)
+
 /* ni_chan encoding for FH phy */
 #define	IEEE80211_FH_CHANMOD	80
 #define	IEEE80211_FH_CHAN(set, pat)	\
@@ -211,6 +256,8 @@
 #define	IEEE80211_MSG_INACT	0x00000080	/* inactivity handling */
 #define	IEEE80211_MSG_ROAM	0x00000040	/* sta-mode roaming */
 #define	IEEE80211_MSG_CONFIG	0x00000020	/* wificonfig/dladm */
+#define	IEEE80211_MSG_ACTION	0x00000010	/* action frame handling */
+#define	IEEE80211_MSG_HT	0x00000008	/* 11n mode debug */
 #define	IEEE80211_MSG_ANY	0xffffffff	/* anything */
 
 /* Error flags returned by ieee80211_match_bss */
@@ -268,6 +315,8 @@
 	uint8_t			*xrates;
 	uint8_t			*wpa;
 	uint8_t			*wme;
+	uint8_t			*htcap;
+	uint8_t			*htinfo;
 };
 
 #define	IEEE80211_SEND_MGMT(_ic, _in, _type, _arg)			\
@@ -351,7 +400,6 @@
 
 void ieee80211_err(const int8_t *, ...);
 void ieee80211_dbg(uint32_t, const int8_t *, ...);
-int ieee80211_hdrspace(const void *);
 
 void ieee80211_notify(ieee80211com_t *, wpa_event_type);
 void ieee80211_mac_update(ieee80211com_t *);
@@ -374,10 +422,11 @@
 ieee80211_node_t *ieee80211_fakeup_adhoc_node(ieee80211_node_table_t *,
     const uint8_t *);
 ieee80211_node_t *ieee80211_tmp_node(ieee80211com_t *, const uint8_t *);
+void ieee80211_setcurchan(ieee80211com_t *, struct ieee80211_channel *);
 
 /* proto */
 void ieee80211_proto_attach(ieee80211com_t *);
-int ieee80211_fix_rate(ieee80211_node_t *, int);
+int ieee80211_fix_rate(ieee80211_node_t *, struct ieee80211_rateset *, int);
 void ieee80211_setbasicrates(struct ieee80211_rateset *,
     enum ieee80211_phymode);
 void ieee80211_reset_erp(ieee80211com_t *);
@@ -395,6 +444,8 @@
     size_t);
 int ieee80211_send_mgmt(ieee80211com_t *, ieee80211_node_t *, int, int);
 int ieee80211_send_nulldata(ieee80211_node_t *);
+int ieee80211_mgmt_output(ieee80211com_t *, ieee80211_node_t *, mblk_t *,
+    int, int);
 
 /* crypto */
 struct ieee80211_key *ieee80211_crypto_getkey(ieee80211com_t *);
@@ -405,6 +456,10 @@
 void ieee80211_notify_node_join(ieee80211com_t *, ieee80211_node_t *);
 void ieee80211_notify_node_leave(ieee80211com_t *, ieee80211_node_t *);
 
+/* WME */
+void	ieee80211_wme_initparams(struct ieee80211com *);
+void	ieee80211_wme_updateparams(struct ieee80211com *);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/io/net80211/net80211_input.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_input.c	Thu Aug 06 13:05:38 2009 +0800
@@ -71,6 +71,27 @@
 	uint8_t type;
 	uint8_t subtype;
 	uint8_t tid;
+	uint8_t qos;
+
+	if (mp->b_flag & 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.
+		 */
+		IEEE80211_LOCK(ic);
+		wh = (struct ieee80211_frame *)mp->b_rptr;
+		type = IEEE80211_FC0_TYPE_DATA;
+		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+		subtype = IEEE80211_FC0_SUBTYPE_QOS;
+		hdrspace = ieee80211_hdrspace(ic, wh);	/* optimize */
+		/* clear driver/net80211 flags before passing up */
+		mp->b_flag &= ~M_AMPDU;
+		goto resubmit_ampdu;
+	}
 
 	ASSERT(in != NULL);
 	in->in_inact = in->in_inact_reload;
@@ -160,9 +181,18 @@
 		in->in_rssi = (uint8_t)rssi;
 		in->in_rstamp = rstamp;
 		if (!(type & IEEE80211_FC0_TYPE_CTL)) {
-			tid = 0;
+			if (IEEE80211_QOS_HAS_SEQ(wh)) {
+				tid = ((struct ieee80211_qosframe *)wh)->
+				    i_qos[0] & IEEE80211_QOS_TID;
+				if (TID_TO_WME_AC(tid) >= WME_AC_VI)
+					ic->ic_wme.wme_hipri_traffic++;
+				tid++;
+			} else {
+				tid = IEEE80211_NONQOS_TID;
+			}
 			rxseq = LE_16(*(uint16_t *)wh->i_seq);
-			if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+			if ((in->in_flags & IEEE80211_NODE_HT) == 0 &&
+			    (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
 			    (rxseq - in->in_rxseqs[tid]) <= 0) {
 				/* duplicate, discard */
 				ieee80211_dbg(IEEE80211_MSG_INPUT,
@@ -184,7 +214,7 @@
 
 	switch (type) {
 	case IEEE80211_FC0_TYPE_DATA:
-		hdrspace = ieee80211_hdrspace(wh);
+		hdrspace = ieee80211_hdrspace(ic, wh);
 		if (len < hdrspace) {
 			ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
 			    "data too short: expecting %u", hdrspace);
@@ -228,6 +258,26 @@
 		}
 
 		/*
+		 * 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 ((in->in_flags & IEEE80211_NODE_HT) &&
+		    (subtype == IEEE80211_FC0_SUBTYPE_QOS)) {
+			IEEE80211_UNLOCK(ic);
+			if (ieee80211_ampdu_reorder(in, mp) != 0) {
+				mp = NULL;	/* CONSUMED */
+				goto out;
+			}
+			IEEE80211_LOCK(ic);
+		}
+	resubmit_ampdu:
+
+		/*
 		 * Handle privacy requirements.
 		 */
 		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
@@ -253,6 +303,17 @@
 		}
 
 		/*
+		 * 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)) {
@@ -273,6 +334,15 @@
 			goto out_exit_mutex;
 		}
 
+		if (qos & IEEE80211_QOS_AMSDU) {
+			ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
+			    "ieee80211_input: QOS_AMSDU (%x)\n", qos);
+
+			mp = ieee80211_decap_amsdu(in, mp);
+			if (mp == NULL)		/* MSDU processed by HT */
+				goto out_exit_mutex;
+		}
+
 		ic->ic_stats.is_rx_frags++;
 		ic->ic_stats.is_rx_bytes += len;
 		IEEE80211_UNLOCK(ic);
@@ -307,7 +377,7 @@
 				ic->ic_stats.is_wep_errors++;
 				goto out_exit_mutex;
 			}
-			hdrspace = ieee80211_hdrspace(wh);
+			hdrspace = ieee80211_hdrspace(ic, wh);
 			key = ieee80211_crypto_decap(ic, mp, hdrspace);
 			if (key == NULL) {
 				/* NB: stats+msgs handled in crypto_decap */
@@ -321,6 +391,15 @@
 		goto out;
 
 	case IEEE80211_FC0_TYPE_CTL:
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			switch (subtype) {
+			case IEEE80211_FC0_SUBTYPE_BAR:
+				ieee80211_recv_bar(in, mp);
+				break;
+			}
+		}
+		break;
+
 	default:
 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
 		    "bad frame type 0x%x", type);
@@ -464,7 +543,7 @@
 		bcopy(xrates + 2, rs->ir_rates + rs->ir_nrates, nxrates);
 		rs->ir_nrates += nxrates;
 	}
-	return (ieee80211_fix_rate(in, flags));
+	return (ieee80211_fix_rate(in, &in->in_rates, flags));
 }
 
 /*
@@ -654,6 +733,89 @@
 	return (frm[1] > 3 && LE_32(c) == ((WPA_OUI_TYPE << 24) | WPA_OUI));
 }
 
+#define	LE_READ_4(p)							\
+	((uint32_t)							\
+	((((uint8_t *)(p))[0]) | (((uint8_t *)(p))[1] <<  8) |		\
+	(((uint8_t *)(p))[2] << 16) | (((uint8_t *)(p))[3] << 24)))
+
+#define	LE_READ_2(p)							\
+	((uint16_t)							\
+	(((uint8_t *)(p))[0]) | (((uint8_t *)(p))[1] <<  8))
+
+static int
+iswmeoui(const uint8_t *frm)
+{
+	return (frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI));
+}
+
+static 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
+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
+ishtcapoui(const uint8_t *frm)
+{
+	return (frm[1] > 3 &&
+	    LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI));
+}
+
+static int
+ishtinfooui(const uint8_t *frm)
+{
+	return (frm[1] > 3 &&
+	    LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI));
+}
+
+/* ARGSUSED */
+static int
+ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm,
+	const struct ieee80211_frame *wh)
+{
+#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	uint_t len = frm[1];
+	uint8_t qosinfo;
+	int i;
+
+	if (len < sizeof (struct ieee80211_wme_param) - 2) {
+		ieee80211_dbg(IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
+		    "WME too short, len %u", len);
+		return (-1);
+	}
+	qosinfo = frm[offsetof(struct ieee80211_wme_param, wme_qosInfo)];
+	qosinfo &= WME_QOSINFO_COUNT;
+	/* do proper check for wraparound */
+	if (qosinfo == wme->wme_wmeChanParams.cap_info)
+		return (0);
+	frm += offsetof(struct ieee80211_wme_param, wme_acParams);
+	for (i = 0; i < WME_NUM_AC; i++) {
+		struct wmeParams *wmep =
+		    &wme->wme_wmeChanParams.cap_wmeParams[i];
+		/* NB: ACI not used */
+		wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
+		wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
+		wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
+		wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
+		wmep->wmep_txopLimit = LE_READ_2(frm+2);
+		frm += 4;
+	}
+	wme->wme_wmeChanParams.cap_info = qosinfo;
+	return (1);
+#undef MS
+}
+
 /*
  * Process a beacon/probe response frame.
  * When the device is in station mode, create a node and add it
@@ -701,6 +863,8 @@
 	 *	[tlv] extended supported rates
 	 *	[tlv] WME
 	 *	[tlv] WPA or RSN
+	 *	[tlv] HT capabilities
+	 *	[tlv] HT information
 	 */
 	IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
 	    IEEE80211_BEACON_ELEM_MIN, return);
@@ -769,12 +933,33 @@
 			scan.erp = frm[2];
 			scan.phytype = IEEE80211_T_OFDM;
 			break;
+		case IEEE80211_ELEMID_HTCAP:
+			scan.htcap = frm;
+			break;
 		case IEEE80211_ELEMID_RSN:
 			scan.wpa = frm;
 			break;
+		case IEEE80211_ELEMID_HTINFO:
+			scan.htinfo = frm;
+			break;
 		case IEEE80211_ELEMID_VENDOR:
 			if (iswpaoui(frm))
 				scan.wpa = frm;		/* IEEE802.11i D3.0 */
+			else if (iswmeparam(frm) || iswmeinfo(frm))
+				scan.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 (scan.htcap == NULL)
+						scan.htcap = frm;
+				} else if (ishtinfooui(frm)) {
+					if (scan.htinfo == NULL)
+						scan.htinfo = frm;
+				}
+			}
 			break;
 		default:
 			ieee80211_dbg(IEEE80211_MSG_ELEMID,
@@ -824,6 +1009,25 @@
 		    IEEE80211_SUBTYPE_NAME(subtype), scan.bintval);
 		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);
+	}
 
 	/*
 	 * When operating in station mode, check for state updates.
@@ -858,7 +1062,24 @@
 			    IEEE80211_CAPINFO_SHORT_SLOTTIME));
 			in->in_capinfo = scan.capinfo;
 		}
-
+		if (scan.wme != NULL &&
+		    (in->in_flags & IEEE80211_NODE_QOS) &&
+		    ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0) {
+			ieee80211_wme_updateparams(ic);
+		}
+		if (scan.htcap != NULL)
+			ieee80211_parse_htcap(in, scan.htcap);
+		if (scan.htinfo != NULL) {
+			ieee80211_parse_htinfo(in, scan.htinfo);
+			if (in->in_chan != ic->ic_curchan) {
+				/*
+				 * Channel has been adjusted based on
+				 * negotiated HT parameters; force the
+				 * channel state to follow.
+				 */
+				ieee80211_setcurchan(ic, in->in_chan);
+			}
+		}
 		if (scan.tim != NULL) {
 			struct ieee80211_tim_ie *ie;
 
@@ -918,6 +1139,8 @@
 	uint8_t *ssid;
 	uint8_t *rates;
 	uint8_t *xrates;	/* extended rates */
+	uint8_t	*wme;
+	uint8_t *htcap, *htinfo;
 	boolean_t allocbs = B_FALSE;
 	uint8_t rate;
 	uint16_t algo;		/* authentication algorithm */
@@ -925,6 +1148,7 @@
 	uint16_t status;
 	uint16_t capinfo;
 	uint16_t associd;	/* association ID */
+	const struct ieee80211_action *ia;
 
 	IEEE80211_LOCK(ic);
 	wh = (struct ieee80211_frame *)mp->b_rptr;
@@ -1086,6 +1310,8 @@
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
 		 *	[tlv] WME
+		 *	[tlv] HT capabilities
+		 *	[tlv] HT info
 		 */
 		IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
 		    IEEE80211_ASSOC_RESP_ELEM_MIN, break);
@@ -1107,7 +1333,7 @@
 		associd = LE_16(*(uint16_t *)frm);
 		frm += 2;
 
-		rates = xrates = NULL;
+		rates = xrates = wme = htcap = htinfo = NULL;
 		while (frm < efrm) {
 			/*
 			 * Do not discard frames containing proprietary Agere
@@ -1131,6 +1357,30 @@
 			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)
+							htinfo = frm;
+					}
+				}
+				break;
 			}
 			frm += frm[1] + 2;
 		}
@@ -1157,7 +1407,32 @@
 
 		in->in_capinfo = capinfo;
 		in->in_associd = associd;
-		in->in_flags &= ~IEEE80211_NODE_QOS;
+		if (wme != NULL &&
+		    ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
+			in->in_flags |= IEEE80211_NODE_QOS;
+			ieee80211_wme_updateparams(ic);
+		} else {
+			in->in_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(in, htcap);
+			ieee80211_parse_htinfo(in, htinfo);
+			(void) ieee80211_setup_htrates(in,
+			    htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+			ieee80211_setup_basic_htrates(in, htinfo);
+			if (in->in_chan != ic->ic_curchan) {
+				/*
+				 * Channel has been adjusted based on
+				 * negotiated HT parameters; force the
+				 * channel state to follow.
+				 */
+				ieee80211_setcurchan(ic, in->in_chan);
+			}
+		}
 		/*
 		 * Configure state now that we are associated.
 		 */
@@ -1242,6 +1517,57 @@
 		}
 		break;
 
+	case IEEE80211_FC0_SUBTYPE_ACTION:
+		if (ic->ic_state != IEEE80211_S_RUN &&
+		    ic->ic_state != IEEE80211_S_ASSOC &&
+		    ic->ic_state != IEEE80211_S_AUTH)
+			break;
+
+		/*
+		 * action frame format:
+		 *	[1] category
+		 *	[1] action
+		 *	[tlv] parameters
+		 */
+		IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+		    sizeof (struct ieee80211_action), break);
+		ia = (const struct ieee80211_action *) frm;
+
+		/* verify frame payloads but defer processing */
+		/* 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(_PTRDIFF(efrm, frm),
+			    sizeof (struct ieee80211_action_ba_addbarequest),
+			    break);
+			break;
+			case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+			IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+			    sizeof (struct ieee80211_action_ba_addbaresponse),
+			    break);
+			break;
+			case IEEE80211_ACTION_BA_DELBA:
+			IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+			    sizeof (struct ieee80211_action_ba_delba),
+			    break);
+			break;
+			}
+			break;
+		case IEEE80211_ACTION_CAT_HT:
+			switch (ia->ia_action) {
+			case IEEE80211_ACTION_HT_TXCHWIDTH:
+			IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+			    sizeof (struct ieee80211_action_ht_txchwidth),
+			    break);
+			break;
+			}
+			break;
+		}
+		ic->ic_recv_action(in, frm, efrm);
+		break;
+
 	default:
 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_recv_mgmt: "
 		    "subtype 0x%x not handled\n", subtype);
--- a/usr/src/uts/common/io/net80211/net80211_ioctl.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_ioctl.c	Thu Aug 06 13:05:38 2009 +0800
@@ -719,6 +719,7 @@
 }
 
 #define	WIFI_HAVE_CAP(in, flag)	(((in)->in_capinfo & (flag)) ? 1 : 0)
+#define	WIFI_HAVE_HTCAP(in)	(((in)->in_htcap != 0) ? 1 : 0)
 
 /*
  * Callback function used by ieee80211_iterate_nodes() in
@@ -768,6 +769,7 @@
 		    (wl_ofdm_t *)&((conf->wl_phy_conf).wl_phy_ofdm_conf);
 		ofdm->wl_ofdm_subtype = WL_OFDM;
 		ofdm->wl_ofdm_frequency = chan->ich_freq;
+		ofdm->wl_ofdm_ht_enabled = WIFI_HAVE_HTCAP(in);
 	} else {
 		switch (in->in_phytype) {
 		case IEEE80211_T_FH: {
@@ -809,6 +811,7 @@
 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_DSSSOFDM);
 			erp->wl_erp_sst_enabled = WIFI_HAVE_CAP(in,
 			    IEEE80211_CAPINFO_SHORT_SLOTTIME);
+			erp->wl_erp_ht_enabled = WIFI_HAVE_HTCAP(in);
 			break;
 		} /* case IEEE80211_T_OFDM */
 		} /* switch in->in_phytype */
@@ -1863,12 +1866,15 @@
 	return (err);
 }
 
+#define	WIFI_HT_MODE(in)	(((in)->in_flags & IEEE80211_NODE_HT) ? 1 : 0)
+
 static int
 wl_get_phy(struct ieee80211com *ic, void *wldp_buf)
 {
 	int err = 0;
 	wl_phy_conf_t *ow_phy;
 	struct ieee80211_channel *ch = ic->ic_curchan;
+	struct ieee80211_node *in = ic->ic_bss;
 
 	ow_phy = (wl_phy_conf_t *)wldp_buf;
 	bzero(wldp_buf, sizeof (wl_phy_conf_t));
@@ -1878,6 +1884,7 @@
 		wl_ofdm_t *ofdm = (wl_ofdm_t *)ow_phy;
 		ofdm->wl_ofdm_subtype = WL_OFDM;
 		ofdm->wl_ofdm_frequency = ch->ich_freq;
+		ofdm->wl_ofdm_ht_enabled = WIFI_HT_MODE(in);
 	} else {
 		switch (ic->ic_phytype) {
 		case IEEE80211_T_FH: {
@@ -1899,6 +1906,7 @@
 			erp->wl_erp_subtype = WL_ERP;
 			erp->wl_erp_channel =
 			    ieee80211_chan2ieee(ic, ch);
+			erp->wl_erp_ht_enabled = WIFI_HT_MODE(in);
 			break;
 		}
 		default:
--- a/usr/src/uts/common/io/net80211/net80211_node.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_node.c	Thu Aug 06 13:05:38 2009 +0800
@@ -184,7 +184,18 @@
 	if (chan == IEEE80211_CHAN_ANYC)
 		chan = ic->ic_curchan;
 	in->in_chan = chan;
-	in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
+	if (IEEE80211_IS_CHAN_HT(chan)) {
+		/*
+		 * 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.
+		 */
+		in->in_htrates = *ieee80211_get_suphtrates(ic, chan);
+	}
+	in->in_rates = *ieee80211_get_suprates(ic, chan);
+	/* in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; */
 }
 
 /*
@@ -434,7 +445,8 @@
 		if (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY)
 			fail |= IEEE80211_BADPRIVACY;
 	}
-	rate = ieee80211_fix_rate(in, IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
+	rate = ieee80211_fix_rate(in, &in->in_rates,
+	    IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
 	if (rate & IEEE80211_RATE_BASIC)
 		fail |= IEEE80211_BADRATE;
 	if (ic->ic_des_esslen != 0 &&
@@ -660,6 +672,18 @@
 }
 
 /*
+ * Change the bss channel.
+ */
+void
+ieee80211_setcurchan(ieee80211com_t *ic, struct ieee80211_channel *c)
+{
+	ic->ic_curchan = c;
+	ic->ic_curmode = ieee80211_chan2mode(ic, ic->ic_curchan);
+	if (ic->ic_set_channel != NULL)
+		ic->ic_set_channel(ic);
+}
+
+/*
  * Join the specified IBSS/BSS network.  The node is assumed to
  * be passed in with a held reference.
  */
@@ -677,7 +701,8 @@
 		 * Delete unusable rates; we've already checked
 		 * that the negotiated rate set is acceptable.
 		 */
-		(void) ieee80211_fix_rate(selbs, IEEE80211_F_DODEL);
+		(void) ieee80211_fix_rate(selbs, &selbs->in_rates,
+		    IEEE80211_F_DODEL);
 		/*
 		 * Fillin the neighbor table
 		 */
@@ -706,6 +731,7 @@
 	 * mode is locked.
 	 */
 	ieee80211_reset_erp(ic);
+	ieee80211_wme_initparams(ic);
 
 	IEEE80211_UNLOCK(ic);
 	if (ic->ic_opmode == IEEE80211_M_STA)
@@ -773,6 +799,10 @@
 	ic->ic_node_cleanup(in);
 	if (in->in_wpa_ie != NULL)
 		ieee80211_free(in->in_wpa_ie);
+	if (in->in_wme_ie != NULL)
+		ieee80211_free(in->in_wme_ie);
+	if (in->in_htcap_ie != NULL)
+		ieee80211_free(in->in_htcap_ie);
 	kmem_free(in, sizeof (ieee80211_node_t));
 }
 
@@ -1108,7 +1138,12 @@
 	 * Record optional information elements that might be
 	 * used by applications or drivers.
 	 */
+	saveie(&in->in_wme_ie, sp->wme);
 	saveie(&in->in_wpa_ie, sp->wpa);
+	saveie(&in->in_htcap_ie, sp->htcap);
+	/* parsed in ieee80211_sta_join() */
+	if (sp->htcap != NULL)
+		ieee80211_parse_htcap(in, in->in_htcap_ie);
 
 	/* NB: must be after in_chan is setup */
 	(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
@@ -1137,6 +1172,8 @@
 	in->in_fhindex = sp->fhindex;
 	in->in_erp = sp->erp;
 	in->in_tim_off = sp->timoff;
+	if (sp->wme != NULL)
+		ieee80211_saveie(&in->in_wme_ie, sp->wme);
 
 	/* NB: must be after in_chan is setup */
 	(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
@@ -1164,9 +1201,17 @@
 	return (in);
 }
 
-#define	IEEE80211_IS_CTL(wh) \
+#define	IEEE80211_IS_CTL(wh)	\
 	((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
 
+#define	IEEE80211_IS_PSPOLL(wh)	\
+	((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==	\
+	    IEEE80211_FC0_SUBTYPE_PS_POLL)
+
+#define	IEEE80211_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
@@ -1190,7 +1235,8 @@
 	}
 
 	IEEE80211_NODE_LOCK(nt);
-	if (IEEE80211_IS_CTL(wh))
+	if (IEEE80211_IS_CTL(wh) &&
+	    !IEEE80211_IS_PSPOLL(wh) && !IEEE80211_IS_BAR(wh))
 		in = ieee80211_find_node_locked(nt, wh->i_addr1);
 	else
 		in = ieee80211_find_node_locked(nt, wh->i_addr2);
@@ -1202,6 +1248,10 @@
 	return (in);
 }
 
+#undef IEEE80211_IS_BAR
+#undef IEEE80211_IS_PSPOLL
+#undef IEEE80211_IS_CTL
+
 /*
  * Return a reference to the appropriate node for sending
  * a data frame.  This handles node discovery in adhoc networks.
--- a/usr/src/uts/common/io/net80211/net80211_output.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_output.c	Thu Aug 06 13:05:38 2009 +0800
@@ -82,9 +82,11 @@
 		IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
 	}
 	*(uint16_t *)&wh->i_dur[0] = 0;	/* set duration */
-	*(uint16_t *)&wh->i_seq[0] =	/* set sequence number */
-	    LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
-	in->in_txseqs[0]++;		/* increase sequence number by 1 */
+	/* NB: use non-QoS tid */
+	*(uint16_t *)&wh->i_seq[0] =
+	    LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
+	    IEEE80211_SEQ_SEQ_SHIFT);
+	in->in_txseqs[IEEE80211_NONQOS_TID]++;
 }
 
 /*
@@ -96,7 +98,7 @@
  *
  * Return 0 on success
  */
-static int
+int
 ieee80211_mgmt_output(ieee80211com_t *ic, ieee80211_node_t *in, mblk_t *mp,
     int type, int timer)
 {
@@ -173,13 +175,70 @@
 {
 	struct ieee80211_frame	*wh;
 	struct ieee80211_key *key;
+	int addqos, ac, tid;
 
 	ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
+	/*
+	 * Some ap's don't handle QoS-encapsulated EAPOL
+	 * frames so suppress use.  This may be an issue if other
+	 * ap's require all data frames to be QoS-encapsulated
+	 * once negotiated in which case we'll need to make this
+	 * configurable.
+	 */
+	addqos = in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT);
 	wh = (struct ieee80211_frame *)mp->b_rptr;
 	*(uint16_t *)wh->i_dur = 0;
-	*(uint16_t *)wh->i_seq =
-	    LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
-	in->in_txseqs[0]++;
+	if (addqos) {
+		struct ieee80211_qosframe *qwh =
+		    (struct ieee80211_qosframe *)wh;
+
+		ac = ieee80211_classify(ic, mp, in);
+		/* 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 ((in->in_flags & IEEE80211_NODE_AMPDU_TX) &&
+		    (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
+			struct ieee80211_tx_ampdu *tap = &in->in_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.
+				 */
+				(void) ieee80211_ampdu_request(in, tap);
+			}
+		}
+		/* works even when BA marked above */
+		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].
+		    wmep_noackPolicy) {
+			qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
+		}
+
+		*(uint16_t *)wh->i_seq =
+		    LE_16(in->in_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+		in->in_txseqs[tid]++;
+	} else {
+		*(uint16_t *)wh->i_seq =
+		    LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
+		    IEEE80211_SEQ_SEQ_SHIFT);
+		in->in_txseqs[IEEE80211_NONQOS_TID]++;
+	}
 
 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
 		key = ieee80211_crypto_getkey(ic);
@@ -192,9 +251,8 @@
 	 */
 	if (key != NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
-		if (!ieee80211_crypto_enmic(isc, key, mp, 0)) {
+		if (!ieee80211_crypto_enmic(isc, key, mp, 0))
 			ieee80211_err("ieee80211_crypto_enmic failed.\n");
-		}
 	}
 
 	return (mp);
@@ -234,6 +292,72 @@
 	return (frm);
 }
 
+#define	WME_OUI_BYTES		0x00, 0x50, 0xf2
+/*
+ * Add a WME information element to a frame.
+ */
+/* ARGSUSED */
+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,
+		.wme_len	= sizeof (struct ieee80211_wme_info) - 2,
+		.wme_oui	= { WME_OUI_BYTES },
+		.wme_type	= WME_OUI_TYPE,
+		.wme_subtype	= WME_INFO_OUI_SUBTYPE,
+		.wme_version	= WME_VERSION,
+		.wme_info	= 0,
+	};
+	(void) memcpy(frm, &info, sizeof (info));
+	return (frm + sizeof (info));
+}
+
+/*
+ * Add a WME parameters element to a frame.
+ */
+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 {			\
+	_NOTE(CONSTCOND)			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+	_NOTE(CONSTCOND)			\
+} while (0)
+	/* NB: this works 'cuz a param has an info at the front */
+	static const struct ieee80211_wme_info param = {
+		.wme_id		= IEEE80211_ELEMID_VENDOR,
+		.wme_len	= sizeof (struct ieee80211_wme_param) - 2,
+		.wme_oui	= { WME_OUI_BYTES },
+		.wme_type	= WME_OUI_TYPE,
+		.wme_subtype	= WME_PARAM_OUI_SUBTYPE,
+		.wme_version	= WME_VERSION,
+	};
+	int i;
+
+	(void) memcpy(frm, &param, sizeof (param));
+	frm += offsetof(struct ieee80211_wme_info, wme_info);
+	*frm++ = wme->wme_bssChanParams.cap_info;	/* AC info */
+	*frm++ = 0;					/* reserved field */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		const struct wmeParams *ac =
+		    &wme->wme_bssChanParams.cap_wmeParams[i];
+		*frm++ = SM(i, WME_PARAM_ACI)
+		    | SM(ac->wmep_acm, WME_PARAM_ACM)
+		    | SM(ac->wmep_aifsn, WME_PARAM_AIFSN);
+		*frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
+		    | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN);
+		ADDSHORT(frm, ac->wmep_txopLimit);
+	}
+	return (frm);
+#undef SM
+#undef ADDSHORT
+}
+#undef WME_OUI_BYTES
+
 /*
  * Add SSID element to a frame
  */
@@ -379,6 +503,10 @@
 		 *	[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)
 		 */
 		mp = ieee80211_getmgtframe(&frm,
 		    8			/* time stamp  */
@@ -394,8 +522,12 @@
 		    2 * sizeof (struct ieee80211_ie_wpa) : 0)
 					/* [tlv] WPA  */
 		    + (ic->ic_flags & IEEE80211_F_WME ?
-		    sizeof (struct ieee80211_wme_param) : 0));
+		    sizeof (struct ieee80211_wme_param) : 0)
 					/* [tlv] WME  */
+		    /* check for cluster requirement */
+		    + 2 * sizeof (struct ieee80211_ie_htcap) + 4
+		    + 2 * sizeof (struct ieee80211_ie_htinfo) + 4);
+
 		if (mp == NULL)
 			return (ENOMEM);
 
@@ -434,12 +566,30 @@
 			*frm++ = IEEE80211_IBSS_LEN;
 			*frm++ = 0; *frm++ = 0;		/* ATIM window */
 		}
-		/* ERP */
 		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
 			frm = ieee80211_add_erp(frm, ic);
-		/* Extended supported rates */
 		frm = ieee80211_add_xrates(frm, &in->in_rates);
-		mp->b_wptr = frm;
+		/*
+		 * 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, in);
+			frm = ieee80211_add_htinfo(frm, in);
+		}
+		if (ic->ic_flags & IEEE80211_F_WME)
+			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+		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, in);
+			frm = ieee80211_add_htinfo_vendor(frm, in);
+		}
+		mp->b_wptr = frm;	/* allocated is greater than used */
+
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_AUTH:
@@ -517,6 +667,8 @@
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
 		 *	[tlv] WME
+		 *	[tlv] HT capabilities
+		 *	[tlv] Vendor OUI HT capabilities (optional)
 		 *	[tlv] user-specified ie's
 		 */
 		mp = ieee80211_getmgtframe(&frm,
@@ -525,6 +677,8 @@
 		    + 2 + IEEE80211_NWID_LEN
 		    + 2 + IEEE80211_RATE_SIZE
 		    + 2 + IEEE80211_XRATE_SIZE
+		    + sizeof (struct ieee80211_wme_info)
+		    + 2 * sizeof (struct ieee80211_ie_htcap) + 4
 		    + ic->ic_opt_ie_len);
 		if (mp == NULL)
 			return (ENOMEM);
@@ -556,6 +710,16 @@
 		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
 		frm = ieee80211_add_rates(frm, &in->in_rates);
 		frm = ieee80211_add_xrates(frm, &in->in_rates);
+		if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
+		    in->in_htcap_ie != NULL &&
+		    in->in_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+			frm = ieee80211_add_htcap(frm, in);
+		if ((ic->ic_flags & IEEE80211_F_WME) && in->in_wme_ie != NULL)
+			frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+		if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
+		    in->in_htcap_ie != NULL &&
+		    in->in_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+			frm = ieee80211_add_htcap_vendor(frm, in);
 		if (ic->ic_opt_ie != NULL) {
 			bcopy(ic->ic_opt_ie, frm, ic->ic_opt_ie_len);
 			frm += ic->ic_opt_ie_len;
@@ -575,6 +739,8 @@
 		 *	[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)
 		 */
 		mp = ieee80211_getmgtframe(&frm,
 		    3 * sizeof (uint16_t)
@@ -629,7 +795,6 @@
 	struct ieee80211_rateset *rs;
 	mblk_t *m;
 	uint8_t *frm;
-	uint8_t *efrm;
 	int pktlen;
 	uint16_t capinfo;
 
@@ -647,6 +812,10 @@
 	 *	[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)
 	 * Vendor-specific OIDs (e.g. Atheros)
 	 * NB: we allocate the max space required for the TIM bitmap.
 	 */
@@ -659,7 +828,13 @@
 	    + 2 + 1			/* DS parameters */
 	    + 2 + 4 + ic->ic_tim_len	/* DTIM/IBSSPARMS */
 	    + 2 + 1			/* ERP */
-	    + 2 + IEEE80211_XRATE_SIZE;
+	    + 2 + IEEE80211_XRATE_SIZE
+	    + (ic->ic_caps & IEEE80211_C_WME ?	/* WME */
+	    sizeof (struct ieee80211_wme_param) : 0)
+	    /* conditional? */
+	    + 4 + 2 * sizeof (struct ieee80211_ie_htcap)	/* HT caps */
+	    + 4 + 2 * sizeof (struct ieee80211_ie_htinfo);	/* HT info */
+
 	m = ieee80211_getmgtframe(&frm, pktlen);
 	if (m == NULL) {
 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_beacon_alloc: "
@@ -716,9 +891,23 @@
 		bo->bo_erp = frm;
 		frm = ieee80211_add_erp(frm, ic);
 	}
-	efrm = ieee80211_add_xrates(frm, rs);
-	bo->bo_trailer_len = _PTRDIFF(efrm, bo->bo_trailer);
-	m->b_wptr = efrm;
+	frm = ieee80211_add_xrates(frm, rs);
+	if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
+		frm = ieee80211_add_htcap(frm, in);
+		bo->bo_htinfo = frm;
+		frm = ieee80211_add_htinfo(frm, in);
+	}
+	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_curchan) &&
+	    (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
+		frm = ieee80211_add_htcap_vendor(frm, in);
+		frm = ieee80211_add_htinfo_vendor(frm, in);
+	}
+	bo->bo_trailer_len = _PTRDIFF(frm, bo->bo_trailer);
+	m->b_wptr = frm;
 
 	wh = (struct ieee80211_frame *)m->b_rptr;
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
@@ -752,3 +941,44 @@
 	IEEE80211_UNLOCK(ic);
 	return (0);
 }
+
+/*
+ * Assign priority to a frame based on any vlan tag assigned
+ * to the station and/or any Diffserv setting in an IP header.
+ * Finally, if an ACM policy is setup (in station mode) it's
+ * applied.
+ */
+int
+ieee80211_classify(struct ieee80211com *ic, mblk_t *m,
+    struct ieee80211_node *ni)
+/* ARGSUSED */
+{
+	int ac;
+
+	if ((ni->in_flags & IEEE80211_NODE_QOS) == 0)
+		return (WME_AC_BE);
+
+	/* Process VLan */
+	/* Process IPQoS */
+
+	ac = WME_AC_BE;
+
+	/*
+	 * Apply ACM policy.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		static const int acmap[4] = {
+			WME_AC_BK,	/* WME_AC_BE */
+			WME_AC_BK,	/* WME_AC_BK */
+			WME_AC_BE,	/* WME_AC_VI */
+			WME_AC_VI,	/* WME_AC_VO */
+		};
+		while (ac != WME_AC_BK &&
+		    ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].
+		    wmep_acm) {
+			ac = acmap[ac];
+		}
+	}
+
+	return (ac);
+}
--- a/usr/src/uts/common/io/net80211/net80211_proto.c	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_proto.c	Thu Aug 06 13:05:38 2009 +0800
@@ -41,6 +41,12 @@
 
 #include "net80211_impl.h"
 
+/* tunables */
+#define	AGGRESSIVE_MODE_SWITCH_HYSTERESIS	3	/* pkts / 100ms */
+#define	HIGH_PRI_SWITCH_THRESH			10	/* pkts / 100ms */
+
+#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
+
 const char *ieee80211_mgt_subtype_name[] = {
 	"assoc_req",	"assoc_resp",	"reassoc_req",	"reassoc_resp",
 	"probe_req",	"probe_resp",	"reserved#6",	"reserved#7",
@@ -60,6 +66,13 @@
 	"ASSOC",	/* IEEE80211_S_ASSOC */
 	"RUN"		/* IEEE80211_S_RUN */
 };
+const char *ieee80211_wme_acnames[] = {
+	"WME_AC_BE",
+	"WME_AC_BK",
+	"WME_AC_VI",
+	"WME_AC_VO",
+	"WME_UPSD",
+};
 
 static int ieee80211_newstate(ieee80211com_t *, enum ieee80211_state, int);
 
@@ -75,10 +88,12 @@
 	ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
 	ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
 	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
-	ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
 	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
 	im->im_bmiss_max = IEEE80211_BMISS_MAX;
 
+	ic->ic_wme.wme_hipri_switch_hysteresis =
+	    AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
+
 	/* protocol state change handler */
 	ic->ic_newstate = ieee80211_newstate;
 
@@ -210,11 +225,11 @@
  * The highest bit of returned rate value is set to 1 on failure.
  */
 int
-ieee80211_fix_rate(ieee80211_node_t *in, int flags)
+ieee80211_fix_rate(ieee80211_node_t *in,
+    struct ieee80211_rateset *nrs, int flags)
 {
 	ieee80211com_t *ic = in->in_ic;
 	struct ieee80211_rateset *srs;
-	struct ieee80211_rateset *nrs;
 	boolean_t ignore;
 	int i;
 	int okrate;
@@ -235,7 +250,6 @@
 	}
 	okrate = badrate = fixedrate = 0;
 	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, in->in_chan)];
-	nrs = &in->in_rates;
 	for (i = 0; i < nrs->ir_nrates; ) {
 		int j;
 
@@ -393,7 +407,12 @@
 		{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G mixed b/g */
 		{ 0 },			/* IEEE80211_MODE_FH */
 		{ 3, { 12, 24, 48 } },	/* IEEE80211_MODE_TURBO_A */
-		{ 4, { 2, 4, 11, 22 } }	/* IEEE80211_MODE_TURBO_G (mixed b/g) */
+		{ 4, { 2, 4, 11, 22 } },
+					/* IEEE80211_MODE_TURBO_G (mixed b/g) */
+		{ 0 },			/* IEEE80211_MODE_STURBO_A */
+		{ 3, { 12, 24, 48 } },	/* IEEE80211_MODE_11NA */
+					/* IEEE80211_MODE_11NG (mixed b/g) */
+		{ 7, { 2, 4, 11, 22, 12, 24, 48 } }
 	};
 	int i, j;
 
@@ -410,6 +429,301 @@
 }
 
 /*
+ * WME protocol support.  The following parameters come from the spec.
+ */
+typedef struct phyParamType {
+	uint8_t aifsn;
+	uint8_t logcwmin;
+	uint8_t logcwmax;
+	uint16_t txopLimit;
+	uint8_t acm;
+} paramType;
+
+static const paramType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
+	{ 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,  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, 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, 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,  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, 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, 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
+ieee80211_wme_initparams(struct ieee80211com *ic)
+{
+	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_curchan != IEEE80211_CHAN_ANYC)
+		mode = ieee80211_chan2mode(ic, ic->ic_curchan);
+	else
+		mode = IEEE80211_MODE_AUTO;
+	for (i = 0; i < WME_NUM_AC; i++) {
+		switch (i) {
+		case WME_AC_BK:
+			pPhyParam = &phyParamForAC_BK[mode];
+			pBssPhyParam = &phyParamForAC_BK[mode];
+			break;
+		case WME_AC_VI:
+			pPhyParam = &phyParamForAC_VI[mode];
+			pBssPhyParam = &bssPhyParamForAC_VI[mode];
+			break;
+		case WME_AC_VO:
+			pPhyParam = &phyParamForAC_VO[mode];
+			pBssPhyParam = &bssPhyParamForAC_VO[mode];
+			break;
+		case WME_AC_BE:
+		default:
+			pPhyParam = &phyParamForAC_BE[mode];
+			pBssPhyParam = &bssPhyParamForAC_BE[mode];
+			break;
+		}
+
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			wmep->wmep_acm = pPhyParam->acm;
+			wmep->wmep_aifsn = pPhyParam->aifsn;
+			wmep->wmep_logcwmin = pPhyParam->logcwmin;
+			wmep->wmep_logcwmax = pPhyParam->logcwmax;
+			wmep->wmep_txopLimit = pPhyParam->txopLimit;
+		} else {
+			wmep->wmep_acm = pBssPhyParam->acm;
+			wmep->wmep_aifsn = pBssPhyParam->aifsn;
+			wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
+			wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
+			wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+
+		}
+		ieee80211_dbg(IEEE80211_MSG_WME, "ieee80211_wme_initparams: "
+		    "%s chan [acm %u aifsn %u log2(cwmin) %u "
+		    "log2(cwmax) %u txpoLimit %u]\n",
+		    ieee80211_wme_acnames[i],
+		    wmep->wmep_acm,
+		    wmep->wmep_aifsn,
+		    wmep->wmep_logcwmin,
+		    wmep->wmep_logcwmax,
+		    wmep->wmep_txopLimit);
+
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+		wmep->wmep_acm = pBssPhyParam->acm;
+		wmep->wmep_aifsn = pBssPhyParam->aifsn;
+		wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
+		wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
+		wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+		ieee80211_dbg(IEEE80211_MSG_WME, "ieee80211_wme_initparams: "
+		    "%s  bss [acm %u aifsn %u log2(cwmin) %u "
+		    "log2(cwmax) %u txpoLimit %u]\n",
+		    ieee80211_wme_acnames[i],
+		    wmep->wmep_acm,
+		    wmep->wmep_aifsn,
+		    wmep->wmep_logcwmin,
+		    wmep->wmep_logcwmax,
+		    wmep->wmep_txopLimit);
+	}
+	/* NB: check ic_bss to avoid NULL deref on initial attach */
+	if (ic->ic_bss != NULL) {
+		/*
+		 * Calculate agressive mode switching threshold based
+		 * on beacon interval.  This doesn't need locking since
+		 * we're only called before entering the RUN state at
+		 * which point we start sending beacon frames.
+		 */
+		wme->wme_hipri_switch_thresh =
+		    (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->in_intval) / 100;
+		ieee80211_wme_updateparams(ic);
+	}
+}
+
+/*
+ * Update WME parameters for ourself and the BSS.
+ */
+void
+ieee80211_wme_updateparams(struct ieee80211com *ic)
+{
+	static const paramType phyParam[IEEE80211_MODE_MAX] = {
+		{ 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 */
+		{ 2, 4, 10, 64, 0 },	/* IEEE80211_MODE_11NG */
+	};
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	const struct wmeParams *wmep;
+	struct wmeParams *chanp, *bssp;
+	enum ieee80211_phymode mode;
+	int i;
+
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		return;
+
+	/* set up the channel access parameters for the physical device */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		chanp = &wme->wme_chanParams.cap_wmeParams[i];
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+		chanp->wmep_aifsn = wmep->wmep_aifsn;
+		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+
+		chanp = &wme->wme_bssChanParams.cap_wmeParams[i];
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+		chanp->wmep_aifsn = wmep->wmep_aifsn;
+		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+	}
+
+	/*
+	 * 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_curchan != IEEE80211_CHAN_ANYC)
+		mode = ieee80211_chan2mode(ic, ic->ic_curchan);
+	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
+	 * traffic by using conservative parameters.  Otherwise
+	 * BE uses agressive params to optimize performance of
+	 * legacy/non-QoS traffic.
+	 */
+	if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    (wme->wme_flags & WME_F_AGGRMODE) != 0) ||
+	    (ic->ic_opmode == IEEE80211_M_STA &&
+	    (ic->ic_bss->in_flags & IEEE80211_NODE_QOS) == 0) ||
+	    (ic->ic_flags & IEEE80211_F_WME) == 0) {
+		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+		chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn;
+		chanp->wmep_logcwmin = bssp->wmep_logcwmin =
+		    phyParam[mode].logcwmin;
+		chanp->wmep_logcwmax = bssp->wmep_logcwmax =
+		    phyParam[mode].logcwmax;
+		chanp->wmep_txopLimit = bssp->wmep_txopLimit =
+		    (ic->ic_flags & IEEE80211_F_BURST) ?
+		    phyParam[mode].txopLimit : 0;
+		ieee80211_dbg(IEEE80211_MSG_WME,
+		    "ieee80211_wme_updateparams_locked: "
+		    "%s [acm %u aifsn %u log2(cwmin) %u "
+		    "log2(cwmax) %u txpoLimit %u]\n",
+		    ieee80211_wme_acnames[WME_AC_BE],
+		    chanp->wmep_acm,
+		    chanp->wmep_aifsn,
+		    chanp->wmep_logcwmin,
+		    chanp->wmep_logcwmax,
+		    chanp->wmep_txopLimit);
+	}
+
+	wme->wme_update(ic);
+
+	ieee80211_dbg(IEEE80211_MSG_WME, "ieee80211_wme_updateparams(): "
+	    "%s: WME params updated, cap_info 0x%x\n",
+	    ic->ic_opmode == IEEE80211_M_STA ?
+	    wme->wme_wmeChanParams.cap_info :
+	    wme->wme_bssChanParams.cap_info);
+}
+
+/*
  * Process STA mode beacon miss events. Send a direct probe request
  * frame to the current ap bmiss_max times (w/o answer) before
  * scanning for a new ap.
@@ -496,8 +810,8 @@
 			switch (ic->ic_opmode) {
 			case IEEE80211_M_STA:
 				IEEE80211_SEND_MGMT(ic, in,
-				    IEEE80211_FC0_SUBTYPE_DISASSOC,
-				    IEEE80211_REASON_ASSOC_LEAVE);
+				    IEEE80211_FC0_SUBTYPE_DEAUTH,
+				    IEEE80211_REASON_AUTH_LEAVE);
 				ieee80211_sta_leave(ic, in);
 				break;
 			case IEEE80211_M_IBSS:
@@ -644,6 +958,15 @@
 			wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
 			wd.wd_opmode = ic->ic_opmode;
 			IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
+			wd.wd_qospad = 0;
+			if (in->in_flags &
+			    (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) {
+				wd.wd_qospad = 2;
+				if (ic->ic_flags & IEEE80211_F_DATAPAD) {
+					wd.wd_qospad = roundup(wd.wd_qospad,
+					    sizeof (uint32_t));
+				}
+			}
 			(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
 			break;
 		}
--- a/usr/src/uts/common/sys/Makefile	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/sys/Makefile	Thu Aug 06 13:05:38 2009 +0800
@@ -390,6 +390,7 @@
 	ndi_impldefs.h		\
 	net80211.h		\
 	net80211_crypto.h	\
+	net80211_ht.h		\
 	net80211_proto.h	\
 	netconfig.h		\
 	neti.h			\
--- a/usr/src/uts/common/sys/mac_wifi.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/sys/mac_wifi.h	Thu Aug 06 13:05:38 2009 +0800
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_SYS_MAC_WIFI_H
 #define	_SYS_MAC_WIFI_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * WiFi MAC-Type Plugin
  */
@@ -47,7 +45,7 @@
  * Maximum size of a WiFi header based on current implementation.
  * May change in the future as new features are added.
  */
-#define	WIFI_HDRSIZE (sizeof (struct ieee80211_frame) + \
+#define	WIFI_HDRSIZE (sizeof (struct ieee80211_qosframe_addr4) + \
     IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN + \
     sizeof (struct ieee80211_llc))
 
@@ -99,12 +97,18 @@
  *			transmission.  The plugin will allocate header
  *		        space for the security portion, and fill in any
  *			fixed-contents fields.
+ *
+ *	wd_qospad	Generally, QoS data field takes 2 bytes, but
+ *			some special hardwares, such as Atheros, will need the
+ *			802.11 header padded to a 32-bit boundary for 4-address
+ *			and QoS frames, at this time, it's 4 bytes.
  */
 typedef struct wifi_data {
 	uint_t			wd_opts;
 	uint8_t			wd_bssid[IEEE80211_ADDR_LEN];
 	enum ieee80211_opmode	wd_opmode;
 	enum wifi_secmode	wd_secalloc;
+	uint_t			wd_qospad;
 } wifi_data_t;
 
 extern uint8_t wifi_bcastaddr[];
--- a/usr/src/uts/common/sys/net80211.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/sys/net80211.h	Thu Aug 06 13:05:38 2009 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -42,6 +42,7 @@
 #include <sys/ethernet.h>
 #include <sys/net80211_proto.h>
 #include <sys/net80211_crypto.h>
+#include <sys/net80211_ht.h>
 #include <net/wpa.h>
 
 /*
@@ -85,6 +86,17 @@
 
 #define	IEEE80211_C_CRYPTO	0x0000001f	/* 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 */
+
 /* ic_flags */
 /* NB: bits 0x4c available */
 #define	IEEE80211_F_FF		0x00000001	/* CONF: ATH FF enabled */
@@ -124,25 +136,52 @@
 #define	IEEE80211_F_WMEUPDATE	0x20000000	/* STATUS: update beacon wme */
 
 /* 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_PROBECHAN 0x00020000	/* CONF: probe passive chan */
+#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 */
 
 /*
  * Channel attributes (ich_flags)
  * bits 0-3 are for private use by drivers
  */
-#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)
 
 #define	IEEE80211_CHAN_MAX	255
 #define	IEEE80211_CHAN_BYTES	32	/* howmany(IEEE80211_CHAN_MAX, NBBY) */
@@ -155,10 +194,30 @@
 #define	IEEE80211_IS_CHAN_5GHZ(_c)	\
 	(((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0)
 
+#define	IEEE80211_NODE_CHWUPDATE 0x0400		/* 11n channel width change */
 #define	IEEE80211_NODE_HASHSIZE	32
 
+#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 */
+#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 */
+
+#define	IEEE80211_NODE_AMPDU \
+	(IEEE80211_NODE_AMPDU_RX | IEEE80211_NODE_AMPDU_TX)
+
 #define	IEEE80211_FIXED_RATE_NONE	0
-#define	IEEE80211_MCAST_RATE_DEFAULT	(2*1)	/* default mcast rate (1M) */
+
+#define	WME_OUI			0xf25000
+#define	WME_OUI_TYPE		0x02
+#define	WME_INFO_OUI_SUBTYPE	0x00
+#define	WME_PARAM_OUI_SUBTYPE	0x01
+#define	WME_VERSION		1
 
 /* WME stream classes */
 #define	WME_AC_BE		0	/* best effort */
@@ -169,6 +228,25 @@
 #define	MAX_EVENT		16
 #define	MAX_IEEE80211STR	256
 
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define	IEEE80211_RADIOTAP_F_CFP	0x01
+					/* sent/received during CFP */
+#define	IEEE80211_RADIOTAP_F_SHORTPRE	0x02
+					/* sent/received with short preamble */
+#define	IEEE80211_RADIOTAP_F_WEP	0x04
+					/* sent/received with WEP encryption */
+#define	IEEE80211_RADIOTAP_F_FRAG	0x08
+					/* sent/received with fragmentation */
+#define	IEEE80211_RADIOTAP_F_DATAPAD	0x20
+					/*
+					 * frame has padding between 802.11
+					 * header and payload (to 32-bit
+					 * boundary
+					 */
+#define	IEEE80211_RADIOTAP_F_FCS	0x10	/* frame includes FCS */
+#define	IEEE80211_RADIOTAP_F_BADFCS	0x40	/* does not pass FCS check */
+#define	IEEE80211_RADIOTAP_F_SHORTGI	0x80	/* HT short GI */
+
 /*
  * Authentication mode.
  */
@@ -204,11 +282,26 @@
 };
 
 /*
+ * 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
+
+/*
  * Channels are specified by frequency and attributes.
  */
 struct ieee80211_channel {
 	uint16_t		ich_freq;	/* setting in Mhz */
-	uint16_t		ich_flags;	/* see below */
+	uint32_t		ich_flags;	/* see below */
 };
 
 struct ieee80211_device_stats {
@@ -254,6 +347,9 @@
 	list_t			nt_hash[IEEE80211_NODE_HASHSIZE];
 };
 
+#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 */
+
 /*
  * Node specific information.  Note that drivers are expected
  * to derive from this structure to add device-specific per-node
@@ -274,8 +370,8 @@
 	 * index 0 is used when QoS is not enabled. index 1-16 is used
 	 * when QoS is enabled. 1-16 corresponds to TID 0-15.
 	 */
-	uint16_t		in_txseqs[17];	/* tx seq per-tid */
-	uint16_t		in_rxseqs[17];	/* rx seq previous per-tid */
+	uint16_t		in_txseqs[IEEE80211_TID_SIZE];
+	uint16_t		in_rxseqs[IEEE80211_TID_SIZE];
 	clock_t			in_rxfragstamp;	/* time stamp of last rx frag */
 	mblk_t			*in_rxfrag;	/* rx frag reassembly */
 	uint32_t		in_scangen;	/* gen# for timeout scan */
@@ -311,23 +407,74 @@
 	uint32_t		*in_challenge;	/* shared-key challenge */
 	struct ieee80211_key	in_ucastkey;	/* unicast key */
 	uint8_t			*in_wpa_ie;	/* captured WPA/RSN ie */
+	uint8_t			*in_wme_ie;	/* captured WME ie */
+
+	/* 11n state */
+	uint8_t			*in_htcap_ie;	/* captured HTCAP ie */
+	uint16_t		in_htcap;	/* HT capabilities */
+	uint8_t			in_htparam;	/* HT params */
+	uint8_t			in_htctlchan;	/* HT control channel */
+	uint8_t			in_ht2ndchan;	/* HT 2nd channel */
+	uint8_t			in_htopmode;	/* HT operating mode */
+	uint8_t			in_htstbc;	/* HT */
+	uint8_t			in_reqcw;	/* requested tx channel width */
+	uint8_t			in_chw;		/* negotiated channel width */
+	struct ieee80211_htrateset in_htrates;	/* negotiated ht rate set */
+	struct ieee80211_tx_ampdu in_tx_ampdu[WME_NUM_AC];
+	struct ieee80211_rx_ampdu in_rx_ampdu[WME_NUM_TID];
 
 	/* others */
 	int32_t			in_fails;	/* failure count to associate */
 	int16_t			in_inact;	/* inactivity mark count */
 	int16_t			in_inact_reload; /* inactivity reload value */
-	int32_t			in_txrate;	/* index to ni_rates[] */
+	int32_t			in_txrate;	/* index to in_rates[] */
 
 	list_node_t		in_node;	/* element of nt->nt_node */
 	list_node_t		in_hash;	/* element of nt->nt_hash */
 };
 
+/*
+ * WME/WMM support.
+ */
+struct wmeParams {
+	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 {
+	uint8_t		cap_info;		/* version of the current set */
+	struct wmeParams cap_wmeParams[WME_NUM_AC];
+};
+
+struct ieee80211_wme_state {
+	uint_t	wme_flags;
+#define	WME_F_AGGRMODE	0x00000001	/* STATUS: WME agressive mode */
+	uint_t	wme_hipri_traffic; /* VI/VO frames in beacon interval */
+	uint_t	wme_hipri_switch_thresh; /* agressive mode switch thresh */
+	uint_t	wme_hipri_switch_hysteresis;
+					/* agressive mode switch hysteresis */
+	struct wmeParams wme_params[4]; /* from assoc resp for each AC */
+	struct chanAccParams wme_wmeChanParams; /* WME params applied to self */
+	struct chanAccParams wme_wmeBssChanParams;
+					/* WME params bcast to stations */
+	struct chanAccParams wme_chanParams; /* params applied to self */
+	struct chanAccParams wme_bssChanParams; /* params bcast to stations */
+	int (*wme_update)(struct ieee80211com *);
+};
+
 struct ieee80211com {
 	mac_handle_t		ic_mach;
 
 	/* Initialized by driver */
 	uint8_t			ic_macaddr[IEEE80211_ADDR_LEN];
 	uint32_t		ic_caps;	/* capabilities */
+	uint32_t		ic_htcaps;	/* HT capabilities */
 	enum ieee80211_phytype	ic_phytype;	/* XXX wrong for multi-mode */
 	enum ieee80211_opmode	ic_opmode;	/* current operation mode */
 	enum ieee80211_state	ic_state;	/* current 802.11 state */
@@ -348,7 +495,6 @@
 	uint8_t			ic_bmissthreshold;
 	uint16_t		ic_rtsthreshold;
 	uint16_t		ic_fragthreshold;
-	int32_t			ic_mcast_rate;	/* rate for mcast frames */
 	uint8_t			ic_fixed_rate;	/* value of fixed rate */
 	int32_t			ic_des_esslen;	/* length of desired essid */
 	uint8_t			ic_des_essid[IEEE80211_NWID_LEN];
@@ -378,6 +524,22 @@
 	struct ieee80211_node_table	ic_scan; /* STA: scan candidates */
 	struct ieee80211_node_table	ic_sta; /* AP:stations/IBSS:neighbors */
 
+	struct ieee80211_wme_state ic_wme;	/* WME/WMM state */
+
+	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) */
+
+	uint16_t		ic_sta_assoc;	/* stations associated */
+	uint16_t		ic_ht_sta_assoc; /* HT stations associated */
+	uint16_t		ic_ht40_sta_assoc; /* HT40 station associated */
+	uint8_t			ic_curhtprotmode; /* HTINFO bss state */
+	enum ieee80211_protmode	ic_htprotmode;	/* HT protection mode */
+	int			ic_lastnonerp;	/* last time nonERP sta noted */
+	int			ic_lastnonht;	/* last time non-HT sta noted */
+
+
 	/* callback functions */
 	/*
 	 * Functions initialized by driver before calling ieee80211_attach()
@@ -423,6 +585,29 @@
 	void			(*ic_node_cleanup)(ieee80211_node_t *);
 	void			(*ic_node_free)(ieee80211_node_t *);
 	uint8_t			(*ic_node_getrssi)(const ieee80211_node_t *);
+	void			(*ic_set_channel)(ieee80211com_t *);
+
+	/*
+	 * 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.
+	 */
+	void			(*ic_recv_action)(ieee80211_node_t *,
+				    const uint8_t *, const uint8_t *);
+	int			(*ic_send_action)(ieee80211_node_t *,
+				    int, int, uint16_t[4]);
+	/* start/stop doing A-MPDU tx aggregation for a station */
+	int			(*ic_addba_request)(ieee80211_node_t *,
+				    struct ieee80211_tx_ampdu *,
+				    int, int, int);
+	int			(*ic_addba_response)(ieee80211_node_t *,
+				    struct ieee80211_tx_ampdu *,
+				    int, int, int);
+	void			(*ic_addba_stop)(ieee80211_node_t *,
+				    struct ieee80211_tx_ampdu *);
 
 	kmutex_t		ic_genlock;
 	void			*ic_private;	/* ieee80211 private data */
@@ -431,6 +616,7 @@
 #define	ic_def_txkey		ic_crypto.cs_def_txkey
 
 extern	const char *ieee80211_state_name[IEEE80211_S_MAX];
+extern	const char *ieee80211_wme_acnames[];
 
 #define	IEEE80211_RATE(_ix)			\
 	(in->in_rates.ir_rates[(_ix)] & IEEE80211_RATE_VAL)
@@ -533,6 +719,12 @@
 void ieee80211_watchdog(void *);
 void ieee80211_start_watchdog(ieee80211com_t *, uint32_t);
 void ieee80211_stop_watchdog(ieee80211com_t *);
+int ieee80211_classify(struct ieee80211com *, mblk_t *,
+    struct ieee80211_node *);
+int ieee80211_hdrsize(const void *);
+int ieee80211_hdrspace(ieee80211com_t *, const void *);
+int ieee80211_anyhdrsize(const void *);
+int ieee80211_anyhdrspace(ieee80211com_t *, const void *);
 
 void *ieee80211_malloc(size_t);
 void ieee80211_free(void *);
@@ -541,6 +733,12 @@
 int ieee80211_getprop(void *, const char *, mac_prop_id_t, uint_t, uint_t,
     void *, uint_t *);
 
+struct ieee80211_channel *ieee80211_find_channel(ieee80211com_t *, int, int);
+const struct ieee80211_rateset *ieee80211_get_suprates(ieee80211com_t *,
+    struct ieee80211_channel *);
+
+/* HT */
+
 #ifdef	__cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/net80211_ht.h	Thu Aug 06 13:05:38 2009 +0800
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef _SYS_NET80211_HT_H
+#define	_SYS_NET80211_HT_H
+
+/*
+ * 802.11n protocol implementation definitions.
+ */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+struct ieee80211com;
+struct ieee80211_node;
+struct ieee80211_channel;
+
+#define	IEEE80211_AGGR_BAWMAX	64	/* max block ack window size */
+/* threshold for aging overlapping non-HT bss */
+#define	IEEE80211_NONHT_PRESENT_AGE	(60*1000)	/* msec */
+
+#define	M_AMPDU			0x8000	/* A-MPDU processing done */
+#define	M_WEP			0x4000	/* WEP done by hardware */
+#define	M_80211_RX		(M_AMPDU | M_WEP)
+
+typedef uint16_t ieee80211_seq;
+
+struct ieee80211_tx_ampdu {
+	ushort_t	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 */
+	clock_t		txa_lastrequest;	/* time of last ADDBA request */
+	timeout_id_t	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 */
+	clock_t		rxa_age;	/* age of oldest frame in window */
+	int		rxa_nframes;	/* frames since ADDBA */
+	mblk_t		*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 *);
+
+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);
+
+mblk_t *ieee80211_decap_amsdu(struct ieee80211_node *, mblk_t *);
+
+int ieee80211_ampdu_reorder(struct ieee80211_node *, mblk_t *);
+void ieee80211_recv_bar(struct ieee80211_node *, mblk_t *);
+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 *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _SYS_NET80211_HT_H */
--- a/usr/src/uts/common/sys/net80211_proto.h	Wed Aug 05 20:39:41 2009 -0700
+++ b/usr/src/uts/common/sys/net80211_proto.h	Thu Aug 06 13:05:38 2009 +0800
@@ -1,11 +1,11 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,8 +38,6 @@
 #ifndef _SYS_NET80211_PROTO_H
 #define	_SYS_NET80211_PROTO_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * 802.11 protocol definitions
  */
@@ -65,6 +63,16 @@
 #define	WME_NUM_AC		4	/* 4 AC categories */
 
 /*
+ * 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 */
+
+/*
  * Protocol Physical Layer
  */
 
@@ -76,15 +84,19 @@
 	IEEE80211_MODE_11G	= 3,	/* 2GHz, OFDM */
 	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_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_phytype {
 	IEEE80211_T_DS,		/* direct sequence spread spectrum */
 	IEEE80211_T_FH,		/* frequency hopping */
 	IEEE80211_T_OFDM,	/* frequency division multiplexing */
-	IEEE80211_T_TURBO	/* high rate OFDM, aka turbo mode */
+	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 */
 
@@ -120,6 +132,22 @@
 	/* see below */
 };
 
+struct ieee80211_qosframe {
+	uint8_t		i_fc[2];
+	uint8_t		i_dur[2];
+	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
+	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
+	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
+	uint8_t		i_seq[2];
+	uint8_t		i_qos[2];
+	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+	/* see below */
+};
+
+struct ieee80211_qoscntl {
+	uint8_t		i_qos[2];
+};
+
 struct ieee80211_frame_addr4 {
 	uint8_t		i_fc[2];
 	uint8_t		i_dur[2];
@@ -130,6 +158,17 @@
 	uint8_t		i_addr4[IEEE80211_ADDR_LEN];
 };
 
+struct ieee80211_qosframe_addr4 {
+	uint8_t		i_fc[2];
+	uint8_t		i_dur[2];
+	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
+	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
+	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
+	uint8_t		i_seq[2];
+	uint8_t		i_addr4[IEEE80211_ADDR_LEN];
+	uint8_t		i_qos[2];
+};
+
 /* Start part(LLC and SNAP) of payload of IEEE80211 frame */
 struct ieee80211_llc {
 	/* LLC */
@@ -150,9 +189,6 @@
 	uint8_t		mnf_dialog;
 	uint8_t		mnf_status;
 };
-#define	IEEE80211_MNF_SETUP_REQ	0
-#define	IEEE80211_MNF_SETUP_RESP	1
-#define	IEEE80211_MNF_TEARDOWN	2
 
 /*
  * Control frames.
@@ -203,6 +239,16 @@
 	/* FCS */
 };
 
+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 */
+};
+
 struct ieee80211_tim_ie {
 	uint8_t		tim_ie;			/* IEEE80211_ELEMID_TIM */
 	uint8_t		tim_len;
@@ -251,9 +297,130 @@
 	uint8_t		wme_oui_sybtype;
 	uint8_t		wme_version;
 	uint8_t		wme_qosInfo;
+#define	WME_QOSINFO_COUNT	0x0f	/* Mask for param count field */
 	uint8_t		wme_reserved;
 	struct ieee80211_wme_acparams	wme_acParams[WME_NUM_AC];
 };
+
+/*
+ * WME/802.11e information element.
+ */
+struct ieee80211_wme_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 */
+};
+
+/*
+ * WME/802.11e Tspec Element
+ */
+struct ieee80211_wme_tspec {
+	uint8_t		ts_id;
+	uint8_t		ts_len;
+	uint8_t		ts_oui[3];
+	uint8_t		ts_oui_type;
+	uint8_t		ts_oui_subtype;
+	uint8_t		ts_version;
+	uint8_t		ts_tsinfo[3];
+	uint8_t		ts_nom_msdu[2];
+	uint8_t		ts_max_msdu[2];
+	uint8_t		ts_min_svc[4];
+	uint8_t		ts_max_svc[4];
+	uint8_t		ts_inactv_intv[4];
+	uint8_t		ts_susp_intv[4];
+	uint8_t		ts_start_svc[4];
+	uint8_t		ts_min_rate[4];
+	uint8_t		ts_mean_rate[4];
+	uint8_t		ts_max_burst[4];
+	uint8_t		ts_min_phy[4];
+	uint8_t		ts_peak_rate[4];
+	uint8_t		ts_delay[4];
+	uint8_t		ts_surplus[2];
+	uint8_t		ts_medium_time[2];
+};
+
+/*
+ * 802.11n Management Action Frames
+ */
+/* generic frame format */
+struct ieee80211_action {
+	uint8_t		ia_category;
+	uint8_t		ia_action;
+};
+
+/* HT - recommended transmission channel width */
+struct ieee80211_action_ht_txchwidth {
+	struct ieee80211_action	at_header;
+	uint8_t		at_chwidth;
+};
+
+struct ieee80211_action_ht_mimopowersave {
+	struct ieee80211_action am_header;
+	uint8_t		am_control;
+};
+
+/* 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;
+};
+
+/* 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 */
+};
+
+/* BA - DELBA */
+struct ieee80211_action_ba_delba {
+	struct ieee80211_action dl_header;
+	uint16_t	dl_baparamset;
+	uint16_t	dl_reasoncode;
+};
+
+struct ieee80211_ba_request {
+	uint16_t	rq_barctl;
+	uint16_t	rq_barseqctl;
+};
+
+/*
+ * 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 */
+};
+
+/*
+ * 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 */
+};
 #pragma pack()
 
 #define	IEEE80211_FC0_VERSION_MASK		0x03
@@ -278,7 +445,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
@@ -302,6 +471,7 @@
 #define	IEEE80211_FC1_DIR_TODS			0x01	/* STA->AP  */
 #define	IEEE80211_FC1_DIR_FROMDS		0x02	/* AP ->STA */
 #define	IEEE80211_FC1_DIR_DSTODS		0x03	/* AP ->AP  */
+
 #define	IEEE80211_FC1_MORE_FRAG			0x04
 #define	IEEE80211_FC1_RETRY			0x08
 #define	IEEE80211_FC1_PWR_MGT			0x10
@@ -313,6 +483,17 @@
 #define	IEEE80211_SEQ_FRAG_SHIFT		0
 #define	IEEE80211_SEQ_SEQ_MASK			0xfff0
 #define	IEEE80211_SEQ_SEQ_SHIFT			4	/* 4bit frag number */
+#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)
 
 /* Length of management frame variable-length components in bytes */
 #define	IEEE80211_NWID_LEN			32	/* SSID */
@@ -321,6 +502,210 @@
 #define	IEEE80211_IBSS_LEN			4	/* IBSS parameters */
 #define	IEEE80211_ERP_LEN			1	/* ERP information */
 
+#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
+
+/* does frame have QoS sequence control data */
+#define	IEEE80211_QOS_HAS_SEQ(wh) \
+	(((wh)->i_fc[0] & \
+	(IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \
+	(IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+
+#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 */
+#define	WME_PARAM_ACM		0x10	/* Mask for ACM bit */
+#define	WME_PARAM_ACM_S		4	/* Shift for ACM bit */
+#define	WME_PARAM_AIFSN		0x0f	/* Mask for aifsn field */
+#define	WME_PARAM_AIFSN_S	0	/* Shift for aifsn field */
+#define	WME_PARAM_LOGCWMIN	0x0f	/* Mask for CwMin field (in log) */
+#define	WME_PARAM_LOGCWMIN_S	0	/* Shift for CwMin field */
+#define	WME_PARAM_LOGCWMAX	0xf0	/* Mask for CwMax field (in log) */
+#define	WME_PARAM_LOGCWMAX_S	4	/* Shift for CwMax field */
+
+#define	WME_AC_TO_TID(_ac) (       \
+	((_ac) == WME_AC_VO) ? 6 : \
+	((_ac) == WME_AC_VI) ? 5 : \
+	((_ac) == WME_AC_BK) ? 1 : \
+	0)
+
+#define	TID_TO_WME_AC(_tid) (      \
+	((_tid) == 0 || (_tid) == 3) ? WME_AC_BE : \
+	((_tid) < 3) ? WME_AC_BK : \
+	((_tid) < 6) ? WME_AC_VI : \
+	WME_AC_VO)
+
+#define	IEEE80211_MNF_SETUP_REQ		0
+#define	IEEE80211_MNF_SETUP_RESP	1
+#define	IEEE80211_MNF_TEARDOWN		2
+
+#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	/* recommend xmit chan width */
+#define	IEEE80211_ACTION_HT_MIMOPWRSAVE	1	/* MIMO power save */
+
+#define	IEEE80211_A_HT_TXCHWIDTH_20	0
+#define	IEEE80211_A_HT_TXCHWIDTH_2040	1
+
+#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
+
+/* 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 */
+
+/* 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 */
+
+/* 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 */
+#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
+
 /*
  * Length of management frame information elements containing
  * a variable-length component is:
@@ -352,6 +737,7 @@
 #define	IEEE80211_CAPINFO_SHORT_PREAMBLE	0x0020
 #define	IEEE80211_CAPINFO_PBCC			0x0040
 #define	IEEE80211_CAPINFO_CHNL_AGILITY		0x0080
+#define	IEEE80211_CAPINFO_SPECTRUM_MGMT		0x0100
 /* bits 8-9 are reserved */
 #define	IEEE80211_CAPINFO_SHORT_SLOTTIME	0x0400
 #define	IEEE80211_CAPINFO_RSN			0x0800
@@ -374,9 +760,21 @@
 	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,
 	/* 128-129 proprietary elements used by Agere chipsets */
 	IEEE80211_ELEMID_AGERE1			= 128,
 	IEEE80211_ELEMID_AGERE2			= 129,
@@ -385,6 +783,10 @@
 	IEEE80211_ELEMID_VENDOR			= 221	/* vendor private */
 };
 
+#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
 #define	WPA_VERSION		1		/* current supported version */
@@ -431,11 +833,20 @@
 	IEEE80211_REASON_NOT_ASSOCED		= 7,
 	IEEE80211_REASON_ASSOC_LEAVE		= 8,
 	IEEE80211_REASON_ASSOC_NOT_AUTHED	= 9,
-	IEEE80211_REASON_INVALID_POWER		= 10,
-	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 */
 };
 
 /*
@@ -455,13 +866,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 */
@@ -533,6 +952,8 @@
 #define	IEEE80211_F_DOFRATE		0x00000002 /* use fixed rate */
 #define	IEEE80211_F_DONEGO		0x00000004 /* calc negotiated rate */
 #define	IEEE80211_F_DODEL		0x00000008 /* delete ignore rate */
+#define	IEEE80211_F_DOBRS		0x00000010 /* check basic rate set */
+#define	IEEE80211_F_JOIN		0x00000020 /* sta joining our bss */
 
 /*
  * Beacon frames constructed by ieee80211_beacon_alloc
@@ -547,6 +968,7 @@
 	uint16_t	bo_tim_len;	/* atim/dtim length in bytes */
 	uint16_t	bo_trailer_len;	/* trailer length in bytes */
 	uint8_t		*bo_erp;	/* start of ERP element */
+	uint8_t		*bo_htinfo;	/* start of HT info element */
 };
 
 #ifdef	__cplusplus