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 */