Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 1 May 2011 15:24:09 +0200
From:      Bernhard Schmidt <bschmidt@freebsd.org>
To:        freebsd-wireless@freebsd.org
Cc:        freebsd-net@freebsd.org
Subject:   Re: CFT: 11n support for iwn(4)
Message-ID:  <201105011524.09613.bschmidt@freebsd.org>
In-Reply-To: <201105011319.30649.bschmidt@freebsd.org>
References:  <201105011319.30649.bschmidt@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
--Boundary-00=_57VvNvUbwvnkjXW
Content-Type: Text/Plain;
  charset="iso-8859-15"
Content-Transfer-Encoding: 7bit

On Sunday 01 May 2011 13:19:30 Bernhard Schmidt wrote:
> Hi,
> 
> I finally managed to get the 11n bits for iwn(4) sorted out. Well,
> there is still an issue somewhere with HT40 frame protection or
> TX chain setup on 5000 adapters, resulting in throughput not being
> that stable. But overall it seems to work pretty decently
> 
> This is for HEAD only right now, net80211 in stable/8 does not yet
> contain the latest 11n related fixes. So, if you run HEAD and have
> some iwn(4) hardware, I'd appreciate feedback.
> ..

Updated version, I've missed a locking issue.

-- 
Bernhard

--Boundary-00=_57VvNvUbwvnkjXW
Content-Type: text/x-patch;
  charset="ISO-8859-1";
  name="iwn_ht2.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="iwn_ht2.diff"

diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c
index 7d6ec68..e2cf4be 100644
--- a/sys/dev/iwn/if_iwn.c
+++ b/sys/dev/iwn/if_iwn.c
@@ -152,9 +152,7 @@ static void	iwn4965_print_power_group(struct iwn_softc *, int);
 static void	iwn5000_read_eeprom(struct iwn_softc *);
 static uint32_t	iwn_eeprom_channel_flags(struct iwn_eeprom_chan *);
 static void	iwn_read_eeprom_band(struct iwn_softc *, int);
-#if 0	/* HT */
 static void	iwn_read_eeprom_ht40(struct iwn_softc *, int);
-#endif
 static void	iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
 static struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
 		    struct ieee80211_channel *);
@@ -172,10 +170,8 @@ static void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
 		    struct iwn_rx_data *);
 static void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
 		    struct iwn_rx_data *);
-#if 0	/* HT */
 static void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
 		    struct iwn_rx_data *);
-#endif
 static void	iwn5000_rx_calib_results(struct iwn_softc *,
 		    struct iwn_rx_desc *, struct iwn_rx_data *);
 static void	iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
@@ -186,6 +182,7 @@ static void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
 		    struct iwn_rx_data *);
 static void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
 		    uint8_t);
+static void	iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *);
 static void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
 static void	iwn_notif_intr(struct iwn_softc *);
 static void	iwn_wakeup_intr(struct iwn_softc *);
@@ -199,7 +196,6 @@ static void	iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
 #ifdef notyet
 static void	iwn5000_reset_sched(struct iwn_softc *, int, int);
 #endif
-static uint8_t	iwn_plcp_signal(int);
 static int	iwn_tx_data(struct iwn_softc *, struct mbuf *,
 		    struct ieee80211_node *);
 static int	iwn_tx_data_raw(struct iwn_softc *, struct mbuf *,
@@ -252,24 +248,26 @@ static uint8_t	*ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int);
 static int	iwn_scan(struct iwn_softc *);
 static int	iwn_auth(struct iwn_softc *, struct ieee80211vap *vap);
 static int	iwn_run(struct iwn_softc *, struct ieee80211vap *vap);
-#if 0	/* HT */
-static int	iwn_ampdu_rx_start(struct ieee80211com *,
-		    struct ieee80211_node *, uint8_t);
-static void	iwn_ampdu_rx_stop(struct ieee80211com *,
-		    struct ieee80211_node *, uint8_t);
+static int	iwn_ampdu_rx_start(struct ieee80211_node *,
+		    struct ieee80211_rx_ampdu *, int, int, int);
+static void	iwn_ampdu_rx_stop(struct ieee80211_node *,
+		    struct ieee80211_rx_ampdu *);
+static int	iwn_addba_request(struct ieee80211_node *,
+		    struct ieee80211_tx_ampdu *, int, int, int);
+static int	iwn_addba_response(struct ieee80211_node *,
+		    struct ieee80211_tx_ampdu *, int, int, int);
 static int	iwn_ampdu_tx_start(struct ieee80211com *,
 		    struct ieee80211_node *, uint8_t);
-static void	iwn_ampdu_tx_stop(struct ieee80211com *,
-		    struct ieee80211_node *, uint8_t);
+static void	iwn_ampdu_tx_stop(struct ieee80211_node *,
+		    struct ieee80211_tx_ampdu *);
 static void	iwn4965_ampdu_tx_start(struct iwn_softc *,
-		    struct ieee80211_node *, uint8_t, uint16_t);
-static void	iwn4965_ampdu_tx_stop(struct iwn_softc *,
+		    struct ieee80211_node *, int, uint8_t, uint16_t);
+static void	iwn4965_ampdu_tx_stop(struct iwn_softc *, int,
 		    uint8_t, uint16_t);
 static void	iwn5000_ampdu_tx_start(struct iwn_softc *,
-		    struct ieee80211_node *, uint8_t, uint16_t);
-static void	iwn5000_ampdu_tx_stop(struct iwn_softc *,
+		    struct ieee80211_node *, int, uint8_t, uint16_t);
+static void	iwn5000_ampdu_tx_stop(struct iwn_softc *, int,
 		    uint8_t, uint16_t);
-#endif
 static int	iwn5000_query_calibration(struct iwn_softc *);
 static int	iwn5000_send_calibration(struct iwn_softc *);
 static int	iwn5000_send_wimax_coex(struct iwn_softc *);
@@ -550,21 +548,6 @@ iwn_attach(device_t dev)
 	/* Clear pending interrupts. */
 	IWN_WRITE(sc, IWN_INT, 0xffffffff);
 
-	/* Count the number of available chains. */
-	sc->ntxchains =
-	    ((sc->txchainmask >> 2) & 1) +
-	    ((sc->txchainmask >> 1) & 1) +
-	    ((sc->txchainmask >> 0) & 1);
-	sc->nrxchains =
-	    ((sc->rxchainmask >> 2) & 1) +
-	    ((sc->rxchainmask >> 1) & 1) +
-	    ((sc->rxchainmask >> 0) & 1);
-	if (bootverbose) {
-		device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
-		    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
-		    macaddr, ":");
-	}
-
 	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
 	if (ifp == NULL) {
 		device_printf(dev, "can not allocate ifnet structure\n");
@@ -584,42 +567,13 @@ iwn_attach(device_t dev)
 		| IEEE80211_C_SHSLOT		/* short slot time supported */
 		| IEEE80211_C_WPA
 		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
-		| IEEE80211_C_BGSCAN		/* background scanning */
 #if 0
 		| IEEE80211_C_IBSS		/* ibss/adhoc mode */
 #endif
 		| IEEE80211_C_WME		/* WME */
 		;
-#if 0	/* HT */
-	/* XXX disable until HT channel setup works */
-	ic->ic_htcaps =
-		  IEEE80211_HTCAP_SMPS_ENA	/* SM PS mode enabled */
-		| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width */
-		| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
-		| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
-		| IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */
-		| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
-		/* s/w capabilities */
-		| IEEE80211_HTC_HT		/* HT operation */
-		| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
-		| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
-		;
-
-	/* Set HT capabilities. */
-	ic->ic_htcaps =
-#if IWN_RBUF_SIZE == 8192
-	    IEEE80211_HTCAP_AMSDU7935 |
-#endif
-	    IEEE80211_HTCAP_CBW20_40 |
-	    IEEE80211_HTCAP_SGI20 |
-	    IEEE80211_HTCAP_SGI40;
 	if (sc->hw_type != IWN_HW_REV_TYPE_4965)
-		ic->ic_htcaps |= IEEE80211_HTCAP_GF;
-	if (sc->hw_type == IWN_HW_REV_TYPE_6050)
-		ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN;
-	else
-		ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS;
-#endif
+		ic->ic_caps |= IEEE80211_C_BGSCAN; /* background scanning */
 
 	/* Read MAC address, channels, etc from EEPROM. */
 	if ((error = iwn_read_eeprom(sc, macaddr)) != 0) {
@@ -628,14 +582,49 @@ iwn_attach(device_t dev)
 		goto fail;
 	}
 
-#if 0	/* HT */
-	/* Set supported HT rates. */
-	ic->ic_sup_mcs[0] = 0xff;
-	if (sc->nrxchains > 1)
-		ic->ic_sup_mcs[1] = 0xff;
-	if (sc->nrxchains > 2)
-		ic->ic_sup_mcs[2] = 0xff;
+	/* Count the number of available chains. */
+	sc->ntxchains =
+	    ((sc->txchainmask >> 2) & 1) +
+	    ((sc->txchainmask >> 1) & 1) +
+	    ((sc->txchainmask >> 0) & 1);
+	sc->nrxchains =
+	    ((sc->rxchainmask >> 2) & 1) +
+	    ((sc->rxchainmask >> 1) & 1) +
+	    ((sc->rxchainmask >> 0) & 1);
+	if (bootverbose) {
+		device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
+		    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
+		    macaddr, ":");
+	}
+
+	if (sc->sc_flags & IWN_FLAG_HAS_11N) {
+		ic->ic_rxstream = sc->nrxchains;
+		ic->ic_txstream = sc->ntxchains;
+		ic->ic_htcaps =
+			  IEEE80211_HTCAP_SMPS_ENA	/* SM PS mode enabled */
+			| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width*/
+			| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
+			| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
+#if IWN_RBUF_SIZE == 8192
+			| IEEE80211_HTCAP_MAXAMSDU_7935	/* max A-MSDU length */
+#else
+			| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
+#endif
+#ifdef notyet
+			| IEEE80211_HTCAP_GREENFIELD
+#endif
+			/* s/w capabilities */
+			| IEEE80211_HTC_HT		/* HT operation */
+			| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
+#ifdef notyet
+			| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
 #endif
+			;
+		if (sc->hw_type == IWN_HW_REV_TYPE_6050)
+			ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYNAMIC;
+		else
+			ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_OFF;
+	}
 
 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
 	ifp->if_softc = sc;
@@ -652,12 +641,16 @@ iwn_attach(device_t dev)
 	ic->ic_vap_delete = iwn_vap_delete;
 	ic->ic_raw_xmit = iwn_raw_xmit;
 	ic->ic_node_alloc = iwn_node_alloc;
-#if 0	/* HT */
+	sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start;
 	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
+	sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
 	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
-	ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
-	ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
-#endif
+	sc->sc_addba_request = ic->ic_addba_request;
+	ic->ic_addba_request = iwn_addba_request;
+	sc->sc_addba_response = ic->ic_addba_response;
+	ic->ic_addba_response = iwn_addba_response;
+	sc->sc_addba_stop = ic->ic_addba_stop;
+	ic->ic_addba_stop = iwn_ampdu_tx_stop;
 	ic->ic_newassoc = iwn_newassoc;
 	ic->ic_wme.wme_update = iwn_updateedca;
 	ic->ic_update_mcast = iwn_update_mcast;
@@ -714,11 +707,10 @@ iwn4965_attach(struct iwn_softc *sc, uint16_t pid)
 	ops->set_gains = iwn4965_set_gains;
 	ops->add_node = iwn4965_add_node;
 	ops->tx_done = iwn4965_tx_done;
-#if 0	/* HT */
 	ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
 	ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
-#endif
 	sc->ntxqs = IWN4965_NTXQUEUES;
+	sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE;
 	sc->ndmachnls = IWN4965_NDMACHNLS;
 	sc->broadcast_id = IWN4965_ID_BROADCAST;
 	sc->rxonsz = IWN4965_RXONSZ;
@@ -753,11 +745,10 @@ iwn5000_attach(struct iwn_softc *sc, uint16_t pid)
 	ops->set_gains = iwn5000_set_gains;
 	ops->add_node = iwn5000_add_node;
 	ops->tx_done = iwn5000_tx_done;
-#if 0	/* HT */
 	ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
 	ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
-#endif
 	sc->ntxqs = IWN5000_NTXQUEUES;
+	sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE;
 	sc->ndmachnls = IWN5000_NDMACHNLS;
 	sc->broadcast_id = IWN5000_ID_BROADCAST;
 	sc->rxonsz = IWN5000_RXONSZ;
@@ -1489,13 +1480,6 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
 		    __func__, error);
 		goto fail;
 	}
-	/*
-	 * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need
-	 * to allocate commands space for other rings.
-	 * XXX Do we really need to allocate descriptors for other rings?
-	 */
-	if (qid > 4)
-		return 0;
 
 	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
 	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
@@ -1694,7 +1678,7 @@ iwn4965_read_eeprom(struct iwn_softc *sc)
 	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
 
 	/* Read the list of authorized channels (20MHz ones only). */
-	for (i = 0; i < 5; i++) {
+	for (i = 0; i < 7; i++) {
 		addr = iwn4965_regulatory_bands[i];
 		iwn_read_eeprom_channels(sc, i, addr);
 	}
@@ -1781,8 +1765,11 @@ iwn5000_read_eeprom(struct iwn_softc *sc)
 	    sc->eeprom_domain, 4);
 
 	/* Read the list of authorized channels (20MHz ones only). */
-	for (i = 0; i < 5; i++) {
-		addr = base + iwn5000_regulatory_bands[i];
+	for (i = 0; i < 7; i++) {
+		if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
+			addr = base + iwn6000_regulatory_bands[i];
+		else
+			addr = base + iwn5000_regulatory_bands[i];
 		iwn_read_eeprom_channels(sc, i, addr);
 	}
 
@@ -1884,18 +1871,15 @@ iwn_read_eeprom_band(struct iwn_softc *sc, int n)
 		    "add chan %d flags 0x%x maxpwr %d\n", chan,
 		    channels[i].flags, channels[i].maxpwr);
 
-#if 0	/* HT */
-		/* XXX no constraints on using HT20 */
-		/* add HT20, HT40 added separately */
-		c = &ic->ic_channels[ic->ic_nchans++];
-		c[0] = c[-1];
-		c->ic_flags |= IEEE80211_CHAN_HT20;
-		/* XXX NARROW =>'s 1/2 and 1/4 width? */
-#endif
+		if (sc->sc_flags & IWN_FLAG_HAS_11N) {
+			/* add HT20, HT40 added separately */
+			c = &ic->ic_channels[ic->ic_nchans++];
+			c[0] = c[-1];
+			c->ic_flags |= IEEE80211_CHAN_HT20;
+		}
 	}
 }
 
-#if 0	/* HT */
 static void
 iwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
 {
@@ -1904,55 +1888,59 @@ iwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
 	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
 	const struct iwn_chan_band *band = &iwn_bands[n];
 	struct ieee80211_channel *c, *cent, *extc;
-	int i;
+	uint8_t chan;
+	int i, nflags;
+
+	if (!(sc->sc_flags & IWN_FLAG_HAS_11N))
+		return;
 
 	for (i = 0; i < band->nchan; i++) {
-		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) ||
-		    !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) {
+		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
 			DPRINTF(sc, IWN_DEBUG_RESET,
 			    "skip chan %d flags 0x%x maxpwr %d\n",
 			    band->chan[i], channels[i].flags,
 			    channels[i].maxpwr);
 			continue;
 		}
+		chan = band->chan[i];
+		nflags = iwn_eeprom_channel_flags(&channels[i]);
+
 		/*
 		 * Each entry defines an HT40 channel pair; find the
 		 * center channel, then the extension channel above.
 		 */
-		cent = ieee80211_find_channel_byieee(ic, band->chan[i],
-		    band->flags & ~IEEE80211_CHAN_HT);
+		cent = ieee80211_find_channel_byieee(ic, chan,
+		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
 		if (cent == NULL) {	/* XXX shouldn't happen */
 			device_printf(sc->sc_dev,
-			    "%s: no entry for channel %d\n",
-			    __func__, band->chan[i]);
+			    "%s: no entry for channel %d\n", __func__, chan);
 			continue;
 		}
 		extc = ieee80211_find_channel(ic, cent->ic_freq+20,
-		    band->flags & ~IEEE80211_CHAN_HT);
+		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
 		if (extc == NULL) {
 			DPRINTF(sc, IWN_DEBUG_RESET,
-			    "skip chan %d, extension channel not found\n",
-			    band->chan[i]);
+			    "%s: skip chan %d, extension channel not found\n",
+			    __func__, chan);
 			continue;
 		}
 
 		DPRINTF(sc, IWN_DEBUG_RESET,
 		    "add ht40 chan %d flags 0x%x maxpwr %d\n",
-		    band->chan[i], channels[i].flags, channels[i].maxpwr);
+		    chan, channels[i].flags, channels[i].maxpwr);
 
 		c = &ic->ic_channels[ic->ic_nchans++];
 		c[0] = cent[0];
 		c->ic_extieee = extc->ic_ieee;
 		c->ic_flags &= ~IEEE80211_CHAN_HT;
-		c->ic_flags |= IEEE80211_CHAN_HT40U;
+		c->ic_flags |= IEEE80211_CHAN_HT40U | nflags;
 		c = &ic->ic_channels[ic->ic_nchans++];
 		c[0] = extc[0];
 		c->ic_extieee = cent->ic_ieee;
-		c->ic_flags &= ~IEEE80211_CHAN_HT;
+		c->ic_flags &= ~IEEE80211_CHAN_HT | nflags;
 		c->ic_flags |= IEEE80211_CHAN_HT40D;
 	}
 }
-#endif
 
 static void
 iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
@@ -1965,25 +1953,34 @@ iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
 
 	if (n < 5)
 		iwn_read_eeprom_band(sc, n);
-#if 0	/* HT */
 	else
 		iwn_read_eeprom_ht40(sc, n);
-#endif
 	ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
 }
 
 static struct iwn_eeprom_chan *
 iwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
 {
-	int i, j;
+	int band, chan, i, j;
 
-	for (j = 0; j < 7; j++) {
-		for (i = 0; i < iwn_bands[j].nchan; i++) {
-			if (iwn_bands[j].chan[i] == c->ic_ieee)
-				return &sc->eeprom_channels[j][i];
+	if (IEEE80211_IS_CHAN_HT40(c)) {
+		band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5;
+		if (IEEE80211_IS_CHAN_HT40D(c))
+			chan = c->ic_extieee;
+		else
+			chan = c->ic_ieee;
+		for (i = 0; i < iwn_bands[band].nchan; i++) {
+			if (iwn_bands[band].chan[i] == chan)
+				return &sc->eeprom_channels[band][i];
+		}
+	} else {
+		for (j = 0; j < 5; j++) {
+			for (i = 0; i < iwn_bands[j].nchan; i++) {
+				if (iwn_bands[j].chan[i] == c->ic_ieee)
+					return &sc->eeprom_channels[j][i];
+			}
 		}
 	}
-
 	return NULL;
 }
 
@@ -2020,18 +2017,22 @@ static void
 iwn_read_eeprom_enhinfo(struct iwn_softc *sc)
 {
 	struct iwn_eeprom_enhinfo enhinfo[35];
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ieee80211com *ic = ifp->if_l2com;
+	struct ieee80211_channel *c;
 	uint16_t val, base;
 	int8_t maxpwr;
-	int i;
+	uint8_t flags;
+	int i, j;
 
 	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
 	base = le16toh(val);
 	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
 	    enhinfo, sizeof enhinfo);
 
-	memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr);
 	for (i = 0; i < nitems(enhinfo); i++) {
-		if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0)
+		flags = enhinfo[i].flags;
+		if (!(flags & IWN_ENHINFO_VALID))
 			continue;	/* Skip invalid entries. */
 
 		maxpwr = 0;
@@ -2045,11 +2046,34 @@ iwn_read_eeprom_enhinfo(struct iwn_softc *sc)
 			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
 		else if (sc->ntxchains == 3)
 			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
-		maxpwr /= 2;	/* Convert half-dBm to dBm. */
 
-		DPRINTF(sc, IWN_DEBUG_RESET, "enhinfo %d, maxpwr=%d\n", i,
-		    maxpwr);
-		sc->enh_maxpwr[i] = maxpwr;
+		for (j = 0; j < ic->ic_nchans; j++) {
+			c = &ic->ic_channels[j];
+			if ((flags & IWN_ENHINFO_5GHZ)) {
+				if (!IEEE80211_IS_CHAN_A(c))
+					continue;
+			} else if ((flags & IWN_ENHINFO_OFDM)) {
+				if (!IEEE80211_IS_CHAN_G(c))
+					continue;
+			} else if (!IEEE80211_IS_CHAN_B(c))
+				continue;
+			if ((flags & IWN_ENHINFO_HT40)) {
+				if (!IEEE80211_IS_CHAN_HT40(c))
+					continue;
+			} else {
+				if (IEEE80211_IS_CHAN_HT40(c))
+					continue;
+			}
+			if (enhinfo[i].chan != 0 &&
+			    enhinfo[i].chan != c->ic_ieee)
+				continue;
+
+			DPRINTF(sc, IWN_DEBUG_RESET,
+			    "channel %d(%x), maxpwr %d\n", c->ic_ieee,
+			    c->ic_flags, maxpwr / 2);
+			c->ic_maxregpower = maxpwr / 2;
+			c->ic_maxpower = maxpwr;
+		}
 	}
 }
 
@@ -2059,15 +2083,73 @@ iwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
 	return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO);
 }
 
+static __inline int
+rate2plcp(int rate)
+{
+	switch (rate & 0xff) {
+	case 12:	return 0xd;
+	case 18:	return 0xf;
+	case 24:	return 0x5;
+	case 36:	return 0x7;
+	case 48:	return 0x9;
+	case 72:	return 0xb;
+	case 96:	return 0x1;
+	case 108:	return 0x3;
+	case 2:		return 10;
+	case 4:		return 20;
+	case 11:	return 55;
+	case 22:	return 110;
+	}
+	return 0;
+}
+
 static void
 iwn_newassoc(struct ieee80211_node *ni, int isnew)
 {
+	struct ieee80211com *ic = ni->ni_ic;
+	struct iwn_softc *sc = ic->ic_ifp->if_softc;
 	struct iwn_node *wn = (void *)ni;
-	int ridx, i;
+	uint8_t txant1, txant2;
+	int i, plcp, rate, ridx;
 
-	for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
-		ridx = iwn_plcp_signal(ni->ni_rates.rs_rates[i]);
-		wn->ridx[i] = ridx;
+	/* Use the first valid TX antenna. */
+	txant1 = IWN_LSB(sc->txchainmask);
+	txant2 = IWN_LSB(sc->txchainmask & ~txant1);
+
+	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
+		ridx = ni->ni_rates.rs_nrates - 1;
+		for (i = ni->ni_htrates.rs_nrates - 1; i >= 0; i--) {
+			plcp = ni->ni_htrates.rs_rates[i] | IWN_RFLAG_MCS;
+			if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+				plcp |= IWN_RFLAG_HT40;
+				if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
+					plcp |= IWN_RFLAG_SGI;
+			} else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+				plcp |= IWN_RFLAG_SGI;
+			if (i > 7)
+				plcp |= IWN_RFLAG_ANT(txant1 | txant2);
+			else
+				plcp |= IWN_RFLAG_ANT(txant1);
+			if (ridx >= 0) {
+				rate = ni->ni_rates.rs_rates[ridx];
+				rate &= IEEE80211_RATE_VAL;
+				wn->ridx[rate] = plcp;
+			}
+			wn->ridx[0x80 | i] = plcp;
+			ridx--;
+		}
+	} else {
+		for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
+			rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL;
+
+			plcp = rate2plcp(rate);
+			ridx = ic->ic_rt->rateCodeToIndex[rate];
+			if (ridx < IWN_RIDX_OFDM6 &&
+			    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+				plcp |= IWN_RFLAG_CCK;
+			plcp |= IWN_RFLAG_ANT(txant1);
+			wn->ridx[rate] = htole32(plcp);
+		}
 	}
 }
 
@@ -2220,7 +2302,6 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
 			    "%s: missing RX_PHY\n", __func__);
 			return;
 		}
-		sc->last_rx_valid = 0;
 		stat = &sc->last_rx_stat;
 	} else
 		stat = (struct iwn_rx_stat *)(desc + 1);
@@ -2343,6 +2424,8 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
 
 	/* Send the frame to the 802.11 layer. */
 	if (ni != NULL) {
+		if (ni->ni_flags & IEEE80211_NODE_HT)
+			m->m_flags |= M_AMPDU;
 		(void)ieee80211_input(ni, m, rssi - nf, nf);
 		/* Node is no longer needed. */
 		ieee80211_free_node(ni);
@@ -2352,21 +2435,53 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
 	IWN_LOCK(sc);
 }
 
-#if 0	/* HT */
 /* Process an incoming Compressed BlockAck. */
 static void
 iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
     struct iwn_rx_data *data)
 {
+	struct ifnet *ifp = sc->sc_ifp;
+	struct iwn_node *wn;
+	struct ieee80211_node *ni;
 	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
 	struct iwn_tx_ring *txq;
+	struct ieee80211_tx_ampdu *tap;
+	uint64_t bitmap;
+	uint8_t tid;
+	int ackfailcnt = 0, i, shift;
 
 	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
 
-	txq = &sc->txq[letoh16(ba->qid)];
-	/* XXX TBD */
+	txq = &sc->txq[le16toh(ba->qid)];
+	tap = sc->qid2tap[le16toh(ba->qid)];
+	tid = WME_AC_TO_TID(tap->txa_ac);
+	ni = tap->txa_ni;
+	wn = (void *)ni;
+
+	if (wn->agg[tid].bitmap == 0)
+		return;
+
+	shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff);
+	if (shift < 0)
+		shift += 0x100;
+
+	if (wn->agg[tid].nframes > (64 - shift))
+		return;
+
+	bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
+	for (i = 0; bitmap; i++) {
+		if ((bitmap & 1) == 0) {
+			ifp->if_oerrors++;
+			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
+			    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
+		} else {
+			ifp->if_opackets++;
+			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
+			    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
+		}
+		bitmap >>= 1;
+	}
 }
-#endif
 
 /*
  * Process a CALIBRATION_RESULT notification sent by the initialization
@@ -2495,7 +2610,11 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
     struct iwn_rx_data *data)
 {
 	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
-	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
+	struct iwn_tx_ring *ring;
+	int qid;
+
+	qid = desc->qid & 0xf;
+	ring = &sc->txq[qid];
 
 	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
 	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
@@ -2504,7 +2623,13 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
 	    le32toh(stat->status));
 
 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
-	iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff);
+	if (qid >= sc->firstaggqueue) {
+		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
+		    &stat->status);
+	} else {
+		iwn_tx_done(sc, desc, stat->ackfailcnt,
+		    le32toh(stat->status) & 0xff);
+	}
 }
 
 static void
@@ -2512,7 +2637,11 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
     struct iwn_rx_data *data)
 {
 	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
-	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
+	struct iwn_tx_ring *ring;
+	int qid;
+
+	qid = desc->qid & 0xf;
+	ring = &sc->txq[qid];
 
 	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
 	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
@@ -2526,7 +2655,13 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
 #endif
 
 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
-	iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff);
+	if (qid >= sc->firstaggqueue) {
+		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
+		    &stat->status);
+	} else {
+		iwn_tx_done(sc, desc, stat->ackfailcnt,
+		    le16toh(stat->status) & 0xff);
+	}
 }
 
 /*
@@ -2627,6 +2762,96 @@ iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
 	wakeup(&ring->desc[desc->idx]);
 }
 
+static void
+iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
+    void *stat)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+	struct iwn_tx_ring *ring = &sc->txq[qid];
+	struct iwn_tx_data *data;
+	struct mbuf *m;
+	struct iwn_node *wn;
+	struct ieee80211_node *ni;
+	struct ieee80211vap *vap;
+	struct ieee80211_tx_ampdu *tap;
+	uint64_t bitmap;
+	uint32_t *status = stat;
+	uint16_t *aggstatus = stat;
+	uint8_t tid;
+	int bit, i, lastidx, seqno, shift, start;
+
+#ifdef NOT_YET
+	if (nframes == 1) {
+		if ((*status & 0xff) != 1 && (*status & 0xff) != 2)
+			printf("ieee80211_send_bar()\n");
+	}
+#endif
+
+	bitmap = 0;
+	start = idx;
+	for (i = 0; i < nframes; i++) {
+		if (le16toh(aggstatus[i * 2]) & 0xc)
+			continue;
+
+		idx = le16toh(aggstatus[2*i + 1]) & 0xff;
+		bit = idx - start;
+		shift = 0;
+		if (bit >= 64) {
+			shift = 0x100 - idx + start;
+			bit = 0;
+			start = idx;
+		} else if (bit <= -64)
+			bit = 0x100 - start + idx;
+		else if (bit < 0) {
+			shift = start - idx;
+			start = idx;
+			bit = 0;
+		}
+		bitmap = bitmap << shift;
+		bitmap |= 1ULL << bit;
+	}
+	tap = sc->qid2tap[qid];
+	tid = WME_AC_TO_TID(tap->txa_ac);
+	wn = (void *)tap->txa_ni;
+	wn->agg[tid].bitmap = bitmap;
+	wn->agg[tid].startidx = start;
+	wn->agg[tid].nframes = nframes;
+
+	seqno = le32toh(*(status + nframes)) & 0xfff;
+	for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
+		data = &ring->data[ring->read];
+
+		KASSERT(data->ni != NULL, ("no node"));
+
+		/* Unmap and free mbuf. */
+		bus_dmamap_sync(ring->data_dmat, data->map,
+		    BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(ring->data_dmat, data->map);
+		m = data->m, data->m = NULL;
+		ni = data->ni, data->ni = NULL;
+		vap = ni->ni_vap;
+
+		if (m->m_flags & M_TXCB)
+			ieee80211_process_callback(ni, m, 1);
+
+		m_freem(m);
+		ieee80211_free_node(ni);
+
+		ring->queued--;
+		ring->read = (ring->read + 1) % IWN_TX_RING_COUNT;
+	}
+
+	sc->sc_tx_timer = 0;
+	if (ring->queued < IWN_TX_RING_LOMARK) {
+		sc->qfullmsk &= ~(1 << ring->qid);
+		if (sc->qfullmsk == 0 &&
+		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
+			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+			iwn_start_locked(ifp);
+		}
+	}
+}
+
 /*
  * Process an INT_FH_RX or INT_SW_RX interrupt.
  */
@@ -2671,12 +2896,10 @@ iwn_notif_intr(struct iwn_softc *sc)
 			iwn_rx_done(sc, desc, data);
 			break;
 
-#if 0	/* HT */
 		case IWN_RX_COMPRESSED_BA:
 			/* A Compressed BlockAck has been received. */
 			iwn_rx_compressed_ba(sc, desc, data);
 			break;
-#endif
 
 		case IWN_TX_DONE:
 			/* An 802.11 frame has been transmitted. */
@@ -3053,21 +3276,10 @@ iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
 }
 #endif
 
-static uint8_t
-iwn_plcp_signal(int rate) {
-	int i;
-
-	for (i = 0; i < IWN_RIDX_MAX + 1; i++) {
-		if ((rate & IEEE80211_RATE_VAL) == iwn_rates[i].rate)
-			return i;
-	}
-
-	return 0;
-}
-
 static int
 iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 {
+	struct iwn_ops *ops = &sc->ops;
 	const struct ieee80211_txparam *tp;
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ni->ni_ic;
@@ -3077,7 +3289,6 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 	struct iwn_tx_data *data;
 	struct iwn_tx_cmd *cmd;
 	struct iwn_cmd_data *tx;
-	const struct iwn_rate *rinfo;
 	struct ieee80211_frame *wh;
 	struct ieee80211_key *k = NULL;
 	struct mbuf *m1;
@@ -3104,7 +3315,16 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 	}
 	ac = M_WME_GETAC(m);
 
-	ring = &sc->txq[ac];
+	if (IEEE80211_AMPDU_RUNNING(&ni->ni_tx_ampdu[ac])) {
+		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
+
+		ring = &sc->txq[*(int *)tap->txa_private];
+		*(uint16_t *)wh->i_seq =
+		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+		ni->ni_txseqs[tid]++;
+	} else {
+		ring = &sc->txq[ac];
+	}
 	desc = &ring->desc[ring->cur];
 	data = &ring->data[ring->cur];
 
@@ -3121,8 +3341,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 		(void) ieee80211_ratectl_rate(ni, NULL, 0);
 		rate = ni->ni_txrate;
 	}
-	ridx = iwn_plcp_signal(rate);
-	rinfo = &iwn_rates[ridx];
+	ridx = ic->ic_rt->rateCodeToIndex[rate];
 
 	/* Encrypt the frame if need be. */
 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
@@ -3141,7 +3360,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
 
 		tap->wt_flags = 0;
-		tap->wt_rate = rinfo->rate;
+		tap->wt_rate = rate;
 		if (k != NULL)
 			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
 
@@ -3228,14 +3447,13 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 	tx->rts_ntries = 60;
 	tx->data_ntries = 15;
 	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
-	tx->plcp = rinfo->plcp;
-	tx->rflags = rinfo->flags;
+	tx->rate = wn->ridx[rate];
 	if (tx->id == sc->broadcast_id) {
 		/* Group or management frame. */
 		tx->linkq = 0;
 		/* XXX Alternate between antenna A and B? */
 		txant = IWN_LSB(sc->txchainmask);
-		tx->rflags |= IWN_RFLAG_ANT(txant);
+		tx->rate |= htole32(IWN_RFLAG_ANT(txant));
 	} else {
 		tx->linkq = ni->ni_rates.rs_nrates - ridx - 1;
 		flags |= IWN_TX_LINKQ;	/* enable MRR */
@@ -3310,10 +3528,8 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
 	    BUS_DMASYNC_PREWRITE);
 
-#ifdef notyet
 	/* Update TX scheduler. */
 	ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
-#endif
 
 	/* Kick TX ring. */
 	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
@@ -3330,7 +3546,7 @@ static int
 iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
     struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
 {
-	const struct iwn_rate *rinfo;
+	struct iwn_ops *ops = &sc->ops;
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ifp->if_l2com;
@@ -3361,13 +3577,12 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
 
 	/* Choose a TX rate index. */
 	rate = params->ibp_rate0;
-	if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
+	ridx = ic->ic_rt->rateCodeToIndex[rate];
+	if (ridx == (uint8_t)-1) {
 		/* XXX fall back to mcast/mgmt rate? */
 		m_freem(m);
 		return EINVAL;
 	}
-	ridx = iwn_plcp_signal(rate);
-	rinfo = &iwn_rates[ridx];
 
 	totlen = m->m_pkthdr.len;
 
@@ -3438,12 +3653,14 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
 	tx->rts_ntries = params->ibp_try1;
 	tx->data_ntries = params->ibp_try0;
 	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
-	tx->plcp = rinfo->plcp;
-	tx->rflags = rinfo->flags;
+	tx->rate = htole32(rate2plcp(rate));
+	if (ridx < IWN_RIDX_OFDM6 &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		tx->rate |= htole32(IWN_RFLAG_CCK);
 	/* Group or management frame. */
 	tx->linkq = 0;
 	txant = IWN_LSB(sc->txchainmask);
-	tx->rflags |= IWN_RFLAG_ANT(txant);
+	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
 	/* Set physical address of "scratch area". */
 	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
 	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
@@ -3514,10 +3731,8 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
 	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
 	    BUS_DMASYNC_PREWRITE);
 
-#ifdef notyet
 	/* Update TX scheduler. */
 	ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
-#endif
 
 	/* Kick TX ring. */
 	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
@@ -3681,6 +3896,7 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 static int
 iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
 {
+	struct iwn_ops *ops = &sc->ops;
 	struct iwn_tx_ring *ring = &sc->txq[4];
 	struct iwn_tx_desc *desc;
 	struct iwn_tx_data *data;
@@ -3689,7 +3905,8 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
 	bus_addr_t paddr;
 	int totlen, error;
 
-	IWN_LOCK_ASSERT(sc);
+	if (async == 0)
+		IWN_LOCK_ASSERT(sc);
 
 	desc = &ring->desc[ring->cur];
 	data = &ring->data[ring->cur];
@@ -3739,10 +3956,8 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
 	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
 	    BUS_DMASYNC_PREWRITE);
 
-#ifdef notyet
 	/* Update TX scheduler. */
 	ops->update_sched(sc, ring->qid, ring->cur, 0, 0);
-#endif
 
 	/* Kick command ring. */
 	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
@@ -3783,9 +3998,8 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
 	struct iwn_node *wn = (void *)ni;
 	struct ieee80211_rateset *rs = &ni->ni_rates;
 	struct iwn_cmd_link_quality linkq;
-	const struct iwn_rate *rinfo;
 	uint8_t txant;
-	int i, txrate;
+	int i, rate, txrate;
 
 	/* Use the first valid TX antenna. */
 	txant = IWN_LSB(sc->txchainmask);
@@ -3794,17 +4008,26 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
 	linkq.id = wn->id;
 	linkq.antmsk_1stream = txant;
 	linkq.antmsk_2stream = IWN_ANT_AB;
-	linkq.ampdu_max = 31;
+	linkq.ampdu_max = 64;
 	linkq.ampdu_threshold = 3;
 	linkq.ampdu_limit = htole16(4000);	/* 4ms */
 
 	/* Start at highest available bit-rate. */
-	txrate = rs->rs_nrates - 1;
+	if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
+		txrate = ni->ni_htrates.rs_nrates - 1;
+	else
+		txrate = rs->rs_nrates - 1;
 	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
-		rinfo = &iwn_rates[wn->ridx[txrate]];
-		linkq.retry[i].plcp = rinfo->plcp;
-		linkq.retry[i].rflags = rinfo->flags;
-		linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
+		if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
+			rate = 0x80 | txrate;
+		else
+			rate = rs->rs_rates[txrate] & IEEE80211_RATE_VAL;
+		linkq.retry[i] = wn->ridx[rate];
+
+		if ((le32toh(wn->ridx[rate]) & IWN_RFLAG_MCS) &&
+		    (le32toh(wn->ridx[rate]) & 0xff) > 7)
+			linkq.mimo = i + 1;
+
 		/* Next retry at immediate lower bit-rate. */
 		if (txrate > 0)
 			txrate--;
@@ -3823,7 +4046,6 @@ iwn_add_broadcast_node(struct iwn_softc *sc, int async)
 	struct ieee80211com *ic = ifp->if_l2com;
 	struct iwn_node_info node;
 	struct iwn_cmd_link_quality linkq;
-	const struct iwn_rate *rinfo;
 	uint8_t txant;
 	int i, error;
 
@@ -3847,16 +4069,13 @@ iwn_add_broadcast_node(struct iwn_softc *sc, int async)
 
 	/* Use lowest mandatory bit-rate. */
 	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
-		rinfo = &iwn_rates[IWN_RIDX_OFDM6];
+		linkq.retry[0] = htole32(0xd);
 	else
-		rinfo = &iwn_rates[IWN_RIDX_CCK1];
-	linkq.retry[0].plcp = rinfo->plcp;
-	linkq.retry[0].rflags = rinfo->flags;
-	linkq.retry[0].rflags |= IWN_RFLAG_ANT(txant);
+		linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK);
+	linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant));
 	/* Use same bit-rate for all TX retries. */
 	for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
-		linkq.retry[i].plcp = linkq.retry[0].plcp;
-		linkq.retry[i].rflags = linkq.retry[0].rflags;
+		linkq.retry[i] = linkq.retry[0];
 	}
 	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
 }
@@ -4908,6 +5127,7 @@ iwn_scan(struct iwn_softc *sc)
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ieee80211com *ic = ifp->if_l2com;
 	struct ieee80211_scan_state *ss = ic->ic_scan;	/*XXX*/
+	struct ieee80211_node *ni = ss->ss_vap->iv_bss;
 	struct iwn_scan_hdr *hdr;
 	struct iwn_cmd_data *tx;
 	struct iwn_scan_essid *essid;
@@ -4956,18 +5176,17 @@ iwn_scan(struct iwn_softc *sc)
 
 	if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) {
 		/* Send probe requests at 6Mbps. */
-		tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp;
+		tx->rate = htole32(0xd);
 		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
 	} else {
 		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
 		/* Send probe requests at 1Mbps. */
-		tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp;
-		tx->rflags = IWN_RFLAG_CCK;
+		tx->rate = htole32(10 | IWN_RFLAG_CCK);
 		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
 	}
 	/* Use the first valid TX antenna. */
 	txant = IWN_LSB(sc->txchainmask);
-	tx->rflags |= IWN_RFLAG_ANT(txant);
+	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
 
 	essid = (struct iwn_scan_essid *)(tx + 1);
 	if (ss->ss_ssid[0].len != 0) {
@@ -4994,10 +5213,8 @@ iwn_scan(struct iwn_softc *sc)
 	frm = ieee80211_add_rates(frm, rs);
 	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
 		frm = ieee80211_add_xrates(frm, rs);
-#if 0	/* HT */
-	if (ic->ic_flags & IEEE80211_F_HTON)
-		frm = ieee80211_add_htcaps(frm, ic);
-#endif
+	if (ic->ic_htcaps & IEEE80211_HTC_HT)
+		frm = ieee80211_add_htcap(frm, ni);
 
 	/* Set length of probe request. */
 	tx->len = htole16(frm - (uint8_t *)wh);
@@ -5124,6 +5341,7 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
 	struct ieee80211com *ic = ifp->if_l2com;
 	struct ieee80211_node *ni = vap->iv_bss;
 	struct iwn_node_info node;
+	uint32_t htflags = 0;
 	int error;
 
 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
@@ -5159,26 +5377,22 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
 		sc->rxon.cck_mask  = 0x0f;
 		sc->rxon.ofdm_mask = 0x15;
 	}
-#if 0	/* HT */
 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
-		sc->rxon.flags &= ~htole32(IWN_RXON_HT);
-		if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
-			sc->rxon.flags |= htole32(IWN_RXON_HT40U);
-		else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
-			sc->rxon.flags |= htole32(IWN_RXON_HT40D);
-		else
-			sc->rxon.flags |= htole32(IWN_RXON_HT20);
-		sc->rxon.rxchain = htole16(
-			  IWN_RXCHAIN_VALID(3)
-			| IWN_RXCHAIN_MIMO_COUNT(3)
-			| IWN_RXCHAIN_IDLE_COUNT(1)
-			| IWN_RXCHAIN_MIMO_FORCE);
-
-		maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
-		ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
-	} else
-		maxrxampdu = ampdudensity = 0;
-#endif
+		htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode);
+		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+			switch (ic->ic_curhtprotmode) {
+			case IEEE80211_HTINFO_OPMODE_HT20PR:
+				htflags |= IWN_RXON_HT_MODEPURE40;
+				break;
+			default:
+				htflags |= IWN_RXON_HT_MODEMIXED;
+				break;
+			}
+		}
+		if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
+			htflags |= IWN_RXON_HT_HT40MINUS;
+	}
+	sc->rxon.flags |= htole32(htflags);
 	sc->rxon.filter |= htole32(IWN_FILTER_BSS);
 	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n",
 	    sc->rxon.chan, sc->rxon.flags);
@@ -5205,10 +5419,20 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
 	memset(&node, 0, sizeof node);
 	IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
 	node.id = IWN_ID_BSS;
-#ifdef notyet
-	node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) |
-	    IWN_AMDPU_DENSITY(5));	/* 2us */
-#endif
+	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
+		switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
+		case IEEE80211_HTCAP_SMPS_ENA:
+			node.htflags |= htole32(IWN_SMPS_MIMO_DIS);
+			break;
+		case IEEE80211_HTCAP_SMPS_DYNAMIC:
+			node.htflags |= htole32(IWN_SMPS_MIMO_PROT);
+			break;
+		}
+		node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) |
+		    IWN_AMDPU_DENSITY(5));	/* 4us */
+		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
+			node.htflags |= htole32(IWN_NODE_HT40);
+	}
 	DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__);
 	error = ops->add_node(sc, &node, 1);
 	if (error != 0) {
@@ -5243,30 +5467,36 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
 #undef MS
 }
 
-#if 0	/* HT */
 /*
  * This function is called by upper layer when an ADDBA request is received
  * from another STA and before the ADDBA response is sent.
  */
 static int
-iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
-    uint8_t tid)
+iwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
+    int baparamset, int batimeout, int baseqctl)
 {
-	struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
-	struct iwn_softc *sc = ic->ic_softc;
+#define MS(_v, _f)	(((_v) & _f) >> _f##_S)
+	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
 	struct iwn_ops *ops = &sc->ops;
 	struct iwn_node *wn = (void *)ni;
 	struct iwn_node_info node;
+	uint8_t tid;
+	int error;
 
+	tid = MS(baparamset, IEEE80211_BAPS_TID);
 	memset(&node, 0, sizeof node);
 	node.id = wn->id;
 	node.control = IWN_NODE_UPDATE;
 	node.flags = IWN_FLAG_SET_ADDBA;
 	node.addba_tid = tid;
-	node.addba_ssn = htole16(ba->ba_winstart);
+	node.addba_ssn = htole16(MS(baseqctl, IEEE80211_BASEQ_START));
 	DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
-	    wn->id, tid, ba->ba_winstart);
-	return ops->add_node(sc, &node, 1);
+	    wn->id, tid, MS(baseqctl, IEEE80211_BASEQ_START));
+	error = ops->add_node(sc, &node, 1);
+	if (error != 0)
+		return error;
+	return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
+#undef MS
 }
 
 /*
@@ -5274,13 +5504,20 @@ iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
  * Block Ack agreement (eg. uppon receipt of a DELBA frame).
  */
 static void
-iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
-    uint8_t tid)
+iwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
 {
-	struct iwn_softc *sc = ic->ic_softc;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct iwn_softc *sc = ic->ic_ifp->if_softc;
 	struct iwn_ops *ops = &sc->ops;
 	struct iwn_node *wn = (void *)ni;
 	struct iwn_node_info node;
+	uint8_t tid;
+
+	/* XXX: tid as an argument */
+	for (tid = 0; tid < WME_NUM_TID; tid++) {
+		if (&ni->ni_rx_ampdu[tid] == rap)
+			break;
+	}
 
 	memset(&node, 0, sizeof node);
 	node.id = wn->id;
@@ -5289,6 +5526,57 @@ iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
 	node.delba_tid = tid;
 	DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
 	(void)ops->add_node(sc, &node, 1);
+	sc->sc_ampdu_rx_stop(ni, rap);
+}
+
+static int
+iwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+    int dialogtoken, int baparamset, int batimeout)
+{
+	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+	int qid;
+
+	for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) {
+		if (sc->qid2tap[qid] == NULL)
+			break;
+	}
+	if (qid == sc->ntxqs) {
+		DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n",
+		    __func__);
+		return 0;
+	}
+	tap->txa_private = malloc(sizeof(int), M_DEVBUF, M_NOWAIT);
+	if (tap->txa_private == NULL) {
+		device_printf(sc->sc_dev,
+		    "%s: failed to alloc TX aggregation structure\n", __func__);
+		return 0;
+	}
+	sc->qid2tap[qid] = tap;
+	*(int *)tap->txa_private = qid;
+	return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
+	    batimeout);
+}
+
+static int
+iwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+    int code, int baparamset, int batimeout)
+{
+	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+	int qid = *(int *)tap->txa_private;
+	uint8_t tid = WME_AC_TO_TID(tap->txa_ac);
+	int ret;
+
+	if (code == IEEE80211_STATUS_SUCCESS) {
+		ni->ni_txseqs[tid] = tap->txa_start & 0xfff;
+		ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid);
+		if (ret != 1)
+			return ret;
+	} else {
+		sc->qid2tap[qid] = NULL;
+		free(tap->txa_private, M_DEVBUF);
+		tap->txa_private = NULL;
+	}
+	return sc->sc_addba_response(ni, tap, code, baparamset, batimeout);
 }
 
 /*
@@ -5299,12 +5587,12 @@ static int
 iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
     uint8_t tid)
 {
-	struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
-	struct iwn_softc *sc = ic->ic_softc;
+	struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)];
+	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
 	struct iwn_ops *ops = &sc->ops;
 	struct iwn_node *wn = (void *)ni;
 	struct iwn_node_info node;
-	int error;
+	int error, qid;
 
 	/* Enable TX for the specified RA/TID. */
 	wn->disable_tid &= ~(1 << tid);
@@ -5315,35 +5603,40 @@ iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
 	node.disable_tid = htole16(wn->disable_tid);
 	error = ops->add_node(sc, &node, 1);
 	if (error != 0)
-		return error;
+		return 0;
 
 	if ((error = iwn_nic_lock(sc)) != 0)
-		return error;
-	ops->ampdu_tx_start(sc, ni, tid, ba->ba_winstart);
+		return 0;
+	qid = *(int *)tap->txa_private;
+	ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff);
 	iwn_nic_unlock(sc);
-	return 0;
+
+	iwn_set_link_quality(sc, ni);
+	return 1;
 }
 
 static void
-iwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
-    uint8_t tid)
+iwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
 {
-	struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
-	struct iwn_softc *sc = ic->ic_softc;
+	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
 	struct iwn_ops *ops = &sc->ops;
+	uint8_t tid = WME_AC_TO_TID(tap->txa_ac);
+	int qid = *(int *)tap->txa_private;
 
 	if (iwn_nic_lock(sc) != 0)
 		return;
-	ops->ampdu_tx_stop(sc, tid, ba->ba_winstart);
+	ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff);
 	iwn_nic_unlock(sc);
+	sc->qid2tap[qid] = NULL;
+	free(tap->txa_private, M_DEVBUF);
+	tap->txa_private = NULL;
 }
 
 static void
 iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
-    uint8_t tid, uint16_t ssn)
+    int qid, uint8_t tid, uint16_t ssn)
 {
 	struct iwn_node *wn = (void *)ni;
-	int qid = 7 + tid;
 
 	/* Stop TX scheduler while we're changing its configuration. */
 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
@@ -5357,6 +5650,7 @@ iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
 	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
 
 	/* Set starting sequence number from the ADDBA request. */
+	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
 
@@ -5377,10 +5671,8 @@ iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
 }
 
 static void
-iwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
+iwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
 {
-	int qid = 7 + tid;
-
 	/* Stop TX scheduler while we're changing its configuration. */
 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
 	    IWN4965_TXQ_STATUS_CHGACT);
@@ -5399,10 +5691,9 @@ iwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
 
 static void
 iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
-    uint8_t tid, uint16_t ssn)
+    int qid, uint8_t tid, uint16_t ssn)
 {
 	struct iwn_node *wn = (void *)ni;
-	int qid = 10 + tid;
 
 	/* Stop TX scheduler while we're changing its configuration. */
 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
@@ -5419,6 +5710,7 @@ iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
 	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
 
 	/* Set starting sequence number from the ADDBA request. */
+	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
 
@@ -5435,10 +5727,8 @@ iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
 }
 
 static void
-iwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
+iwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
 {
-	int qid = 10 + tid;
-
 	/* Stop TX scheduler while we're changing its configuration. */
 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
 	    IWN5000_TXQ_STATUS_CHGACT);
@@ -5457,7 +5747,6 @@ iwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
 	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
 }
-#endif
 
 /*
  * Query calibration tables from the initialization firmware.  We do this
diff --git a/sys/dev/iwn/if_iwnreg.h b/sys/dev/iwn/if_iwnreg.h
index be70b49..4cbf652 100644
--- a/sys/dev/iwn/if_iwnreg.h
+++ b/sys/dev/iwn/if_iwnreg.h
@@ -27,6 +27,9 @@
 #define IWN4965_NTXQUEUES	16
 #define IWN5000_NTXQUEUES	20
 
+#define IWN4965_FIRSTAGGQUEUE	7
+#define IWN5000_FIRSTAGGQUEUE	10
+
 #define IWN4965_NDMACHNLS	7
 #define IWN5000_NDMACHNLS	8
 
@@ -489,6 +492,10 @@ struct iwn_rxon {
 #define IWN_RXON_ANTENNA_A	(1 <<  8)
 #define IWN_RXON_ANTENNA_B	(1 <<  9)
 #define IWN_RXON_TSF		(1 << 15)
+#define IWN_RXON_HT_HT40MINUS	(1 << 22)
+#define IWN_RXON_HT_PROTMODE(x)	(x << 23)
+#define IWN_RXON_HT_MODEPURE40	(1 << 25)
+#define IWN_RXON_HT_MODEMIXED	(2 << 25)
 #define IWN_RXON_CTS_TO_SELF	(1 << 30)
 
 	uint32_t	filter;
@@ -588,7 +595,10 @@ struct iwn_node_info {
 	uint8_t		txmic[8];
 
 	uint32_t	htflags;
+#define IWN_SMPS_MIMO_PROT		(1 << 17)
 #define IWN_AMDPU_SIZE_FACTOR(x)	((x) << 19)
+#define IWN_NODE_HT40			(1 << 21)
+#define IWN_SMPS_MIMO_DIS		(1 << 22)
 #define IWN_AMDPU_DENSITY(x)		((x) << 23)
 
 	uint32_t	mask;
@@ -625,8 +635,13 @@ struct iwn4965_node_info {
 	uint32_t	reserved7;
 } __packed;
 
-#define IWN_RFLAG_CCK		(1 << 1)
-#define IWN_RFLAG_ANT(x)	((x) << 6)
+#define IWN_RFLAG_MCS		(1 << 8)
+#define IWN_RFLAG_CCK		(1 << 9)
+#define IWN_RFLAG_GREENFIELD	(1 << 10)
+#define IWN_RFLAG_HT40		(1 << 11)
+#define IWN_RFLAG_DUPLICATE	(1 << 12)
+#define IWN_RFLAG_SGI		(1 << 13)
+#define IWN_RFLAG_ANT(x)	((x) << 14)
 
 /* Structure for command IWN_CMD_TX_DATA. */
 struct iwn_cmd_data {
@@ -647,9 +662,7 @@ struct iwn_cmd_data {
 #define IWN_TX_NEED_PADDING	(1 << 20)
 
 	uint32_t	scratch;
-	uint8_t		plcp;
-	uint8_t		rflags;
-	uint16_t	xrflags;
+	uint32_t	rate;
 
 	uint8_t		id;
 	uint8_t		security;
@@ -690,11 +703,7 @@ struct iwn_cmd_link_quality {
 	uint8_t		ampdu_threshold;
 	uint8_t		ampdu_max;
 	uint32_t	reserved2;
-	struct {
-		uint8_t		plcp;
-		uint8_t		rflags;
-		uint16_t	xrflags;
-	} __packed	retry[IWN_MAX_TX_RETRIES];
+	uint32_t	retry[IWN_MAX_TX_RETRIES];
 	uint32_t	reserved3;
 } __packed;
 
@@ -1065,9 +1074,7 @@ struct iwn4965_tx_stat {
 	uint8_t		btkillcnt;
 	uint8_t		rtsfailcnt;
 	uint8_t		ackfailcnt;
-	uint8_t		rate;
-	uint8_t		rflags;
-	uint16_t	xrflags;
+	uint32_t	rate;
 	uint16_t	duration;
 	uint16_t	reserved;
 	uint32_t	power[2];
@@ -1079,9 +1086,7 @@ struct iwn5000_tx_stat {
 	uint8_t		btkillcnt;
 	uint8_t		rtsfailcnt;
 	uint8_t		ackfailcnt;
-	uint8_t		rate;
-	uint8_t		rflags;
-	uint16_t	xrflags;
+	uint32_t	rate;
 	uint16_t	duration;
 	uint16_t	reserved;
 	uint32_t	power[2];
@@ -1136,9 +1141,7 @@ struct iwn_rx_stat {
 
 	uint16_t	chan;
 	uint8_t		phybuf[32];
-	uint8_t		rate;
-	uint8_t		rflags;
-	uint16_t	xrflags;
+	uint32_t	rate;
 	uint16_t	len;
 	uint16_t	reserve3;
 } __packed;
@@ -1403,6 +1406,7 @@ struct iwn_fw_tlv {
 #define IWN5000_EEPROM_BAND4	0x02e
 #define IWN5000_EEPROM_BAND5	0x03a
 #define IWN5000_EEPROM_BAND6	0x041
+#define IWN6000_EEPROM_BAND6	0x040
 #define IWN5000_EEPROM_BAND7	0x049
 #define IWN6000_EEPROM_ENHINFO	0x054
 #define IWN5000_EEPROM_CRYSTAL	0x128
@@ -1432,7 +1436,17 @@ struct iwn_eeprom_chan {
 } __packed;
 
 struct iwn_eeprom_enhinfo {
-	uint16_t	chan;
+	uint8_t		flags;
+#define IWN_ENHINFO_VALID	0x01
+#define IWN_ENHINFO_5GHZ	0x02
+#define IWN_ENHINFO_OFDM	0x04
+#define IWN_ENHINFO_HT40	0x08
+#define IWN_ENHINFO_HTAP	0x10
+#define IWN_ENHINFO_RES1	0x20
+#define IWN_ENHINFO_RES2	0x40
+#define IWN_ENHINFO_COMMON	0x80
+
+	uint8_t		chan;
 	int8_t		chain[3];	/* max power in half-dBm */
 	uint8_t		reserved;
 	int8_t		mimo2;		/* max power in half-dBm */
@@ -1486,6 +1500,16 @@ static const uint32_t iwn5000_regulatory_bands[IWN_NBANDS] = {
 	IWN5000_EEPROM_BAND7
 };
 
+static const uint32_t iwn6000_regulatory_bands[IWN_NBANDS] = {
+	IWN5000_EEPROM_BAND1,
+	IWN5000_EEPROM_BAND2,
+	IWN5000_EEPROM_BAND3,
+	IWN5000_EEPROM_BAND4,
+	IWN5000_EEPROM_BAND5,
+	IWN6000_EEPROM_BAND6,
+	IWN5000_EEPROM_BAND7
+};
+
 #define IWN_CHAN_BANDS_COUNT	 7
 #define IWN_MAX_CHAN_PER_BAND	14
 static const struct iwn_chan_band {
@@ -1513,26 +1537,6 @@ static const struct iwn_chan_band {
 #define IWN_RIDX_CCK1	0
 #define IWN_RIDX_OFDM6	4
 
-static const struct iwn_rate {
-	uint8_t	rate;
-	uint8_t	plcp;
-	uint8_t	flags;
-} iwn_rates[IWN_RIDX_MAX + 1] = {
-	{   2,  10, IWN_RFLAG_CCK },
-	{   4,  20, IWN_RFLAG_CCK },
-	{  11,  55, IWN_RFLAG_CCK },
-	{  22, 110, IWN_RFLAG_CCK },
-	{  12, 0xd, 0 },
-	{  18, 0xf, 0 },
-	{  24, 0x5, 0 },
-	{  36, 0x7, 0 },
-	{  48, 0x9, 0 },
-	{  72, 0xb, 0 },
-	{  96, 0x1, 0 },
-	{ 108, 0x3, 0 },
-	{ 120, 0x3, 0 }
-};
-
 #define IWN4965_MAX_PWR_INDEX	107
 
 /*
diff --git a/sys/dev/iwn/if_iwnvar.h b/sys/dev/iwn/if_iwnvar.h
index a61b3b5..5f46a36 100644
--- a/sys/dev/iwn/if_iwnvar.h
+++ b/sys/dev/iwn/if_iwnvar.h
@@ -78,6 +78,7 @@ struct iwn_tx_ring {
 	int			qid;
 	int			queued;
 	int			cur;
+	int			read;
 };
 
 struct iwn_softc;
@@ -101,7 +102,12 @@ struct iwn_node {
 	struct	ieee80211_node		ni;	/* must be the first */
 	uint16_t			disable_tid;
 	uint8_t				id;
-	uint8_t				ridx[IEEE80211_RATE_MAXSIZE];
+	uint32_t			ridx[256];
+	struct {
+		uint64_t		bitmap;
+		int			startidx;
+		int			nframes;
+	} agg[IEEE80211_TID_SIZE];
 };
 
 struct iwn_calib_state {
@@ -174,12 +180,10 @@ struct iwn_ops {
 			    int);
 	void		(*tx_done)(struct iwn_softc *, struct iwn_rx_desc *,
 			    struct iwn_rx_data *);
-#if 0	/* HT */
 	void		(*ampdu_tx_start)(struct iwn_softc *,
-			    struct ieee80211_node *, uint8_t, uint16_t);
-	void		(*ampdu_tx_stop)(struct iwn_softc *, uint8_t,
+			    struct ieee80211_node *, int, uint8_t, uint16_t);
+	void		(*ampdu_tx_stop)(struct iwn_softc *, int, uint8_t,
 			    uint16_t);
-#endif
 };
 
 struct iwn_vap {
@@ -215,6 +219,7 @@ struct iwn_softc {
 	const struct iwn_sensitivity_limits
 				*limits;
 	int			ntxqs;
+	int			firstaggqueue;
 	int			ndmachnls;
 	uint8_t			broadcast_id;
 	int			rxonsz;
@@ -295,7 +300,6 @@ struct iwn_softc {
 	int8_t			maxpwr2GHz;
 	int8_t			maxpwr5GHz;
 	int8_t			maxpwr[IEEE80211_CHAN_MAX];
-	int8_t			enh_maxpwr[35];
 
 	int32_t			temp_off;
 	uint32_t		int_mask;
@@ -307,6 +311,20 @@ struct iwn_softc {
 
 	int			sc_tx_timer;
 
+	struct ieee80211_tx_ampdu *qid2tap[IWN5000_NTXQUEUES];
+
+	int			(*sc_ampdu_rx_start)(struct ieee80211_node *,
+				    struct ieee80211_rx_ampdu *, int, int, int);
+	void			(*sc_ampdu_rx_stop)(struct ieee80211_node *,
+				    struct ieee80211_rx_ampdu *);
+	int			(*sc_addba_request)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *, int, int, int);
+	int			(*sc_addba_response)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *, int, int, int);
+	void			(*sc_addba_stop)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *);
+
+
 	struct iwn_rx_radiotap_header sc_rxtap;
 	struct iwn_tx_radiotap_header sc_txtap;
 };

--Boundary-00=_57VvNvUbwvnkjXW--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201105011524.09613.bschmidt>