On Fri, 30 Aug 2019, Stefan Sperling wrote: > 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. > [...] > Please test this on as many iwm devices as possible. Thanks!
I've tested this some. I used nc(1) to transfer a 500MB file between iwm(4) and my ral(4) AP once in each direction, both with and without the patch; diff'd the netstat -s output; and tried the one of the most demanding tests I know for an interface, which is: AP# ping -q -s 10 -l 400 -c 400 -f iwm_machine I've seen no problems or regressions. The patched transfer saw ~1/3 of the duplicate packets received but this could easily be noise. Every ping was received, according to netstat. A big caveat is that my ral(4) AP is 11g and unlikely to be pushing iwm(4) hard. I forced ral(4) to OFDM54, and iwm(4) was seeing 73% signal strength from it. I've not used monitor mode before. I was expecting to see frames from other nodes in tcpdump with monitor mode enabled, but didn't, whether iwm(4) was associated with a network or not. I didn't see any "unhandled firmware response" errors. best, Richard. iwm0 at pci1 dev 0 function 0 "Intel Dual Band Wireless AC 7265" rev 0x69, msi iwm0: hw rev 0x210, fw ver 16.242414.0, address xx:xx:xx:xx:xx:xx > > 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 */ > > >