I would like to try this again: In iwm(4), process more than one frame
per Rx interrupt, and enable monitor mode.

Monitor mode triggers "unhandled fimware response" errors without multi-Rx
support. We have seen these infamous errors in other contexts as well.
It is possible that the firmware decides to use multi-Rx in such cases
which would of course confuse our driver.

The fact is that Linux has not used single-Rx mode for years. One of their
developers told me that Intel has stopped testing it entirely. The current
code isn't future-proof and is likely to break with newer versions of
firmware and/or hardware.

These changes were attempted and reverted twice already.
Last time because they were causing throughput issues (and I now see why).
I have rebased my old diff and tweaked it slightly to avoid an unneeded
mbuf copy and reworked all the offset/length calculations once again.

Works fine here against several APs with no observable drop in throughput.

Please test this on as many iwm devices as possible. Thanks!


diff refs/heads/master refs/heads/iwm-multirx
blob - 42cb00c62cc455d871da09560dcf82876db2f4c0
blob + c9a4dc36c3a7cf09c9836141b6a966370ac94824
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -367,6 +367,7 @@ int iwm_get_signal_strength(struct iwm_softc *, struct
 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 *);
+int    iwm_rx_frame(struct iwm_softc *, struct mbuf *, uint32_t);
 int    iwm_ccmp_decap(struct iwm_softc *, struct mbuf *,
            struct ieee80211_node *);
 void   iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
@@ -431,7 +432,7 @@ uint8_t     iwm_ridx2rate(struct ieee80211_rateset *, int)
 int    iwm_rval2ridx(int);
 void   iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *);
 void   iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *,
-           struct iwm_mac_ctx_cmd *, uint32_t, int);
+           struct iwm_mac_ctx_cmd *, uint32_t);
 void   iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *,
            struct iwm_mac_data_sta *, int);
 int    iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, uint32_t, int);
@@ -476,6 +477,9 @@ const char *iwm_desc_lookup(uint32_t);
 void   iwm_nic_error(struct iwm_softc *);
 void   iwm_nic_umac_error(struct iwm_softc *);
 #endif
+void   iwm_rx_mpdu(struct iwm_softc *, struct mbuf *, size_t);
+int    iwm_rx_pkt_valid(struct iwm_rx_packet *);
+void   iwm_rx_pkt(struct iwm_softc *, struct iwm_rx_data *);
 void   iwm_notif_intr(struct iwm_softc *);
 int    iwm_intr(void *);
 int    iwm_match(struct device *, void *, void *);
@@ -1729,7 +1733,6 @@ iwm_nic_rx_init(struct iwm_softc *sc)
            IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL            |
            IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY               |  /* HW bug */
            IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL   |
-           IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK        |
            (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
            IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K            |
            IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
@@ -3514,57 +3517,23 @@ iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, s
        return 0;
 }
 
-void
-iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
-    struct iwm_rx_data *data)
+int
+iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, uint32_t rx_pkt_status)
 {
        struct ieee80211com *ic = &sc->sc_ic;
-       struct ifnet *ifp = IC2IFP(ic);
        struct ieee80211_frame *wh;
        struct ieee80211_node *ni;
        struct ieee80211_rxinfo rxi;
        struct ieee80211_channel *bss_chan;
-       struct mbuf *m;
        struct iwm_rx_phy_info *phy_info;
-       struct iwm_rx_mpdu_res_start *rx_res;
        int device_timestamp;
-       uint32_t len;
-       uint32_t rx_pkt_status;
        int rssi, chanidx;
        uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
 
-       bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
-           BUS_DMASYNC_POSTREAD);
-
        phy_info = &sc->sc_last_phy_info;
-       rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
-       wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
-       len = le16toh(rx_res->byte_count);
-       if (len < IEEE80211_MIN_LEN) {
-               ic->ic_stats.is_rx_tooshort++;
-               IC2IFP(ic)->if_ierrors++;
-               return;
-       }
-       if (len > IWM_RBUF_SIZE - sizeof(*rx_res)) {
-               IC2IFP(ic)->if_ierrors++;
-               return;
-       }
-       rx_pkt_status = le32toh(*(uint32_t *)(pkt->data +
-           sizeof(*rx_res) + len));
-
        if (__predict_false(phy_info->cfg_phy_cnt > 20))
-               return;
+               return EINVAL;
 
-       if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
-           !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK))
-               return; /* drop */
-
-       m = data->m;
-       if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0)
-               return;
-       m->m_data = pkt->data + sizeof(*rx_res);
-       m->m_pkthdr.len = m->m_len = len;
-
        device_timestamp = le32toh(phy_info->system_timestamp);
 
        if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
@@ -3579,6 +3548,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
        if (chanidx < 0 || chanidx >= nitems(ic->ic_channels))  
                chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
 
+       wh = mtod(m, struct ieee80211_frame *);
        ni = ieee80211_find_rxnode(ic, wh);
        if (ni == ic->ic_bss) {
                /* 
@@ -3603,7 +3573,6 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
                if ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK) !=
                    IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC) {
                        ic->ic_stats.is_ccmp_dec_errs++;
-                       ifp->if_ierrors++;
                        m_freem(m);
                        goto done;
                }
@@ -3614,12 +3583,10 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
                    (IWM_RX_MPDU_RES_STATUS_DEC_DONE |
                    IWM_RX_MPDU_RES_STATUS_MIC_OK)) {
                        ic->ic_stats.is_ccmp_dec_errs++;
-                       ifp->if_ierrors++;
                        m_freem(m);
                        goto done;
                }
                if (iwm_ccmp_decap(sc, m, ni) != 0) {
-                       ifp->if_ierrors++;
                        m_freem(m);
                        goto done;
                }
@@ -3691,6 +3658,8 @@ done:
        if (ni == ic->ic_bss && IEEE80211_ADDR_EQ(saved_bssid, ni->ni_macaddr))
                ni->ni_chan = bss_chan;
        ieee80211_release_node(ic, ni);
+
+       return 0;
 }
 
 void
@@ -4666,6 +4635,7 @@ void
 iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in,
     struct iwm_mac_power_cmd *cmd)
 {
+       struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211_node *ni = &in->in_ni;
        int dtim_period, dtim_msec, keep_alive;
 
@@ -4687,7 +4657,8 @@ iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_n
        keep_alive = roundup(keep_alive, 1000) / 1000;
        cmd->keep_alive_seconds = htole16(keep_alive);
 
-       cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+       if (ic->ic_opmode != IEEE80211_M_MONITOR)
+               cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
 }
 
 int
@@ -4714,13 +4685,15 @@ iwm_power_mac_update_mode(struct iwm_softc *sc, struct
 int
 iwm_power_update_device(struct iwm_softc *sc)
 {
-       struct iwm_device_power_cmd cmd = {
-               .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
-       };
+       struct iwm_device_power_cmd cmd = { };
+       struct ieee80211com *ic = &sc->sc_ic;
 
        if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
                return 0;
 
+       if (ic->ic_opmode != IEEE80211_M_MONITOR)
+               cmd.flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
        return iwm_send_cmd_pdu(sc,
            IWM_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd);
 }
@@ -4782,7 +4755,12 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node 
                        add_sta_cmd.tfd_queue_msk |=
                            htole32(1 << iwm_ac_to_tx_fifo[ac]);
                }
-               IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
+               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
@@ -5452,7 +5430,7 @@ iwm_ack_rates(struct iwm_softc *sc, struct iwm_node *i
 
 void
 iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in,
-    struct iwm_mac_ctx_cmd *cmd, uint32_t action, int assoc)
+    struct iwm_mac_ctx_cmd *cmd, uint32_t action)
 {
 #define IWM_EXP2(x)    ((1 << (x)) - 1)        /* CWmin = 2^ECWmin - 1 */
        struct ieee80211com *ic = &sc->sc_ic;
@@ -5464,12 +5442,21 @@ iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct i
            in->in_color));
        cmd->action = htole32(action);
 
-       cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
+       if (ic->ic_opmode == IEEE80211_M_MONITOR)
+               cmd->mac_type = htole32(IWM_FW_MAC_TYPE_LISTENER);
+       else if (ic->ic_opmode == IEEE80211_M_STA)
+               cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
+       else
+               panic("unsupported operating mode %d\n", ic->ic_opmode);
        cmd->tsf_id = htole32(IWM_TSF_ID_A);
 
        IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_myaddr);
-       IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
+       if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+               IEEE80211_ADDR_COPY(cmd->bssid_addr, etherbroadcastaddr);
+               return;
+       }
 
+       IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
        iwm_ack_rates(sc, in, &cck_ack_rates, &ofdm_ack_rates);
        cmd->cck_rates = htole32(cck_ack_rates);
        cmd->ofdm_rates = htole32(ofdm_ack_rates);
@@ -5560,6 +5547,7 @@ int
 iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action,
     int assoc)
 {
+       struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211_node *ni = &in->in_ni;
        struct iwm_mac_ctx_cmd cmd;
        int active = (sc->sc_flags & IWM_FLAG_MAC_ACTIVE);
@@ -5571,11 +5559,19 @@ iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node
 
        memset(&cmd, 0, sizeof(cmd));
 
-       iwm_mac_ctxt_cmd_common(sc, in, &cmd, action, assoc);
+       iwm_mac_ctxt_cmd_common(sc, in, &cmd, action);
 
-       /* Allow beacons to pass through as long as we are not associated or we
-        * do not have dtim period information */
-       if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
+       if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+               cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_PROMISC |
+                   IWM_MAC_FILTER_IN_CONTROL_AND_MGMT |
+                   IWM_MAC_FILTER_IN_BEACON |
+                   IWM_MAC_FILTER_IN_PROBE_REQUEST |
+                   IWM_MAC_FILTER_IN_CRC32);
+       } else if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
+               /* 
+                * Allow beacons to pass through as long as we are not
+                * associated or we do not have dtim period information.
+                */
                cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_BEACON);
        else
                iwm_mac_ctxt_cmd_fill_sta(sc, in, &cmd.sta, assoc);
@@ -5792,7 +5788,10 @@ iwm_auth(struct iwm_softc *sc)
 
        splassert(IPL_NET);
 
-       sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
+       if (ic->ic_opmode == IEEE80211_M_MONITOR)
+               sc->sc_phyctxt[0].channel = ic->ic_ibss_chan;
+       else
+               sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
        err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1,
            IWM_FW_CTXT_ACTION_MODIFY, 0);
        if (err) {
@@ -5826,6 +5825,9 @@ iwm_auth(struct iwm_softc *sc)
        }
        sc->sc_flags |= IWM_FLAG_STA_ACTIVE;
 
+       if (ic->ic_opmode == IEEE80211_M_MONITOR)
+               return 0;
+
        /*
         * Prevent the FW from wandering off channel during association
         * by "protecting" the session with a time event.
@@ -5956,8 +5958,16 @@ iwm_run(struct iwm_softc *sc)
 
        splassert(IPL_NET);
 
+       if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+               /* Add a MAC context and a sniffing STA. */
+               err = iwm_auth(sc);
+               if (err)
+                       return err;
+       }
+
        /* Configure Rx chains for MIMO. */
-       if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) &&
+       if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
+           (in->in_ni.ni_flags & IEEE80211_NODE_HT)) &&
            !sc->sc_nvm.sku_cap_mimo_disable) {
                err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0],
                    2, 2, IWM_FW_CTXT_ACTION_MODIFY, 0);
@@ -6025,6 +6035,11 @@ iwm_run(struct iwm_softc *sc)
        ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn);
        ieee80211_mira_node_init(&in->in_mn);
 
+       if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+               iwm_led_blink_start(sc);
+               return 0;
+       }
+
        /* Start at lowest available bit-rate, AMRR will raise. */
        in->in_ni.ni_txrate = 0;
        in->in_ni.ni_txmcs = 0;
@@ -6044,6 +6059,9 @@ iwm_run_stop(struct iwm_softc *sc)
 
        splassert(IPL_NET);
 
+       if (ic->ic_opmode == IEEE80211_M_MONITOR)
+               iwm_led_blink_stop(sc);
+
        err = iwm_sf_config(sc, IWM_SF_INIT_OFF);
        if (err)
                return err;
@@ -6715,6 +6733,12 @@ iwm_init(struct ifnet *ifp)
        ifq_clr_oactive(&ifp->if_snd);
        ifp->if_flags |= IFF_RUNNING;
 
+       if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+               ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+               ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+               return 0;
+       }
+
        ieee80211_begin_scan(ifp);
 
        /* 
@@ -7194,37 +7218,104 @@ do {                                                   
                \
 #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
 
 void
-iwm_notif_intr(struct iwm_softc *sc)
+iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, size_t maxlen)
 {
-       uint16_t hw;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = IC2IFP(ic);
+       struct iwm_rx_packet *pkt;
+       struct iwm_rx_mpdu_res_start *rx_res;
+       uint16_t len;
+       uint32_t rx_pkt_status;
+       int rxfail;
 
-       bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
-           0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
+       pkt = mtod(m, struct iwm_rx_packet *);
+       rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
+       len = le16toh(rx_res->byte_count);
+       if (len < IEEE80211_MIN_LEN) {
+               ic->ic_stats.is_rx_tooshort++;
+               IC2IFP(ic)->if_ierrors++;
+               m_freem(m);
+               return;
+       }
+       if (len + sizeof(*rx_res) + sizeof(rx_pkt_status) > maxlen ||
+           len > IEEE80211_MAX_LEN) {
+               IC2IFP(ic)->if_ierrors++;
+               m_freem(m);
+               return;
+       }
 
-       hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
-       hw &= (IWM_RX_RING_COUNT - 1);
-       while (sc->rxq.cur != hw) {
-               struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
-               struct iwm_rx_packet *pkt;
-               int qid, idx, code, handled = 1;
+       memcpy(&rx_pkt_status, pkt->data + sizeof(*rx_res) + len,
+           sizeof(rx_pkt_status));
+       rx_pkt_status = le32toh(rx_pkt_status);
+       rxfail = ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) == 0 ||
+           (rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK) == 0);
+       if (rxfail) {
+               ifp->if_ierrors++;
+               m_freem(m);
+               return;
+       }
 
-               bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt),
-                   BUS_DMASYNC_POSTREAD);
-               pkt = mtod(data->m, struct iwm_rx_packet *);
+       /* Extract the 802.11 frame. */
+       m->m_data = (caddr_t)pkt->data + sizeof(*rx_res);
+       m->m_pkthdr.len = m->m_len = len;
+       if (iwm_rx_frame(sc, m, rx_pkt_status) != 0) {
+               ifp->if_ierrors++;
+               m_freem(m);
+       }
+}
 
+int
+iwm_rx_pkt_valid(struct iwm_rx_packet *pkt)
+{
+       int qid, idx, code;
+
+       code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
+       qid = pkt->hdr.qid & ~0x80;
+       idx = pkt->hdr.idx;
+
+       if ((code == 0 && qid == 0 && idx == 0) ||
+           pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID))
+               return 0;
+
+       return 1;
+}
+
+void
+iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data)
+{
+       struct ifnet *ifp = IC2IFP(&sc->sc_ic);
+       struct iwm_rx_packet *pkt, *nextpkt;
+       uint32_t offset = 0, nextoff = 0, nmpdu = 0, len;
+       struct mbuf *m0, *m;
+       const size_t minsz = sizeof(pkt->len_n_flags) + sizeof(pkt->hdr);
+       size_t remain = IWM_RBUF_SIZE;
+       int qid, idx, code, handled = 1;
+
+       bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
+           BUS_DMASYNC_POSTREAD);
+
+       m0 = data->m;
+       while (m0 && offset + minsz < IWM_RBUF_SIZE) {
+               pkt = (struct iwm_rx_packet *)(m0->m_data + offset);
+
+               if (!iwm_rx_pkt_valid(pkt))
+                       break;
+
+               code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
                qid = pkt->hdr.qid & ~0x80;
                idx = pkt->hdr.idx;
+               len = sizeof(pkt->len_n_flags) + iwm_rx_packet_len(pkt);
+               if (len < sizeof(pkt->hdr) ||
+                   len > (IWM_RBUF_SIZE - offset - minsz))
+                       break;
 
-               code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
-
-               /*
-                * randomly get these from the firmware, no idea why.
-                * they at least seem harmless, so just ignore them for now
-                */
-               if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
-                   || pkt->len_n_flags == htole32(0x55550000))) {
-                       ADVANCE_RXQ(sc);
-                       continue;
+               if (code == IWM_REPLY_RX_MPDU_CMD && ++nmpdu == 1) {
+                       /* Take mbuf m0 off the RX ring. */
+                       if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur)) {
+                               ifp->if_ierrors++;
+                               break;
+                       }
+                       KASSERT(data->m != m0);
                }
 
                switch (code) {
@@ -7232,10 +7323,40 @@ iwm_notif_intr(struct iwm_softc *sc)
                        iwm_rx_rx_phy_cmd(sc, pkt, data);
                        break;
 
-               case IWM_REPLY_RX_MPDU_CMD:
-                       iwm_rx_rx_mpdu(sc, pkt, data);
-                       break;
+               case IWM_REPLY_RX_MPDU_CMD: {
+                       nextoff = offset +
+                           roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
+                       nextpkt = (struct iwm_rx_packet *)
+                           (m0->m_data + nextoff);
+                       if (nextoff + minsz >= IWM_RBUF_SIZE ||
+                           !iwm_rx_pkt_valid(nextpkt)) {
+                               /* No need to copy last frame in buffer. */
+                               if (offset > 0)
+                                       m_adj(m0, offset);
+                               iwm_rx_mpdu(sc, m0, remain - minsz);
+                               m0 = NULL; /* stack owns m0 now; abort loop */
+                       } else {
+                               /*
+                                * Create an mbuf which points to the current
+                                * packet. Always copy from offset zero to
+                                * preserve m_pkthdr.
+                                */
+                               m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
+                               if (m == NULL) {
+                                       ifp->if_ierrors++;
+                                       break;
+                               }
+                               m_adj(m, offset);
+                               iwm_rx_mpdu(sc, m, remain - minsz);
+                       }
 
+                       if (offset + minsz < remain)
+                               remain -= offset;
+                       else
+                               remain = minsz;
+                       break;
+               }
+
                case IWM_TX_CMD:
                        iwm_rx_tx_cmd(sc, pkt, data);
                        break;
@@ -7479,6 +7600,26 @@ iwm_notif_intr(struct iwm_softc *sc)
                        iwm_cmd_done(sc, pkt);
                }
 
+               offset += roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
+       }
+
+       if (m0 && m0 != data->m)
+               m_freem(m0);
+}
+
+void
+iwm_notif_intr(struct iwm_softc *sc)
+{
+       uint16_t hw;
+
+       bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
+           0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
+
+       hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
+       hw &= (IWM_RX_RING_COUNT - 1);
+       while (sc->rxq.cur != hw) {
+               struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
+               iwm_rx_pkt(sc, data);
                ADVANCE_RXQ(sc);
        }
 
@@ -7962,6 +8103,7 @@ iwm_attach(struct device *parent, struct device *self,
            IEEE80211_C_RSN |           /* WPA/RSN */
            IEEE80211_C_SCANALL |       /* device scans all channels at once */
            IEEE80211_C_SCANALLBAND |   /* device scans all bands at once */
+           IEEE80211_C_MONITOR |       /* monitor mode supported */
            IEEE80211_C_SHSLOT |        /* short slot time supported */
            IEEE80211_C_SHPREAMBLE;     /* short preamble supported */
 

Reply via email to