Woahhh,

was also trying 5GHz (and tcpbench against one of our bsd servers in DMZ):

469651560 bytes sent over 85.291 seconds
bandwidth min/avg/max/std-dev = 3.475/43.927/87.071/29.809 Mbps


        6 new output block ack agreements
        0 output block ack agreements timed out

(Tomorrow @work I will test against our new APs. My AP @home is a Technicolor 
MediaAccess TG789vac).

mbk Uwe

On 29 Jun 09:48, Uwe Werler wrote:
> Hi Stefan,
> 
> for me the patch works in mode 11n:
> 
> before (OpenBSD 6.7-current (GENERIC.MP) #304: Fri Jun 26 02:08:50 MDT 2020)
> bandwidth min/avg/max/std-dev = 2.354/12.319/15.391/3.850 Mbps
> 
> with patch (OpenBSD 6.7-current (GENERIC.MP) #0: Mon Jun 29 09:35:24 GMT 2020)
> bandwidth min/avg/max/std-dev = 12.174/31.411/57.746/15.154 Mbps
> 
> iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless-AC 8265" rev 0x78, msi
> iwm0: hw rev 0x230, fw ver 34.0.1, address 60:f6:77:bc:3a:04
> 
> (mode 11g: bandwidth min/avg/max/std-dev = 0.620/0.844/1.101/0.153 Mbps)
> 
> mbk Uwe
> 
> 
> On 26 Jun 14:45, Stefan Sperling wrote:
> > This patch adds support for 11n Tx aggregation to iwm(4).
> > 
> > Please help with testing if you can by running the patch and using wifi
> > as usual. Nothing should change, except that Tx speed may potentially
> > improve. If you have time to run before/after performance measurements with
> > tcpbench or such, that would be nice. But it's not required for testing.
> > 
> > If Tx aggregation is active then netstat will show a non-zero output block 
> > ack
> > agreement counter:
> > 
> > $ netstat -W iwm0 | grep 'output block'
> >         3 new output block ack agreements
> >     0 output block ack agreements timed out
> > 
> > It would be great to get at least one test for all the chipsets the driver
> > supports: 7260, 7265, 3160, 3165, 3168, 8260, 8265, 9260, 9560
> > The behaviour of the access point also matters a great deal. It won't
> > hurt to test the same chipset against several different access points.
> > 
> > I have tested this version on 8265 only so far. I've run older revisions
> > of this patch on 7265 so I'm confident that this chip will work, too.
> > So far, the APs I have tested against are athn(4) in 11a mode and in 11n
> > mode with the 'nomimo' nwflag, and a Sagemcom 11ac AP. All on 5Ghz channels.
> > 
> > diff refs/heads/master refs/heads/txagg
> > blob - 3a75d07a60a7eb4c66540474e47aeffd7a85250a
> > blob + 853bdd1290ad509f5fce7b5bf20550f458a2b460
> > --- sys/dev/pci/if_iwm.c
> > +++ sys/dev/pci/if_iwm.c
> > @@ -144,6 +144,8 @@
> >  #include <net80211/ieee80211_amrr.h>
> >  #include <net80211/ieee80211_mira.h>
> >  #include <net80211/ieee80211_radiotap.h>
> > +#include <net80211/ieee80211_priv.h> /* for SEQ_LT */
> > +#undef DPRINTF /* defined in ieee80211_priv.h */
> >  
> >  #define DEVNAME(_s)        ((_s)->sc_dev.dv_xname)
> >  
> > @@ -299,7 +301,8 @@ int     iwm_nic_rx_mq_init(struct iwm_softc *);
> >  int        iwm_nic_tx_init(struct iwm_softc *);
> >  int        iwm_nic_init(struct iwm_softc *);
> >  int        iwm_enable_ac_txq(struct iwm_softc *, int, int);
> > -int        iwm_enable_txq(struct iwm_softc *, int, int, int);
> > +int        iwm_enable_txq(struct iwm_softc *, int, int, int, int, uint8_t,
> > +       uint16_t);
> >  int        iwm_post_alive(struct iwm_softc *);
> >  struct iwm_phy_db_entry *iwm_phy_db_get_section(struct iwm_softc *, 
> > uint16_t,
> >         uint16_t);
> > @@ -334,12 +337,12 @@ void  iwm_ampdu_rx_stop(struct ieee80211com *, struct 
> > i
> >         uint8_t);
> >  void       iwm_sta_rx_agg(struct iwm_softc *, struct ieee80211_node *, 
> > uint8_t,
> >         uint16_t, uint16_t, int);
> > -#ifdef notyet
> > +void       iwm_sta_tx_agg(struct iwm_softc *, struct ieee80211_node *, 
> > uint8_t,
> > +       uint16_t, uint16_t, int);
> >  int        iwm_ampdu_tx_start(struct ieee80211com *, struct ieee80211_node 
> > *,
> >         uint8_t);
> >  void       iwm_ampdu_tx_stop(struct ieee80211com *, struct ieee80211_node 
> > *,
> >         uint8_t);
> > -#endif
> >  void       iwm_ba_task(void *);
> >  
> >  int        iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *,
> > @@ -372,14 +375,25 @@ int   iwm_rxmq_get_signal_strength(struct iwm_softc 
> > *, s
> >  void       iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *,
> >         struct iwm_rx_data *);
> >  int        iwm_get_noise(const struct iwm_statistics_rx_non_phy *);
> > +void       iwm_txq_advance(struct iwm_softc *, struct iwm_tx_ring *, int);
> > +void       iwm_ampdu_tx_done(struct iwm_softc *, struct iwm_cmd_header *,
> > +       struct iwm_node *, struct iwm_tx_ring *, uint32_t, uint8_t,
> > +       uint8_t, uint16_t, int, struct iwm_agg_tx_status *);
> >  int        iwm_ccmp_decap(struct iwm_softc *, struct mbuf *,
> >         struct ieee80211_node *);
> >  void       iwm_rx_frame(struct iwm_softc *, struct mbuf *, int, uint32_t, 
> > int, int,
> >         uint32_t, struct ieee80211_rxinfo *, struct mbuf_list *);
> > -void       iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *,
> > -       struct iwm_node *, int, int);
> > +void       iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_tx_resp *,
> > +       struct iwm_node *, int, int, int);
> > +void       iwm_txd_done(struct iwm_softc *, struct iwm_tx_data *);
> >  void       iwm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *,
> >         struct iwm_rx_data *);
> > +void       iwm_clear_oactive(struct iwm_softc *, struct iwm_tx_ring *);
> > +void       iwm_mira_choose(struct iwm_softc *, struct ieee80211_node *);
> > +void       iwm_ampdu_rate_control(struct iwm_softc *, struct 
> > ieee80211_node *,
> > +       struct iwm_tx_ring *, int, uint16_t, uint16_t);
> > +void       iwm_rx_ba(struct iwm_softc *, struct iwm_rx_packet *,
> > +       struct iwm_rx_data *);
> >  void       iwm_rx_bmiss(struct iwm_softc *, struct iwm_rx_packet *,
> >         struct iwm_rx_data *);
> >  int        iwm_binding_cmd(struct iwm_softc *, struct iwm_node *, 
> > uint32_t);
> > @@ -399,6 +413,7 @@ int     iwm_send_cmd_pdu_status(struct iwm_softc *, 
> > uint32
> >  void       iwm_free_resp(struct iwm_softc *, struct iwm_host_cmd *);
> >  void       iwm_cmd_done(struct iwm_softc *, int, int, int);
> >  void       iwm_update_sched(struct iwm_softc *, int, int, uint8_t, 
> > uint16_t);
> > +void       iwm_reset_sched(struct iwm_softc *, int, int, uint8_t);
> >  const struct iwm_rate *iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node 
> > *,
> >         struct ieee80211_frame *, struct iwm_tx_cmd *);
> >  int        iwm_tx(struct iwm_softc *, struct mbuf *, struct ieee80211_node 
> > *, int);
> > @@ -1306,17 +1321,17 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct 
> > iwm_tx_
> >      * The command is queue 0 (sc->txq[0]), and 4 mgmt/data frame queues
> >      * are sc->tqx[IWM_DQA_MIN_MGMT_QUEUE + ac], i.e. sc->txq[5:8],
> >      * in order to provide one queue per EDCA category.
> > +    * Tx aggregation requires additional queues, one queue per TID for
> > +    * which aggregation is enabled. We map TID 0-7 to sc->txq[10:17].
> >      *
> > -    * In non-DQA mode, we use rings 0 through 9 (0-3 are EDCA, 9 is cmd).
> > +    * In non-DQA mode, we use rings 0 through 9 (0-3 are EDCA, 9 is cmd),
> > +    * and Tx aggregation is not supported.
> >      *
> > -    * Tx aggregation will require additional queues (one queue per TID
> > -    * for which aggregation is enabled) but we do not implement this yet.
> > -    *
> >      * Unfortunately, we cannot tell if DQA will be used until the
> >      * firmware gets loaded later, so just allocate sufficient rings
> >      * in order to satisfy both cases.
> >      */
> > -   if (qid > IWM_CMD_QUEUE)
> > +   if (qid > IWM_LAST_AGG_TX_QUEUE)
> >             return 0;
> >  
> >     size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd);
> > @@ -1380,6 +1395,7 @@ iwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_
> >     bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, 0,
> >         ring->desc_dma.size, BUS_DMASYNC_PREWRITE);
> >     sc->qfullmsk &= ~(1 << ring->qid);
> > +   sc->qenablemsk &= ~(1 << ring->qid);
> >     /* 7000 family NICs are locked while commands are in progress. */
> >     if (ring->qid == sc->cmdqid && ring->queued > 0) {
> >             if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000)
> > @@ -2208,6 +2224,18 @@ const uint8_t iwm_ac_to_tx_fifo[] = {
> >     IWM_TX_FIFO_VO,
> >  };
> >  
> > +/* Map a TID to an ieee80211_edca_ac category. */
> > +const uint8_t iwm_tid_to_ac[IWM_MAX_TID_COUNT] = {
> > +   EDCA_AC_BE,
> > +   EDCA_AC_BK,
> > +   EDCA_AC_BK,
> > +   EDCA_AC_BE,
> > +   EDCA_AC_VI,
> > +   EDCA_AC_VI,
> > +   EDCA_AC_VO,
> > +   EDCA_AC_VO,
> > +};
> > +
> >  int
> >  iwm_enable_ac_txq(struct iwm_softc *sc, int qid, int fifo)
> >  {
> > @@ -2250,28 +2278,48 @@ iwm_enable_ac_txq(struct iwm_softc *sc, int qid, 
> > int f
> >  }
> >  
> >  int
> > -iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo)
> > +iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo,
> > +    int aggregate, uint8_t tid, uint16_t ssn)
> >  {
> > +   struct iwm_tx_ring *ring = &sc->txq[qid];
> >     struct iwm_scd_txq_cfg_cmd cmd;
> > -   int err;
> > +   int err, idx, scd_bug;
> >  
> >     iwm_nic_assert_locked(sc);
> >  
> > -   IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0);
> > +   /*
> > +    * If we need to move the SCD write pointer by steps of
> > +    * 0x40, 0x80 or 0xc0, it gets stuck.
> > +    * This is really ugly, but this is the easiest way out for
> > +    * this sad hardware issue.
> > +    * This bug has been fixed on devices 9000 and up.
> > +    */
> > +   scd_bug = !sc->sc_mqrx_supported &&
> > +           !((ssn - ring->cur) & 0x3f) &&
> > +           (ssn != ring->cur);
> > +   if (scd_bug)
> > +           ssn = (ssn + 1) & 0xfff;
> >  
> > +   idx = IWM_AGG_SSN_TO_TXQ_IDX(ssn);
> > +   IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | idx);
> > +   ring->cur = idx;
> > +   ring->tail = idx;
> > +
> >     memset(&cmd, 0, sizeof(cmd));
> > +   cmd.tid = tid;
> >     cmd.scd_queue = qid;
> >     cmd.enable = 1;
> >     cmd.sta_id = sta_id;
> >     cmd.tx_fifo = fifo;
> > -   cmd.aggregate = 0;
> > +   cmd.aggregate = aggregate;
> > +   cmd.ssn = htole16(ssn);
> >     cmd.window = IWM_FRAME_LIMIT;
> >  
> > -   err = iwm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, 0,
> > -       sizeof(cmd), &cmd);
> > +   err = iwm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd);
> >     if (err)
> >             return err;
> >  
> > +   sc->qenablemsk |= (1 << qid);
> >     return 0;
> >  }
> >  
> > @@ -2950,8 +2998,12 @@ iwm_sta_rx_agg(struct iwm_softc *sc, struct 
> > ieee80211_
> >             if (start) {
> >                     sc->sc_rx_ba_sessions++;
> >                     ieee80211_addba_req_accept(ic, ni, tid);
> > -           } else if (sc->sc_rx_ba_sessions > 0)
> > -                   sc->sc_rx_ba_sessions--;
> > +                   in->ampdu_rx_tid_mask |= (1 << tid);
> > +           } else {
> > +                   in->ampdu_rx_tid_mask &= ~(1 << tid);
> > +                   if (sc->sc_rx_ba_sessions > 0)
> > +                           sc->sc_rx_ba_sessions--;
> > +           }
> >     } else if (start)
> >             ieee80211_addba_req_refuse(ic, ni, tid);
> >  
> > @@ -2959,6 +3011,75 @@ iwm_sta_rx_agg(struct iwm_softc *sc, struct 
> > ieee80211_
> >  }
> >  
> >  void
> > +iwm_sta_tx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, uint8_t 
> > tid,
> > +    uint16_t ssn, uint16_t winsize, int start)
> > +{
> > +   struct ieee80211com *ic = &sc->sc_ic;
> > +   struct iwm_node *in = (void *)ni;
> > +   int qid = IWM_FIRST_AGG_TX_QUEUE + tid;
> > +   enum ieee80211_edca_ac ac = iwm_tid_to_ac[tid];
> > +   int fifo = iwm_ac_to_tx_fifo[ac];
> > +   int err;
> > +
> > +   if (qid > IWM_LAST_AGG_TX_QUEUE || !iwm_nic_lock(sc)) {
> > +           ieee80211_addba_resp_refuse(ic, ni, tid,
> > +               IEEE80211_STATUS_UNSPECIFIED);
> > +           return;
> > +   }
> > +
> > +   if (start) {
> > +           if ((sc->qenablemsk & (1 << qid)) == 0) {
> > +                   struct iwm_tx_ring *ring = &sc->txq[qid];
> > +
> > +                   err = iwm_enable_txq(sc, IWM_STATION_ID, qid, fifo,
> > +                       1, tid, ssn);
> > +                   if (err)
> > +                           goto done;
> > +                   /*
> > +                    * If iwm_enable_txq() employed the SCD hardware bug
> > +                    * workaround we must skip the frame with seqnum SSN.
> > +                    */
> > +                   if (IWM_AGG_SSN_TO_TXQ_IDX(ring->cur) !=
> > +                       IWM_AGG_SSN_TO_TXQ_IDX(ssn)) {
> > +                           ssn = (ssn + 1) & 0xfff;
> > +                           KASSERT(IWM_AGG_SSN_TO_TXQ_IDX(ring->cur) ==
> > +                               IWM_AGG_SSN_TO_TXQ_IDX(ssn));
> > +                           ieee80211_output_ba_move_window(ic, ni,
> > +                               tid, ssn);
> > +                           ni->ni_qos_txseqs[tid] = ssn;
> > +                   }
> > +           }
> > +           in->ampdu_tx_tid_mask |= (1 << tid);
> > +           err = iwm_add_sta_cmd(sc, in, 1);
> > +           if (err) {
> > +                   printf("%s: could not update sta (error %d)\n",
> > +                       DEVNAME(sc), err);
> > +                   in->ampdu_tx_tid_mask &= ~(1 << tid);
> > +                   goto done;
> > +           }
> > +   } else {
> > +           in->ampdu_tx_tid_mask &= ~(1 << tid);
> > +           err = iwm_add_sta_cmd(sc, in, 1);
> > +           if (err) {
> > +                   printf("%s: could not update sta (error %d)\n",
> > +                       DEVNAME(sc), err);
> > +                   in->ampdu_tx_tid_mask |= (1 << tid);
> > +                   goto done;
> > +           }
> > +   }
> > +
> > +done:
> > +   iwm_nic_unlock(sc);
> > +   if (start) {
> > +           if (err)
> > +                   ieee80211_addba_resp_refuse(ic, ni, tid,
> > +                       IEEE80211_STATUS_UNSPECIFIED);
> > +           else
> > +                   ieee80211_addba_resp_accept(ic, ni, tid);
> > +   }
> > +}
> > +
> > +void
> >  iwm_htprot_task(void *arg)
> >  {
> >     struct iwm_softc *sc = arg;
> > @@ -3002,19 +3123,53 @@ iwm_ba_task(void *arg)
> >     struct ieee80211com *ic = &sc->sc_ic;
> >     struct ieee80211_node *ni = ic->ic_bss;
> >     int s = splnet();
> > +   int tid;
> >  
> > -   if (sc->sc_flags & IWM_FLAG_SHUTDOWN) {
> > +   if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) ||
> > +       ic->ic_state != IEEE80211_S_RUN) {
> >             refcnt_rele_wake(&sc->task_refs);
> >             splx(s);
> >             return;
> >     }
> >     
> > -   if (sc->ba_start)
> > -           iwm_sta_rx_agg(sc, ni, sc->ba_tid, sc->ba_ssn,
> > -               sc->ba_winsize, 1);
> > -   else
> > -           iwm_sta_rx_agg(sc, ni, sc->ba_tid, 0, 0, 0);
> > +   if (sc->ba_flags & IWM_RX_BA_START) {
> > +           for (tid = 0; tid < IWM_MAX_TID_COUNT; tid++) {
> > +                   if ((sc->rx_ba_start.tid_mask & (1 << tid)) == 0)
> > +                           continue;
> > +                   iwm_sta_rx_agg(sc, ni, tid, sc->rx_ba_start.ssn[tid],
> > +                       sc->rx_ba_start.winsize[tid], 1);
> > +                   sc->rx_ba_start.tid_mask &= ~(1 << tid);
> > +           }
> > +   }
> > +   
> > +   if (sc->ba_flags & IWM_RX_BA_STOP) {
> > +           for (tid = 0; tid < IWM_MAX_TID_COUNT; tid++) {
> > +                   if ((sc->rx_ba_stop.tid_mask & (1 << tid)) == 0)
> > +                           continue;
> > +                   iwm_sta_rx_agg(sc, ni, tid, 0, 0, 0);
> > +                   sc->rx_ba_stop.tid_mask &= ~(1 << tid);
> > +           }
> > +   }
> >  
> > +   if (sc->ba_flags & IWM_TX_BA_START) {
> > +           for (tid = 0; tid < IWM_MAX_TID_COUNT; tid++) {
> > +                   if ((sc->tx_ba_start.tid_mask & (1 << tid)) == 0)
> > +                           continue;
> > +                   iwm_sta_tx_agg(sc, ni, tid, sc->tx_ba_start.ssn[tid],
> > +                       sc->tx_ba_start.winsize[tid], 1);
> > +                   sc->tx_ba_start.tid_mask &= ~(1 << tid);
> > +           }
> > +   }
> > +   
> > +   if (sc->ba_flags & IWM_TX_BA_STOP) {
> > +           for (tid = 0; tid < IWM_MAX_TID_COUNT; tid++) {
> > +                   if ((sc->tx_ba_stop.tid_mask & (1 << tid)) == 0)
> > +                           continue;
> > +                   iwm_sta_tx_agg(sc, ni, tid, 0, 0, 0);
> > +                   sc->tx_ba_stop.tid_mask &= ~(1 << tid);
> > +           }
> > +   }
> > +
> >     refcnt_rele_wake(&sc->task_refs);
> >     splx(s);
> >  }
> > @@ -3029,14 +3184,23 @@ iwm_ampdu_rx_start(struct ieee80211com *ic, struct 
> > iee
> >  {
> >     struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
> >     struct iwm_softc *sc = IC2IFP(ic)->if_softc;
> > +   struct iwm_node *in = (void *)ni;
> >  
> > -   if (sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS)
> > +   if (tid >= IWM_MAX_TID_COUNT)
> > +           return EINVAL;
> > +
> > +   if (sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS ||
> > +       (in->ampdu_rx_tid_mask & (1 << tid)) != 0)
> >             return ENOSPC;
> >  
> > -   sc->ba_start = 1;
> > -   sc->ba_tid = tid;
> > -   sc->ba_ssn = htole16(ba->ba_winstart);
> > -   sc->ba_winsize = htole16(ba->ba_winsize);
> > +   if (sc->rx_ba_start.tid_mask & (1 << tid) ||
> > +       sc->rx_ba_stop.tid_mask & (1 << tid))
> > +           return EAGAIN;
> > +
> > +   sc->ba_flags |= IWM_RX_BA_START;
> > +   sc->rx_ba_start.tid_mask |= (1 << tid);
> > +   sc->rx_ba_start.ssn[tid] = htole16(ba->ba_winstart);
> > +   sc->rx_ba_start.winsize[tid] = htole16(ba->ba_winsize);
> >     iwm_add_task(sc, systq, &sc->ba_task);
> >  
> >     return EBUSY;
> > @@ -3051,13 +3215,69 @@ iwm_ampdu_rx_stop(struct ieee80211com *ic, struct 
> > ieee
> >      uint8_t tid)
> >  {
> >     struct iwm_softc *sc = IC2IFP(ic)->if_softc;
> > +   struct iwm_node *in = (void *)ni;
> >  
> > -   sc->ba_start = 0;
> > -   sc->ba_tid = tid;
> > +   if (in->ampdu_rx_tid_mask & (1 << tid))  {
> > +           sc->ba_flags |= IWM_RX_BA_STOP;
> > +           sc->rx_ba_stop.tid_mask |= (1 << tid);
> > +           iwm_add_task(sc, systq, &sc->ba_task);
> > +   }
> > +}
> > +
> > +int
> > +iwm_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
> > +    uint8_t tid)
> > +{
> > +   struct iwm_softc *sc = IC2IFP(ic)->if_softc;
> > +   struct iwm_node *in = (void *)ni;
> > +   struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
> > +
> > +   /* We only implement Tx aggregation with DQA-capable firmware. */
> > +   if (!isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
> > +           return ENOTSUP;
> > +
> > +   /* Ensure we can map this TID to an aggregation queue. */
> > +   if (tid >= IWM_MAX_TID_COUNT)
> > +           return EINVAL;
> > +
> > +   /* We only support a fixed Tx aggregation window size, for now. */
> > +   if (ba->ba_winsize != IWM_FRAME_LIMIT)
> > +           return ENOTSUP;
> > +
> > +   if ((in->ampdu_tx_tid_mask & (1 << tid)) != 0)
> > +           return ENOSPC;
> > +
> > +   if (sc->tx_ba_start.tid_mask & (1 << tid) ||
> > +       sc->tx_ba_stop.tid_mask & (1 << tid))
> > +           return EAGAIN;
> > +
> > +   sc->ba_flags |= IWM_TX_BA_START;
> > +   sc->tx_ba_start.tid_mask |= (1 << tid);
> > +   sc->tx_ba_start.ssn[tid] = htole16(ba->ba_winstart);
> > +   sc->tx_ba_start.winsize[tid] = htole16(ba->ba_winsize);
> >     iwm_add_task(sc, systq, &sc->ba_task);
> > +
> > +   return EBUSY;
> >  }
> >  
> >  void
> > +iwm_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
> > +    uint8_t tid)
> > +{
> > +   struct iwm_softc *sc = IC2IFP(ic)->if_softc;
> > +   struct iwm_node *in = (void *)ni;
> > +
> > +   if (tid >= IWM_MAX_TID_COUNT)
> > +           return;
> > +
> > +   if (in->ampdu_tx_tid_mask & (1 << tid))  {
> > +           sc->ba_flags |= IWM_TX_BA_STOP;
> > +           sc->tx_ba_stop.tid_mask |= (1 << tid);
> > +           iwm_add_task(sc, systq, &sc->ba_task);
> > +   }
> > +}
> > +
> > +void
> >  iwm_set_hw_address_8000(struct iwm_softc *sc, struct iwm_nvm_data *data,
> >      const uint16_t *mac_override, const uint16_t *nvm_hw)
> >  {
> > @@ -4238,13 +4458,178 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf 
> > *m, v
> >  }
> >  
> >  void
> > -iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
> > -    struct iwm_node *in, int txmcs, int txrate)
> > +iwm_txq_advance(struct iwm_softc *sc, struct iwm_tx_ring *ring, int idx)
> >  {
> > +   struct iwm_tx_data *txd;
> > +
> > +   while (ring->tail != idx) {
> > +           txd = &ring->data[ring->tail];
> > +           if (txd->m != NULL) {
> > +                   if (ring->qid < IWM_FIRST_AGG_TX_QUEUE)
> > +                           DPRINTF(("%s: missed Tx completion: tail=%d "
> > +                               "idx=%d\n", __func__, ring->tail, idx));
> > +                   iwm_reset_sched(sc, ring->qid, ring->tail, 
> > IWM_STATION_ID);
> > +                   iwm_txd_done(sc, txd);
> > +                   ring->queued--;
> > +           }
> > +           ring->tail = (ring->tail + 1) % IWM_TX_RING_COUNT;
> > +   }
> > +}
> > +
> > +void
> > +iwm_ampdu_tx_done(struct iwm_softc *sc, struct iwm_cmd_header *cmd_hdr,
> > +    struct iwm_node *in, struct iwm_tx_ring *txq, uint32_t initial_rate,
> > +    uint8_t nframes, uint8_t failure_frame, uint16_t ssn, int status,
> > +    struct iwm_agg_tx_status *agg_status)
> > +{
> >     struct ieee80211com *ic = &sc->sc_ic;
> > +   int tid = cmd_hdr->qid - IWM_FIRST_AGG_TX_QUEUE;
> > +   struct iwm_tx_data *txdata = &txq->data[cmd_hdr->idx];
> >     struct ieee80211_node *ni = &in->in_ni;
> > +   int txfail = (status != IWM_TX_STATUS_SUCCESS &&
> > +       status != IWM_TX_STATUS_DIRECT_DONE);
> > +   struct ieee80211_tx_ba *ba;
> > +
> > +   sc->sc_tx_timer = 0;
> > +
> > +   if (ic->ic_state != IEEE80211_S_RUN)
> > +           return;
> > +
> > +   if (nframes > 1) {
> > +           int ampdu_id, have_ampdu_id = 0, ampdu_size = 0;
> > +           int i;
> > +
> > +           /* Compute the size of this A-MPDU. */
> > +           for (i = 0; i < nframes; i++) {
> > +                   uint8_t qid = agg_status[i].qid;
> > +                   uint8_t idx = agg_status[i].idx;
> > +
> > +                   if (qid != cmd_hdr->qid)
> > +                           continue;
> > +
> > +                   txdata = &txq->data[idx];
> > +                   if (txdata->m == NULL)
> > +                           continue;
> > +
> > +                   ampdu_size += txdata->totlen + IEEE80211_CRC_LEN;
> > +           }
> > +
> > +           /*
> > +            * For each subframe collect Tx status, retries, and Tx rate.
> > +            * (The Tx rate is the same for all subframes in this batch.)
> > +            */
> > +           for (i = 0; i < nframes; i++) {
> > +                   uint8_t qid = agg_status[i].qid;
> > +                   uint8_t idx = agg_status[i].idx;
> > +                   uint16_t txstatus = (le16toh(agg_status[i].status) &
> > +                       IWM_AGG_TX_STATE_STATUS_MSK);
> > +                   uint16_t trycnt = (le16toh(agg_status[i].status) &
> > +                       IWM_AGG_TX_STATE_TRY_CNT_MSK) >>
> > +                       IWM_AGG_TX_STATE_TRY_CNT_POS;
> > +
> > +                   if (qid != cmd_hdr->qid)
> > +                           continue;
> > +
> > +                   txdata = &txq->data[idx];
> > +                   if (txdata->m == NULL)
> > +                           continue;
> > +
> > +                   if (initial_rate & IWM_RATE_MCS_HT_MSK)
> > +                           txdata->ampdu_txmcs = (initial_rate &
> > +                               (IWM_RATE_HT_MCS_RATE_CODE_MSK |
> > +                               IWM_RATE_HT_MCS_NSS_MSK));
> > +                   if (txstatus != IWM_AGG_TX_STATE_TRANSMITTED)
> > +                           txdata->txfail++;
> > +                   if (trycnt > 1)
> > +                           txdata->retries++;
> > +
> > +                   /*
> > +                    * Assign a common ID to all subframes of this A-MPDU.
> > +                    * This ID will be used during Tx rate control to
> > +                    * infer the ACK status of individual subframes.
> > +                    */
> > +                   if (!have_ampdu_id) {
> > +                           ampdu_id = txdata->in->next_ampdu_id++;
> > +                           have_ampdu_id = 1;
> > +                   }
> > +                   txdata->ampdu_id = ampdu_id;
> > +
> > +                   /*
> > +                    * We will also need to know the total number of
> > +                    * subframes and the size of this A-MPDU. We store
> > +                    * this redundantly on each subframe because firmware
> > +                    * only reports acknowledged subframes via compressed
> > +                    * block-ack notification. This way we will know what
> > +                    * the total number of subframes and size were even if
> > +                    * just one of these subframes gets acknowledged.
> > +                    */
> > +                   txdata->ampdu_nframes = nframes;
> > +                   txdata->ampdu_size = ampdu_size;
> > +           }
> > +           return;
> > +   }
> > +
> > +   if (ni == NULL)
> > +           return;
> > +
> > +   ba = &ni->ni_tx_ba[tid];
> > +   if (ba->ba_state != IEEE80211_BA_AGREED)
> > +           return;
> > +
> > +   /* This is a final single-frame Tx attempt. */
> > +   DPRINTFN(3, ("%s: final tx status=0x%x qid=%d queued=%d idx=%d ssn=%u "
> > +       "bitmap=0x%llx\n", __func__, status, desc->qid, txq->queued,
> > +       desc->idx, ssn, ba->ba_bitmap));
> > +
> > +   /*
> > +    * Skip rate control if our Tx rate is fixed.
> > +    * Don't report frames to MiRA which were sent at a different
> > +    * Tx rate than ni->ni_txmcs.
> > +    */
> > +   if (ic->ic_fixed_mcs == -1 && txdata->txmcs == ni->ni_txmcs) {
> > +           in->in_mn.frames++;
> > +           in->in_mn.agglen = 1;
> > +           in->in_mn.ampdu_size = txdata->totlen + IEEE80211_CRC_LEN;
> > +           if (failure_frame > 0)
> > +                   in->in_mn.retries++;
> > +           if (txfail)
> > +                   in->in_mn.txfail++;
> > +           iwm_mira_choose(sc, ni);
> > +   }
> > +
> > +   if (txfail)
> > +           ieee80211_tx_compressed_bar(ic, ni, tid, ssn);
> > +   else if (!SEQ_LT(ssn, ba->ba_winstart)) {
> > +           /*
> > +            * Move window forward if SSN lies beyond end of window,
> > +            * otherwise we can't record the ACK for this frame.
> > +            * Non-acked frames which left holes in the bitmap near
> > +            * the beginning of the window must be discarded.
> > +            */
> > +           uint16_t s = ssn;
> > +           while (SEQ_LT(ba->ba_winend, s)) {
> > +                   ieee80211_output_ba_move_window(ic, ni, tid, s);
> > +                   iwm_txq_advance(sc, txq, IWM_AGG_SSN_TO_TXQ_IDX(s));
> > +                   s = (s + 1) % 0xfff;
> > +           }
> > +           /* SSN should now be within window; set corresponding bit. */
> > +           ieee80211_output_ba_record_ack(ic, ni, tid, ssn);
> > +   }
> > +
> > +   /* Move window forward up to the first hole in the bitmap. */
> > +   ieee80211_output_ba_move_window_to_first_unacked(ic, ni, tid, ssn);
> > +   iwm_txq_advance(sc, txq, IWM_AGG_SSN_TO_TXQ_IDX(ba->ba_winstart));
> > +
> > +   iwm_clear_oactive(sc, txq);
> > +}
> > +
> > +void
> > +iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_tx_resp *tx_resp,
> > +    struct iwm_node *in, int txmcs, int txrate, int qid)
> > +{
> > +   struct ieee80211com *ic = &sc->sc_ic;
> > +   struct ieee80211_node *ni = &in->in_ni;
> >     struct ifnet *ifp = IC2IFP(ic);
> > -   struct iwm_tx_resp *tx_resp = (void *)pkt->data;
> >     int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK;
> >     int txfail;
> >     
> > @@ -4277,22 +4662,8 @@ iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct 
> > iwm_
> >                     in->in_mn.retries += tx_resp->failure_frame;
> >             if (txfail)
> >                     in->in_mn.txfail += tx_resp->frame_count;
> > -           if (ic->ic_state == IEEE80211_S_RUN) {
> > -                   int best_mcs;
> > -
> > -                   ieee80211_mira_choose(&in->in_mn, ic, &in->in_ni);
> > -                   /* 
> > -                    * If MiRA has chosen a new TX rate we must update
> > -                    * the firwmare's LQ rate table from process context.
> > -                    * ni_txmcs may change again before the task runs so
> > -                    * cache the chosen rate in the iwm_node structure.
> > -                    */
> > -                   best_mcs = ieee80211_mira_get_best_mcs(&in->in_mn);
> > -                   if (best_mcs != in->chosen_txmcs) {
> > -                           in->chosen_txmcs = best_mcs;
> > -                           iwm_setrates(in, 1);
> > -                   }
> > -           }
> > +           if (ic->ic_state == IEEE80211_S_RUN)
> > +                   iwm_mira_choose(sc, ni);
> >     }
> >  
> >     if (txfail)
> > @@ -4313,49 +4684,91 @@ iwm_txd_done(struct iwm_softc *sc, struct 
> > iwm_tx_data 
> >     KASSERT(txd->in);
> >     ieee80211_release_node(ic, &txd->in->in_ni);
> >     txd->in = NULL;
> > +
> > +   txd->retries = 0;
> > +   txd->txfail = 0;
> > +   txd->txmcs = 0;
> > +   txd->ampdu_txmcs = 0;
> > +   txd->txrate = 0;
> >  }
> >  
> >  void
> >  iwm_rx_tx_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
> >      struct iwm_rx_data *data)
> >  {
> > -   struct ieee80211com *ic = &sc->sc_ic;
> > -   struct ifnet *ifp = IC2IFP(ic);
> >     struct iwm_cmd_header *cmd_hdr = &pkt->hdr;
> >     int idx = cmd_hdr->idx;
> >     int qid = cmd_hdr->qid;
> >     struct iwm_tx_ring *ring = &sc->txq[qid];
> >     struct iwm_tx_data *txd;
> > +   struct iwm_tx_resp *tx_resp = (void *)pkt->data;
> > +   uint32_t ssn;
> > +   uint32_t len = iwm_rx_packet_len(pkt);
> >  
> >     bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
> >         BUS_DMASYNC_POSTREAD);
> >  
> >     sc->sc_tx_timer = 0;
> >  
> > +   /* Sanity checks. */
> > +   if (sizeof(*tx_resp) > len)
> > +           return;
> > +   if (qid < IWM_FIRST_AGG_TX_QUEUE && tx_resp->frame_count > 1)
> > +           return;
> > +   if (qid >= IWM_FIRST_AGG_TX_QUEUE && sizeof(*tx_resp) + sizeof(ssn) +
> > +       tx_resp->frame_count * sizeof(tx_resp->status) > len)
> > +           return;
> > +
> > +   /*
> > +    * In the multi-frame case the firmware has just transmitted a bunch
> > +    * of frames in an A-MPDU. The final Tx status of those frames won't
> > +    * be known until the peer ACKs subframes with a block ack or firmware
> > +    * gives up on a particular subframe.
> > +    * Subframes for which the firmware never sees an ACK will be retried
> > +    * and will eventually arrive here as a single-frame Tx failure.
> > +    * So there is nothing to do, for now.
> > +    */
> > +   if (tx_resp->frame_count != 1)
> > +           return;
> > +
> >     txd = &ring->data[idx];
> >     if (txd->m == NULL)
> >             return;
> >  
> > -   iwm_rx_tx_cmd_single(sc, pkt, txd->in, txd->txmcs, txd->txrate);
> > -   iwm_txd_done(sc, txd);
> > +   if (qid >= IWM_FIRST_AGG_TX_QUEUE) {
> > +           int status;
> >  
> > -   /*
> > -    * XXX Sometimes we miss Tx completion interrupts.
> > -    * We cannot check Tx success/failure for affected frames; just free
> > -    * the associated mbuf and release the associated node reference.
> > -    */
> > -   while (ring->tail != idx) {
> > -           txd = &ring->data[ring->tail];
> > -           if (txd->m != NULL) {
> > -                   DPRINTF(("%s: missed Tx completion: tail=%d idx=%d\n",
> > -                       __func__, ring->tail, idx));
> > -                   iwm_txd_done(sc, txd);
> > -                   ring->queued--;
> > -           }
> > -           ring->tail = (ring->tail + 1) % IWM_TX_RING_COUNT;
> > +           memcpy(&ssn, &tx_resp->status + tx_resp->frame_count, 
> > sizeof(ssn));
> > +           ssn = le32toh(ssn) & 0xfff;
> > +           status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK;
> > +           iwm_ampdu_tx_done(sc, cmd_hdr, txd->in, ring,
> > +               le32toh(tx_resp->initial_rate), tx_resp->frame_count,
> > +               tx_resp->failure_frame, ssn, status, &tx_resp->status);
> > +   } else {
> > +           iwm_rx_tx_cmd_single(sc, tx_resp, txd->in, txd->txmcs,
> > +               txd->txrate, qid);
> > +           iwm_reset_sched(sc, qid, idx, IWM_STATION_ID);
> > +           iwm_txd_done(sc, txd);
> > +           ring->queued--;
> > +
> > +           /*
> > +            * XXX Sometimes we miss Tx completion interrupts.
> > +            * We cannot check Tx success/failure for affected frames;
> > +            * just free the associated mbuf and release the associated
> > +            * node reference.
> > +            */
> > +           iwm_txq_advance(sc, ring, idx);
> > +           iwm_clear_oactive(sc, ring);
> >     }
> > +}
> >  
> > -   if (--ring->queued < IWM_TX_RING_LOMARK) {
> > +void
> > +iwm_clear_oactive(struct iwm_softc *sc, struct iwm_tx_ring *ring)
> > +{
> > +   struct ieee80211com *ic = &sc->sc_ic;
> > +   struct ifnet *ifp = IC2IFP(ic);
> > +
> > +   if (ring->queued < IWM_TX_RING_LOMARK) {
> >             sc->qfullmsk &= ~(1 << ring->qid);
> >             if (sc->qfullmsk == 0 && ifq_is_oactive(&ifp->if_snd)) {
> >                     ifq_clr_oactive(&ifp->if_snd);
> > @@ -4370,6 +4783,183 @@ iwm_rx_tx_cmd(struct iwm_softc *sc, struct 
> > iwm_rx_pack
> >  }
> >  
> >  void
> > +iwm_mira_choose(struct iwm_softc *sc, struct ieee80211_node *ni)
> > +{
> > +   struct ieee80211com *ic = &sc->sc_ic;
> > +   struct iwm_node *in = (void *)ni;
> > +   int best_mcs = ieee80211_mira_get_best_mcs(&in->in_mn);
> > +
> > +   ieee80211_mira_choose(&in->in_mn, ic, ni);
> > +
> > +   /*
> > +    * Update firmware's LQ retry table if MiRA has chosen a new MCS.
> > +    *
> > +    * We only need to do this if the best MCS has changed because
> > +    * we ask firmware to use a fixed MCS while MiRA is probing a
> > +    * candidate MCS.
> > +    * While not probing we ask firmware to retry at lower rates in case
> > +    * Tx at the newly chosen best MCS ends up failing, and then report
> > +    * any resulting Tx retries to MiRA in order to trigger probing.
> > +    */
> > +   if (best_mcs != ieee80211_mira_get_best_mcs(&in->in_mn)) {
> > +           in->chosen_txmcs = best_mcs;
> > +           iwm_setrates(in, 1);
> > +   }
> > +}
> > +
> > +void
> > +iwm_ampdu_rate_control(struct iwm_softc *sc, struct ieee80211_node *ni,
> > +    struct iwm_tx_ring *txq, int tid, uint16_t seq, uint16_t ssn)
> > +{
> > +   struct ieee80211com *ic = &sc->sc_ic;
> > +   struct iwm_node *in = (void *)ni;
> > +   struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
> > +   int min_ampdu_id, max_ampdu_id, id;
> > +   int idx, end_idx;
> > +
> > +   /* Determine the min/max IDs we assigned to AMPDUs in this range. */
> > +   idx = IWM_AGG_SSN_TO_TXQ_IDX(seq);
> > +   end_idx = IWM_AGG_SSN_TO_TXQ_IDX(ssn);
> > +   min_ampdu_id = txq->data[idx].ampdu_id;
> > +   max_ampdu_id = min_ampdu_id;
> > +   while (idx != end_idx) {
> > +           struct iwm_tx_data *txdata = &txq->data[idx];
> > +
> > +           if (txdata->m != NULL) {
> > +                   if (min_ampdu_id > txdata->ampdu_id)
> > +                           min_ampdu_id = txdata->ampdu_id;
> > +                   if (max_ampdu_id < txdata->ampdu_id)
> > +                           max_ampdu_id = txdata->ampdu_id;
> > +           }
> > +
> > +           idx = (idx + 1) % IWM_TX_RING_COUNT;
> > +   }
> > +
> > +   /*
> > +    * Update Tx rate statistics for A-MPDUs before firmware's BA window.
> > +    */
> > +   for (id = min_ampdu_id; id <= max_ampdu_id; id++) {
> > +           int have_ack = 0, bit = 0;
> > +           idx = IWM_AGG_SSN_TO_TXQ_IDX(seq);
> > +           end_idx = IWM_AGG_SSN_TO_TXQ_IDX(ssn);
> > +           in->in_mn.agglen = 0;
> > +           in->in_mn.ampdu_size = 0;
> > +           while (idx != end_idx) {
> > +                   struct iwm_tx_data *txdata = &txq->data[idx];
> > +                   uint16_t s = (seq + bit) & 0xfff;
> > +                   /*
> > +                    * We can assume that this subframe has been ACKed
> > +                    * because ACK failures come as single frames and
> > +                    * before failing an A-MPDU subframe the firmware
> > +                    * sends it as a single frame at least once.
> > +                    *
> > +                    * However, when this A-MPDU was transmitted we
> > +                    * learned how many subframes it contained.
> > +                    * So if firmware isn't reporting all subframes now
> > +                    * we can deduce an ACK failure for missing frames.
> > +                    */
> > +                   if (txdata->m != NULL && txdata->ampdu_id == id &&
> > +                       txdata->ampdu_txmcs == ni->ni_txmcs &&
> > +                       txdata->ampdu_nframes > 0 &&
> > +                       (SEQ_LT(ba->ba_winend, s) ||
> > +                       (ba->ba_bitmap & (1 << bit)) == 0)) {
> > +                           have_ack++;
> > +                           in->in_mn.frames = txdata->ampdu_nframes;
> > +                           in->in_mn.agglen = txdata->ampdu_nframes;
> > +                           in->in_mn.ampdu_size = txdata->ampdu_size;
> > +                           if (txdata->retries > 1)
> > +                                   in->in_mn.retries++;
> > +                           if (!SEQ_LT(ba->ba_winend, s))
> > +                                   ieee80211_output_ba_record_ack(ic, ni,
> > +                                       tid, s);
> > +                   }
> > +
> > +                   idx = (idx + 1) % IWM_TX_RING_COUNT;
> > +                   bit++;
> > +           }
> > +
> > +           if (have_ack > 0) {
> > +                   in->in_mn.txfail = in->in_mn.frames - have_ack;
> > +                   iwm_mira_choose(sc, ni);
> > +           }
> > +   }
> > +}
> > +
> > +void
> > +iwm_rx_ba(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
> > +    struct iwm_rx_data *data)
> > +{
> > +   struct iwm_ba_notif *ban = (void *)pkt->data;
> > +   struct ieee80211com *ic = &sc->sc_ic;
> > +   struct ieee80211_node *ni;
> > +   struct ieee80211_tx_ba *ba;
> > +   struct iwm_node *in;
> > +   struct iwm_tx_ring *ring;
> > +   uint16_t seq, ssn, idx;
> > +   int qid;
> > +
> > +   if (ic->ic_state != IEEE80211_S_RUN)
> > +           return;
> > +
> > +   if (iwm_rx_packet_payload_len(pkt) < sizeof(*ban))
> > +           return;
> > +
> > +   if (ban->sta_id != IWM_STATION_ID ||
> > +       !IEEE80211_ADDR_EQ(ic->ic_bss->ni_macaddr, ban->sta_addr))
> > +           return;
> > +
> > +   ni = ic->ic_bss;
> > +   in = (void *)ni;
> > +
> > +   qid = le16toh(ban->scd_flow);
> > +   if (qid < IWM_FIRST_AGG_TX_QUEUE || qid > IWM_LAST_AGG_TX_QUEUE)
> > +           return;
> > +
> > +   /* Protect against a firmware bug where the queue/TID are off. */
> > +   if (qid != IWM_FIRST_AGG_TX_QUEUE + ban->tid)
> > +           return;
> > +
> > +   ba = &ni->ni_tx_ba[ban->tid];
> > +   if (ba->ba_state != IEEE80211_BA_AGREED)
> > +           return;
> > +
> > +   ring = &sc->txq[qid];
> > +   ssn = le16toh(ban->scd_ssn); /* BA window starting sequence number */
> > +   idx = IWM_AGG_SSN_TO_TXQ_IDX(ssn);
> > +
> > +   /*
> > +    * The first bit in ban->bitmap corresponds to the sequence number
> > +    * stored in the sequence control field ban->seq_ctl.
> > +    * Multiple BA notifications in a row may be using this number, with
> > +    * additional bits being set in cba->bitmap. It is unclear how the
> > +    * firmware decides to shift this window forward.
> > +    */
> > +   seq = le16toh(ban->seq_ctl) >> IEEE80211_SEQ_SEQ_SHIFT;
> > +
> > +   /*
> > +    * The firmware's new BA window starting sequence number
> > +    * corresponds to the first hole in ban->scd_ssn, implying
> > +    * that all frames between 'seq' and 'ssn' have been acked.
> > +    */
> > +   ssn = le16toh(ban->scd_ssn);
> > +
> > +   /* Skip rate control if our Tx rate is fixed. */
> > +   if (ic->ic_fixed_mcs != -1)
> > +           iwm_ampdu_rate_control(sc, ni, ring, ban->tid, seq, ssn);
> > +
> > +   /*
> > +    * SSN corresponds to the first (perhaps not yet transmitted) frame
> > +    * in firmware's BA window. Firmware is not going to retransmit any
> > +    * frames before its BA window so mark them all as done.
> > +    */
> > +   if (SEQ_LT(ba->ba_winstart, ssn)) {
> > +           ieee80211_output_ba_move_window(ic, ni, ban->tid, ssn);
> > +           iwm_txq_advance(sc, ring, IWM_AGG_SSN_TO_TXQ_IDX(ssn));
> > +           iwm_clear_oactive(sc, ring);
> > +   }
> > +}
> > +
> > +void
> >  iwm_rx_bmiss(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
> >      struct iwm_rx_data *data)
> >  {
> > @@ -4638,9 +5228,6 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd
> >             }
> >     }
> >  
> > -#if 0
> > -   iwm_update_sched(sc, ring->qid, ring->cur, 0, 0);
> > -#endif
> >     /* Kick command ring. */
> >     ring->queued++;
> >     ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT;
> > @@ -4771,7 +5358,6 @@ iwm_cmd_done(struct iwm_softc *sc, int qid, int idx, i
> >     }
> >  }
> >  
> > -#if 0
> >  /*
> >   * necessary only for block ack mode
> >   */
> > @@ -4780,32 +5366,49 @@ iwm_update_sched(struct iwm_softc *sc, int qid, int 
> > id
> >      uint16_t len)
> >  {
> >     struct iwm_agn_scd_bc_tbl *scd_bc_tbl;
> > -   uint16_t w_val;
> > +   uint16_t val;
> >  
> >     scd_bc_tbl = sc->sched_dma.vaddr;
> >  
> > -   len += 8; /* magic numbers came naturally from paris */
> > +   len += IWM_TX_CRC_SIZE + IWM_TX_DELIMITER_SIZE;
> >     if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE)
> >             len = roundup(len, 4) / 4;
> >  
> > -   w_val = htole16(sta_id << 12 | len);
> > +   val = htole16(sta_id << 12 | len);
> >  
> > +   bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map,
> > +       0, sc->sched_dma.size, BUS_DMASYNC_PREWRITE);
> > +
> >     /* Update TX scheduler. */
> > -   scd_bc_tbl[qid].tfd_offset[idx] = w_val;
> > +   scd_bc_tbl[qid].tfd_offset[idx] = val;
> > +   if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP)
> > +           scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = val;
> > +
> >     bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map,
> > -       (char *)(void *)w - (char *)(void *)sc->sched_dma.vaddr,
> > -       sizeof(uint16_t), BUS_DMASYNC_PREWRITE);
> > +       0, sc->sched_dma.size, BUS_DMASYNC_POSTWRITE);
> > +}
> >  
> > -   /* I really wonder what this is ?!? */
> > -   if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) {
> > -           scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = 
> > w_val;
> > -           bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map,
> > -               (char *)(void *)(w + IWM_TFD_QUEUE_SIZE_MAX) -
> > -               (char *)(void *)sc->sched_dma.vaddr,
> > -               sizeof (uint16_t), BUS_DMASYNC_PREWRITE);
> > -   }
> > +void
> > +iwm_reset_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id)
> > +{
> > +   struct iwm_agn_scd_bc_tbl *scd_bc_tbl;
> > +   uint16_t val;
> > +
> > +   scd_bc_tbl = sc->sched_dma.vaddr;
> > +
> > +   val = htole16(1 | (sta_id << 12));
> > +
> > +   bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map,
> > +       0, sc->sched_dma.size, BUS_DMASYNC_PREWRITE);
> > +
> > +   /* Update TX scheduler. */
> > +   scd_bc_tbl[qid].tfd_offset[idx] = val;
> > +   if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP)
> > +           scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = val;
> > +
> > +   bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map,
> > +       0, sc->sched_dma.size, BUS_DMASYNC_POSTWRITE);
> >  }
> > -#endif
> >  
> >  /*
> >   * Fill in various bit for management frames, and leave them
> > @@ -4897,19 +5500,24 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct 
> > ie
> >     uint32_t flags;
> >     u_int hdrlen;
> >     bus_dma_segment_t *seg;
> > -   uint8_t tid, type;
> > +   uint8_t tid, type, subtype;
> >     int i, totlen, err, pad;
> > -   int hdrlen2, rtsthres = ic->ic_rtsthreshold;
> > +   int qid, hasqos, rtsthres = ic->ic_rtsthreshold;
> >  
> >     wh = mtod(m, struct ieee80211_frame *);
> > -   hdrlen = ieee80211_get_hdrlen(wh);
> >     type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
> > +   subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
> > +   if (type == IEEE80211_FC0_TYPE_CTL)
> > +           hdrlen = sizeof(struct ieee80211_frame_min);
> > +   else
> > +           hdrlen = ieee80211_get_hdrlen(wh);
> >  
> > -   hdrlen2 = (ieee80211_has_qos(wh)) ?
> > -       sizeof (struct ieee80211_qosframe) :
> > -       sizeof (struct ieee80211_frame);
> > +   hasqos = ieee80211_has_qos(wh);
> >  
> > -   tid = 0;
> > +   if (type == IEEE80211_FC0_TYPE_DATA)
> > +           tid = IWM_TID_NON_QOS;
> > +   else
> > +           tid = IWM_MAX_TID_COUNT;
> >  
> >     /*
> >      * Map EDCA categories to Tx data queues.
> > @@ -4918,14 +5526,31 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct 
> > ie
> >      * need to share Tx queues between stations because we only implement
> >      * client mode; the firmware's station table contains only one entry
> >      * which represents our access point.
> > -    *
> > -    * Tx aggregation will require additional queues (one queue per TID
> > -    * for which aggregation is enabled) but we do not implement this yet.
> >      */
> >     if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
> > -           ring = &sc->txq[IWM_DQA_MIN_MGMT_QUEUE + ac];
> > +           qid = IWM_DQA_MIN_MGMT_QUEUE + ac;
> >     else
> > -           ring = &sc->txq[ac];
> > +           qid = ac;
> > +
> > +   /* If possible, put this frame on an aggregation queue. */
> > +   if (hasqos) {
> > +           struct ieee80211_tx_ba *ba;
> > +           uint16_t qos = ieee80211_get_qos(wh);
> > +           int qostid = qos & IEEE80211_QOS_TID;
> > +           int qosac = ieee80211_up_to_ac(ic, qostid);
> > +
> > +           ba = &ni->ni_tx_ba[qostid];
> > +           if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
> > +               type == IEEE80211_FC0_TYPE_DATA &&
> > +               (in->ampdu_tx_tid_mask & (1 << qostid)) &&
> > +               ba->ba_state == IEEE80211_BA_AGREED) {
> > +                   qid = IWM_FIRST_AGG_TX_QUEUE + qostid;
> > +                   tid = qostid;
> > +                   ac = qosac;
> > +           }
> > +   }
> > +
> > +   ring = &sc->txq[qid];
> >     desc = &ring->desc[ring->cur];
> >     memset(desc, 0, sizeof(*desc));
> >     data = &ring->data[ring->cur];
> > @@ -5004,14 +5629,28 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct 
> > ie
> >     tx->sta_id = IWM_STATION_ID;
> >  
> >     if (type == IEEE80211_FC0_TYPE_MGT) {
> > -           uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
> > -
> >             if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
> >                 subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
> >                     tx->pm_frame_timeout = htole16(3);
> >             else
> >                     tx->pm_frame_timeout = htole16(2);
> >     } else {
> > +           if (type == IEEE80211_FC0_TYPE_CTL &&
> > +               subtype == IEEE80211_FC0_SUBTYPE_BAR) {
> > +                   struct ieee80211_frame_min *wbar;
> > +                   uint8_t *frm;
> > +                   uint16_t ctl;
> > +
> > +                   flags |= IWM_TX_CMD_FLG_ACK | IWM_TX_CMD_FLG_BAR;
> > +                   tx->data_retry_limit = IWM_BAR_DFAULT_RETRY_LIMIT;
> > +
> > +                   wbar = mtod(m, struct ieee80211_frame_min *);
> > +                   frm = (uint8_t *)&wbar[1];
> > +                   memcpy(&ctl, frm, sizeof(ctl));
> > +                   tid = (le16toh(ctl) & IEEE80211_BA_TID_INFO_MASK) >>
> > +                       IEEE80211_BA_TID_INFO_SHIFT;
> > +           }
> > +
> >             tx->pm_frame_timeout = htole16(0);
> >     }
> >  
> > @@ -5058,7 +5697,9 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie
> >             tx->sec_ctl = 0;
> >     }
> >  
> > -   flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL;
> > +   flags |= IWM_TX_CMD_FLG_BT_DIS;
> > +   if (!hasqos)
> > +           flags |= IWM_TX_CMD_FLG_SEQ_CTL;
> >  
> >     tx->tx_flags |= htole32(flags);
> >  
> > @@ -5085,9 +5726,11 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct 
> > ie
> >             }
> >     }
> >     data->m = m;
> > +   data->totlen = totlen;
> >     data->in = in;
> >     data->txmcs = ni->ni_txmcs;
> >     data->txrate = ni->ni_txrate;
> > +   data->ampdu_txmcs = ni->ni_txmcs; /* updated upon Tx interrupt */
> >  
> >     /* Fill TX descriptor. */
> >     desc->num_tbs = 2 + data->map->dm_nsegs;
> > @@ -5118,9 +5761,7 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie
> >         (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr,
> >         sizeof (*desc), BUS_DMASYNC_PREWRITE);
> >  
> > -#if 0
> >     iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, 
> > le16toh(tx->len));
> > -#endif
> >  
> >     /* Kick TX ring. */
> >     ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT;
> > @@ -5336,6 +5977,7 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node 
> >     uint32_t status;
> >     size_t cmdsize;
> >     struct ieee80211com *ic = &sc->sc_ic;
> > +   uint16_t tid_disable_tx = 0xffff;
> >  
> >     if (!update && (sc->sc_flags & IWM_FLAG_STA_ACTIVE))
> >             panic("STA already added");
> > @@ -5362,7 +6004,7 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node 
> >             else
> >                     qid = IWM_AUX_QUEUE;
> >             add_sta_cmd.tfd_queue_msk |= htole32(1 << qid);
> > -   } else if (!update) {
> > +   } else {
> >             int ac;
> >             for (ac = 0; ac < EDCA_NUM_AC; ac++) {
> >                     int qid = ac;
> > @@ -5371,15 +6013,33 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct 
> > iwm_node 
> >                             qid += IWM_DQA_MIN_MGMT_QUEUE;
> >                     add_sta_cmd.tfd_queue_msk |= htole32(1 << qid);
> >             }
> > -           IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
> >     }
> > +   if (!update) {
> > +           if (ic->ic_opmode == IEEE80211_M_MONITOR)
> > +                   IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
> > +                       etherbroadcastaddr);
> > +           else
> > +                   IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
> > +                       in->in_ni.ni_bssid);
> > +   }
> >     add_sta_cmd.add_modify = update ? 1 : 0;
> >     add_sta_cmd.station_flags_msk
> >         |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK);
> > -   add_sta_cmd.tid_disable_tx = htole16(0xffff);
> > -   if (update)
> > -           add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_TID_DISABLE_TX);
> > +   if (update) {
> > +           int tid, qid;
> > +           for (tid = 0; tid < IWM_MAX_TID_COUNT; tid++) {
> > +                   if ((in->ampdu_tx_tid_mask & (1 << tid)) == 0)
> > +                           continue;
> >  
> > +                   qid = IWM_FIRST_AGG_TX_QUEUE + tid;
> > +                   add_sta_cmd.tfd_queue_msk |= htole32(1 << qid);
> > +                   tid_disable_tx &= ~(1 << tid);
> > +                   add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_QUEUES |
> > +                       IWM_STA_MODIFY_TID_DISABLE_TX);
> > +           }
> > +   }
> > +   add_sta_cmd.tid_disable_tx = htole16(tid_disable_tx);
> > +
> >     if (in->in_ni.ni_flags & IEEE80211_NODE_HT) {
> >             add_sta_cmd.station_flags_msk
> >                 |= htole32(IWM_STA_FLG_MAX_AGG_SIZE_MSK |
> > @@ -5444,7 +6104,7 @@ iwm_add_aux_sta(struct iwm_softc *sc)
> >     if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT)) {
> >             qid = IWM_DQA_AUX_QUEUE;
> >             err = iwm_enable_txq(sc, IWM_AUX_STA_ID, qid,
> > -               IWM_TX_FIFO_MCAST);
> > +               IWM_TX_FIFO_MCAST, 0, IWM_MAX_TID_COUNT, 0);
> >     } else {
> >             qid = IWM_AUX_QUEUE;
> >             err = iwm_enable_ac_txq(sc, qid, IWM_TX_FIFO_MCAST);
> > @@ -6582,6 +7242,9 @@ iwm_auth(struct iwm_softc *sc)
> >  
> >     splassert(IPL_NET);
> >  
> > +   in->ampdu_rx_tid_mask = 0;
> > +   in->ampdu_tx_tid_mask = 0;
> > +
> >     if (ic->ic_opmode == IEEE80211_M_MONITOR)
> >             sc->sc_phyctxt[0].channel = ic->ic_ibss_chan;
> >     else
> > @@ -7156,11 +7819,7 @@ iwm_setrates(struct iwm_node *in, int async)
> >  
> >     lqcmd.agg_time_limit = htole16(4000);   /* 4ms */
> >     lqcmd.agg_disable_start_th = 3;
> > -#ifdef notyet
> >     lqcmd.agg_frame_cnt_limit = 0x3f;
> > -#else
> > -   lqcmd.agg_frame_cnt_limit = 1; /* tx agg disabled */
> > -#endif
> >  
> >     cmd.data[0] = &lqcmd;
> >     iwm_send_cmd(sc, &cmd);
> > @@ -7892,7 +8551,7 @@ iwm_init_hw(struct iwm_softc *sc)
> >             else
> >                     qid = IWM_AUX_QUEUE;
> >             err = iwm_enable_txq(sc, IWM_MONITOR_STA_ID, qid,
> > -               iwm_ac_to_tx_fifo[EDCA_AC_BE]);
> > +               iwm_ac_to_tx_fifo[EDCA_AC_BE], 0, IWM_MAX_TID_COUNT, 0);
> >             if (err) {
> >                     printf("%s: could not enable monitor inject Tx queue "
> >                         "(error %d)\n", DEVNAME(sc), err);
> > @@ -7906,7 +8565,7 @@ iwm_init_hw(struct iwm_softc *sc)
> >                     else
> >                             qid = ac;
> >                     err = iwm_enable_txq(sc, IWM_STATION_ID, qid,
> > -                       iwm_ac_to_tx_fifo[ac]);
> > +                       iwm_ac_to_tx_fifo[ac], 0, IWM_TID_NON_QOS, 0);
> >                     if (err) {
> >                             printf("%s: could not enable Tx queue %d "
> >                                 "(error %d)\n", DEVNAME(sc), ac, err);
> > @@ -8578,6 +9237,10 @@ iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data 
> > *d
> >                     iwm_rx_tx_cmd(sc, pkt, data);
> >                     break;
> >  
> > +           case IWM_BA_NOTIF:
> > +                   iwm_rx_ba(sc, pkt, data);
> > +                   break;
> > +
> >             case IWM_MISSED_BEACONS_NOTIFICATION:
> >                     iwm_rx_bmiss(sc, pkt, data);
> >                     break;
> > @@ -8943,9 +9606,9 @@ iwm_intr(void *arg)
> >             DPRINTF(("driver status:\n"));
> >             for (i = 0; i < IWM_MAX_QUEUES; i++) {
> >                     struct iwm_tx_ring *ring = &sc->txq[i];
> > -                   DPRINTF(("  tx ring %2d: qid=%-2d cur=%-3d "
> > +                   DPRINTF(("  tx ring %2d: qid=%-2d tail=%-3d cur=%-3d "
> >                         "queued=%-3d\n",
> > -                       i, ring->qid, ring->cur, ring->queued));
> > +                       i, ring->qid, ring->tail, ring->cur, ring->queued));
> >             }
> >             DPRINTF(("  rx ring: cur=%d\n", sc->rxq.cur));
> >             DPRINTF(("  802.11 state %s\n",
> > @@ -9053,9 +9716,9 @@ iwm_intr_msix(void *arg)
> >             DPRINTF(("driver status:\n"));
> >             for (i = 0; i < IWM_MAX_QUEUES; i++) {
> >                     struct iwm_tx_ring *ring = &sc->txq[i];
> > -                   DPRINTF(("  tx ring %2d: qid=%-2d cur=%-3d "
> > +                   DPRINTF(("  tx ring %2d: qid=%-2d tail=%-3d cur=%-3d "
> >                         "queued=%-3d\n",
> > -                       i, ring->qid, ring->cur, ring->queued));
> > +                       i, ring->qid, ring->tail, ring->cur, ring->queued));
> >             }
> >             DPRINTF(("  rx ring: cur=%d\n", sc->rxq.cur));
> >             DPRINTF(("  802.11 state %s\n",
> > @@ -9465,6 +10128,7 @@ iwm_attach(struct device *parent, struct device 
> > *self,
> >  
> >     /* Set device capabilities. */
> >     ic->ic_caps =
> > +       IEEE80211_C_QOS | IEEE80211_C_TX_AMPDU | /* A-MPDU */
> >         IEEE80211_C_WEP |           /* WEP */
> >         IEEE80211_C_RSN |           /* WPA/RSN */
> >         IEEE80211_C_SCANALL |       /* device scans all channels at once */
> > @@ -9529,10 +10193,8 @@ iwm_attach(struct device *parent, struct device 
> > *self,
> >     ic->ic_update_htprot = iwm_update_htprot;
> >     ic->ic_ampdu_rx_start = iwm_ampdu_rx_start;
> >     ic->ic_ampdu_rx_stop = iwm_ampdu_rx_stop;
> > -#ifdef notyet
> >     ic->ic_ampdu_tx_start = iwm_ampdu_tx_start;
> >     ic->ic_ampdu_tx_stop = iwm_ampdu_tx_stop;
> > -#endif
> >     /*
> >      * We cannot read the MAC address without loading the
> >      * firmware from disk. Postpone until mountroot is done.
> > blob - 201ce69014b9422335a6d698cd4a3cc3f314b2b5
> > blob + 1e2e4e01e2a98f60221b72fc6e82a1246f7b9cef
> > --- sys/dev/pci/if_iwmreg.h
> > +++ sys/dev/pci/if_iwmreg.h
> > @@ -1837,6 +1837,9 @@ struct iwm_agn_scd_bc_tbl {
> >     uint16_t tfd_offset[IWM_TFD_QUEUE_BC_SIZE];
> >  } __packed;
> >  
> > +#define IWM_TX_CRC_SIZE 4
> > +#define IWM_TX_DELIMITER_SIZE 4
> > +
> >  /* Maximum number of Tx queues. */
> >  #define IWM_MAX_QUEUES     31
> >  
> > @@ -1875,6 +1878,11 @@ struct iwm_agn_scd_bc_tbl {
> >  #define IWM_DQA_MIN_DATA_QUEUE             10
> >  #define IWM_DQA_MAX_DATA_QUEUE             31
> >  
> > +/* Reserve 8 DQA Tx queues, from 10 up to 17, for A-MPDU aggregation. */
> > +#define IWM_MAX_TID_COUNT  8
> > +#define IWM_FIRST_AGG_TX_QUEUE     IWM_DQA_MIN_DATA_QUEUE
> > +#define IWM_LAST_AGG_TX_QUEUE      (IWM_FIRST_AGG_TX_QUEUE + 
> > IWM_MAX_TID_COUNT - 1)
> > +
> >  /* legacy non-DQA queues; the legacy command queue uses a different 
> > number! */
> >  #define IWM_OFFCHANNEL_QUEUE       8
> >  #define IWM_CMD_QUEUE              9
> > @@ -4627,7 +4635,8 @@ struct iwm_lq_cmd {
> >  /*
> >   * TID for non QoS frames - to be written in tid_tspec
> >   */
> > -#define IWM_TID_NON_QOS    IWM_MAX_TID_COUNT
> > +#define IWM_TID_NON_QOS    0
> > +#define IWM_TID_MGMT       15
> >  
> >  /*
> >   * Limits on the retransmissions - to be written in {data,rts}_retry_limit
> > @@ -4898,21 +4907,23 @@ struct iwm_tx_resp {
> >  /**
> >   * struct iwm_ba_notif - notifies about reception of BA
> >   * ( IWM_BA_NOTIF = 0xc5 )
> > - * @sta_addr_lo32: lower 32 bits of the MAC address
> > - * @sta_addr_hi16: upper 16 bits of the MAC address
> > + * @sta_addr: MAC address
> >   * @sta_id: Index of recipient (BA-sending) station in fw's station table
> >   * @tid: tid of the session
> > - * @seq_ctl: sequence control field from IEEE80211 frame header (it is 
> > unclear
> > - *  which frame this relates to; info or reverse engineering welcome)
> > + * @seq_ctl: sequence control field from IEEE80211 frame header (the first
> > + * bit in @bitmap corresponds to the sequence number stored here)
> >   * @bitmap: the bitmap of the BA notification as seen in the air
> >   * @scd_flow: the tx queue this BA relates to
> >   * @scd_ssn: the index of the last contiguously sent packet
> >   * @txed: number of Txed frames in this batch
> >   * @txed_2_done: number of Acked frames in this batch
> > + * @reduced_txp: power reduced according to TPC. This is the actual value 
> > and
> > + * not a copy from the LQ command. Thus, if not the first rate was used
> > + * for Tx-ing then this value will be set to 0 by FW.
> > + * @reserved1: reserved
> >   */
> >  struct iwm_ba_notif {
> > -   uint32_t sta_addr_lo32;
> > -   uint16_t sta_addr_hi16;
> > +   uint8_t sta_addr[ETHER_ADDR_LEN];
> >     uint16_t reserved;
> >  
> >     uint8_t sta_id;
> > @@ -4923,6 +4934,7 @@ struct iwm_ba_notif {
> >     uint16_t scd_ssn;
> >     uint8_t txed;
> >     uint8_t txed_2_done;
> > +   uint8_t reduced_txp;
> >     uint16_t reserved1;
> >  } __packed;
> >  
> > blob - 89abe2c1dbdf5ac3ccbf710994380502530ef2a8
> > blob + 7d9e26bffe0f1658c771bf85768797c23e94e147
> > --- sys/dev/pci/if_iwmvar.h
> > +++ sys/dev/pci/if_iwmvar.h
> > @@ -252,14 +252,26 @@ struct iwm_fw_paging {
> >  #define IWM_TX_RING_LOMARK 192
> >  #define IWM_TX_RING_HIMARK 224
> >  
> > +/* For aggregation queues, index must be aligned to frame sequence number. 
> > */
> > +#define IWM_AGG_SSN_TO_TXQ_IDX(x)  ((x) & (IWM_TX_RING_COUNT - 1))
> > +
> >  struct iwm_tx_data {
> >     bus_dmamap_t    map;
> >     bus_addr_t      cmd_paddr;
> >     bus_addr_t      scratch_paddr;
> >     struct mbuf     *m;
> >     struct iwm_node *in;
> > +   int totlen;
> > +   int retries;
> > +   int txfail;
> >     int txmcs;
> >     int txrate;
> > +
> > +   /* A-MPDU subframes */
> > +   int ampdu_id;
> > +   int ampdu_txmcs;
> > +   int ampdu_nframes;
> > +   int ampdu_size;
> >  };
> >  
> >  struct iwm_tx_ring {
> > @@ -363,6 +375,12 @@ struct iwm_bf_data {
> >     int last_cqm_event;
> >  };
> >  
> > +struct iwm_ba_param {
> > +   uint16_t                tid_mask;
> > +   uint16_t                ssn[IWM_MAX_TID_COUNT];
> > +   uint16_t                winsize[IWM_MAX_TID_COUNT];
> > +};
> > +
> >  struct iwm_softc {
> >     struct device sc_dev;
> >     struct ieee80211com sc_ic;
> > @@ -381,10 +399,15 @@ struct iwm_softc {
> >  
> >     /* Task for firmware BlockAck setup/teardown and its arguments. */
> >     struct task             ba_task;
> > -   int                     ba_start;
> > -   int                     ba_tid;
> > -   uint16_t                ba_ssn;
> > -   uint16_t                ba_winsize;
> > +   int                     ba_flags;
> > +#define IWM_RX_BA_START    0x01
> > +#define IWM_TX_BA_START    0x02
> > +#define IWM_RX_BA_STOP     0x04
> > +#define IWM_TX_BA_STOP     0x08
> > +   struct iwm_ba_param     rx_ba_start;
> > +   struct iwm_ba_param     rx_ba_stop;
> > +   struct iwm_ba_param     tx_ba_start;
> > +   struct iwm_ba_param     tx_ba_stop;
> >  
> >     /* Task for HT protection updates. */
> >     struct task             htprot_task;
> > @@ -407,6 +430,7 @@ struct iwm_softc {
> >     struct iwm_rx_ring rxq;
> >     int qfullmsk;
> >     int cmdqid;
> > +   int qenablemsk;
> >  
> >     int sc_sf_state;
> >  
> > @@ -551,6 +575,12 @@ struct iwm_node {
> >     int chosen_txrate;
> >     struct ieee80211_mira_node in_mn;
> >     int chosen_txmcs;
> > +
> > +   uint32_t next_ampdu_id;
> > +
> > +   /* Currently active Rx/Tx block ack sessions; tracked per TID. */
> > +   uint8_t ampdu_rx_tid_mask;
> > +   uint8_t ampdu_tx_tid_mask;
> >  };
> >  #define IWM_STATION_ID 0
> >  #define IWM_AUX_STA_ID 1
> > 
> 
> -- 
> 

-- 

Reply via email to