No regressions noticed using a single AP setup. iwn0 at pci2 dev 0 function 0 "Intel Centrino Advanced-N 6205" rev 0x34: msi, MIMO 2T2R, MoW
On Wed, Dec 13 2017, Stefan Sperling wrote: > Since nobody is reporting problems with iwm(4), I took some time to write the > corresponding diff for iwn(4) as well. I hope this increases test coverage :) > > Works for me on: > iwn0 at pci3 dev 0 function 0 "Intel Centrino Advanced-N 6200" rev 0x35: msi, > MIMO 2T2R, MoW > > Index: if_iwn.c > =================================================================== > RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v > retrieving revision 1.194 > diff -u -p -r1.194 if_iwn.c > --- if_iwn.c 26 Oct 2017 15:00:28 -0000 1.194 > +++ if_iwn.c 13 Dec 2017 16:07:07 -0000 > @@ -222,7 +222,9 @@ int iwn_config(struct iwn_softc *); > uint16_t iwn_get_active_dwell_time(struct iwn_softc *, uint16_t, > uint8_t); > uint16_t iwn_limit_dwell(struct iwn_softc *, uint16_t); > uint16_t iwn_get_passive_dwell_time(struct iwn_softc *, uint16_t); > -int iwn_scan(struct iwn_softc *, uint16_t); > +int iwn_scan(struct iwn_softc *, uint16_t, int); > +void iwn_scan_abort(struct iwn_softc *); > +int iwn_bgscan(struct ieee80211com *); > int iwn_auth(struct iwn_softc *, int); > int iwn_run(struct iwn_softc *); > int iwn_set_key(struct ieee80211com *, struct ieee80211_node *, > @@ -516,6 +518,7 @@ iwn_attach(struct device *parent, struct > if_attach(ifp); > ieee80211_ifattach(ifp); > ic->ic_node_alloc = iwn_node_alloc; > + ic->ic_bgscan_start = iwn_bgscan; > ic->ic_newassoc = iwn_newassoc; > ic->ic_updateedca = iwn_updateedca; > ic->ic_set_key = iwn_set_key; > @@ -1761,18 +1764,20 @@ iwn_newstate(struct ieee80211com *ic, en > struct iwn_node *wn = (void *)ni; > int error; > > - timeout_del(&sc->calib_to); > - > - if (ic->ic_state == IEEE80211_S_RUN && > - (ni->ni_flags & IEEE80211_NODE_HT)) > + if (ic->ic_state == IEEE80211_S_RUN) { > ieee80211_mira_cancel_timeouts(&wn->mn); > + timeout_del(&sc->calib_to); > + sc->calib.state = IWN_CALIB_STATE_INIT; > + if (sc->sc_flags & IWN_FLAG_BGSCAN) > + iwn_scan_abort(sc); > + } > > switch (nstate) { > case IEEE80211_S_SCAN: > /* Make the link LED blink while we're scanning. */ > iwn_set_led(sc, IWN_LED_LINK, 10, 10); > > - if ((error = iwn_scan(sc, IEEE80211_CHAN_2GHZ)) != 0) { > + if ((error = iwn_scan(sc, IEEE80211_CHAN_2GHZ, 0)) != 0) { > printf("%s: could not initiate scan\n", > sc->sc_dev.dv_xname); > return error; > @@ -1971,11 +1976,13 @@ iwn_rx_done(struct iwn_softc *sc, struct > struct ieee80211_frame *wh; > struct ieee80211_rxinfo rxi; > struct ieee80211_node *ni; > + struct ieee80211_channel *bss_chan = NULL; > struct mbuf *m, *m1; > struct iwn_rx_stat *stat; > caddr_t head; > uint32_t flags; > int error, len, rssi; > + uint8_t chan; > > if (desc->type == IWN_MPDU_RX_DONE) { > /* Check for prior RX_PHY notification. */ > @@ -2131,6 +2138,16 @@ iwn_rx_done(struct iwn_softc *sc, struct > > rssi = ops->get_rssi(stat); > > + chan = stat->chan; > + if (chan > IEEE80211_CHAN_MAX) > + chan = IEEE80211_CHAN_MAX; > + > + if (ni == ic->ic_bss) { > + bss_chan = ni->ni_chan; > + /* Fix current channel. */ > + ni->ni_chan = &ic->ic_channels[chan]; > + } > + > #if NBPFILTER > 0 > if (sc->sc_drvbpf != NULL) { > struct mbuf mb; > @@ -2140,9 +2157,8 @@ iwn_rx_done(struct iwn_softc *sc, struct > tap->wr_flags = 0; > if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE)) > tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; > - tap->wr_chan_freq = > - htole16(ic->ic_channels[stat->chan].ic_freq); > - chan_flags = ic->ic_channels[stat->chan].ic_flags; > + tap->wr_chan_freq = htole16(ic->ic_channels[chan].ic_freq); > + chan_flags = ic->ic_channels[chan].ic_flags; > if (ic->ic_curmode != IEEE80211_MODE_11N) > chan_flags &= ~IEEE80211_CHAN_HT; > tap->wr_chan_flags = htole16(chan_flags); > @@ -2187,6 +2203,10 @@ iwn_rx_done(struct iwn_softc *sc, struct > rxi.rxi_tstamp = 0; /* unused */ > ieee80211_input(ifp, m, ni, &rxi); > > + /* Restore BSS channel. */ > + if (ni == ic->ic_bss) > + ni->ni_chan = bss_chan; > + > /* Node is no longer needed. */ > ieee80211_release_node(ic, ni); > } > @@ -2586,6 +2606,9 @@ iwn_notif_intr(struct iwn_softc *sc) > DPRINTFN(2, ("scanning channel %d status %x\n", > scan->chan, letoh32(scan->status))); > > + if (sc->sc_flags & IWN_FLAG_BGSCAN) > + break; > + > /* Fix current channel. */ > ic->ic_bss->ni_chan = &ic->ic_channels[scan->chan]; > break; > @@ -2602,14 +2625,18 @@ iwn_notif_intr(struct iwn_softc *sc) > > if (scan->status == 1 && scan->chan <= 14 && > (sc->sc_flags & IWN_FLAG_HAS_5GHZ)) { > + int error; > /* > * We just finished scanning 2GHz channels, > * start scanning 5GHz ones. > */ > - if (iwn_scan(sc, IEEE80211_CHAN_5GHZ) == 0) > + error = iwn_scan(sc, IEEE80211_CHAN_5GHZ, > + (sc->sc_flags & IWN_FLAG_BGSCAN) ? 1 : 0); > + if (error == 0) > break; > } > ieee80211_end_scan(ifp); > + sc->sc_flags &= ~IWN_FLAG_BGSCAN; > break; > } > case IWN5000_CALIBRATION_RESULT: > @@ -4642,9 +4669,8 @@ iwn_limit_dwell(struct iwn_softc *sc, ui > * XXX Yes, the math should take into account that bintval > * is 1.024mS, not 1mS.. > */ > - if (bintval > 0) { > + if (ic->ic_state == IEEE80211_S_RUN && bintval > 0) > return (MIN(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100))); > - } > > /* No association context? Default */ > return (IWN_PASSIVE_DWELL_BASE); > @@ -4665,7 +4691,7 @@ iwn_get_passive_dwell_time(struct iwn_so > } > > int > -iwn_scan(struct iwn_softc *sc, uint16_t flags) > +iwn_scan(struct iwn_softc *sc, uint16_t flags, int bgscan) > { > struct ieee80211com *ic = &sc->sc_ic; > struct iwn_scan_hdr *hdr; > @@ -4695,6 +4721,18 @@ iwn_scan(struct iwn_softc *sc, uint16_t > hdr->quiet_time = htole16(10); /* timeout in milliseconds */ > hdr->quiet_threshold = htole16(1); /* min # of packets */ > > + if (bgscan) { > + int bintval; > + > + /* Set maximum off-channel time. */ > + hdr->max_out = htole32(200 * 1024); > + > + /* Configure scan pauses which service on-channel traffic. */ > + bintval = ic->ic_bss->ni_intval ? ic->ic_bss->ni_intval : 100; > + hdr->pause_scan = htole32(((100 / bintval) << 22) | > + ((100 % bintval) * 1024)); > + } > + > /* Select antennas for scanning. */ > rxchain = > IWN_RXCHAIN_VALID(sc->rxchainmask) | > @@ -4852,11 +4890,39 @@ iwn_scan(struct iwn_softc *sc, uint16_t > hdr->len = htole16(buflen); > > DPRINTF(("sending scan command nchan=%d\n", hdr->nchan)); > + if (bgscan) > + sc->sc_flags |= IWN_FLAG_BGSCAN; > error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1); > + if (bgscan && error) > + sc->sc_flags &= ~IWN_FLAG_BGSCAN; > free(buf, M_DEVBUF, IWN_SCAN_MAXSZ); > return error; > } > > +void > +iwn_scan_abort(struct iwn_softc *sc) > +{ > + iwn_cmd(sc, IWN_CMD_SCAN_ABORT, NULL, 0, 1); > + > + /* XXX Cannot wait for status response in interrupt context. */ > + DELAY(100); > + > + sc->sc_flags &= ~IWN_FLAG_BGSCAN; > +} > + > +int > +iwn_bgscan(struct ieee80211com *ic) > +{ > + struct iwn_softc *sc = ic->ic_softc; > + int error; > + > + error = iwn_scan(sc, IEEE80211_CHAN_2GHZ, 1); > + if (error) > + printf("%s: could not initiate background scan\n", > + sc->sc_dev.dv_xname); > + return error; > +} > + > int > iwn_auth(struct iwn_softc *sc, int arg) > { > @@ -4864,6 +4930,9 @@ iwn_auth(struct iwn_softc *sc, int arg) > struct ieee80211com *ic = &sc->sc_ic; > struct ieee80211_node *ni = ic->ic_bss; > int error, ridx; > + int bss_switch = > + (!IEEE80211_ADDR_EQ(sc->bss_node_addr, etheranyaddr) && > + !IEEE80211_ADDR_EQ(sc->bss_node_addr, ni->ni_macaddr)); > > /* Update adapter configuration. */ > IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); > @@ -4878,8 +4947,12 @@ iwn_auth(struct iwn_softc *sc, int arg) > } > if (ic->ic_flags & IEEE80211_F_SHSLOT) > sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); > + else > + sc->rxon.flags &= ~htole32(IWN_RXON_SHSLOT); > if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) > sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); > + else > + sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE); > switch (ic->ic_curmode) { > case IEEE80211_MODE_11A: > sc->rxon.cck_mask = 0; > @@ -4923,12 +4996,19 @@ iwn_auth(struct iwn_softc *sc, int arg) > * Make sure the firmware gets to see a beacon before we send > * the auth request. Otherwise the Tx attempt can fail due to > * the firmware's built-in regulatory domain enforcement. > + * Delaying here for every incoming deauth frame can result in a DoS. > * Don't delay if we're here because of an incoming frame (arg != -1) > * or if we're already waiting for a response (ic_mgt_timer != 0). > + * If we are switching APs after a background scan then net80211 has > + * just faked the reception of a deauth frame from our old AP, so it > + * is safe to delay in that case. > */ > - if (arg == -1 && ic->ic_mgt_timer == 0) > + if ((arg == -1 || bss_switch) && ic->ic_mgt_timer == 0) > DELAY(ni->ni_intval * 3 * IEEE80211_DUR_TU); > > + /* We can now clear the cached address of our previous AP. */ > + memset(sc->bss_node_addr, 0, sizeof(sc->bss_node_addr)); > + > return 0; > } > > @@ -4938,6 +5018,7 @@ iwn_run(struct iwn_softc *sc) > struct iwn_ops *ops = &sc->ops; > struct ieee80211com *ic = &sc->sc_ic; > struct ieee80211_node *ni = ic->ic_bss; > + struct iwn_node *wn = (void *)ni; > struct iwn_node_info node; > int error; > > @@ -5028,6 +5109,10 @@ iwn_run(struct iwn_softc *sc) > printf("%s: could not add BSS node\n", sc->sc_dev.dv_xname); > return error; > } > + > + /* Cache address of AP in case it changes after a background scan. */ > + IEEE80211_ADDR_COPY(sc->bss_node_addr, ni->ni_macaddr); > + > DPRINTF(("setting link quality for node %d\n", node.id)); > if ((error = iwn_set_link_quality(sc, ni)) != 0) { > printf("%s: could not setup link quality for node %d\n", > @@ -5045,10 +5130,7 @@ iwn_run(struct iwn_softc *sc) > sc->calib_cnt = 0; > timeout_add_msec(&sc->calib_to, 500); > > - if (ni->ni_flags & IEEE80211_NODE_HT) { > - struct iwn_node *wn = (void *)ni; > - ieee80211_mira_node_init(&wn->mn); > - } > + ieee80211_mira_node_init(&wn->mn); > > /* Link LED always on while associated. */ > iwn_set_led(sc, IWN_LED_LINK, 0, 1); > @@ -6443,6 +6525,8 @@ iwn_init(struct ifnet *ifp) > struct iwn_softc *sc = ifp->if_softc; > struct ieee80211com *ic = &sc->sc_ic; > int error; > + > + memset(sc->bss_node_addr, 0, sizeof(sc->bss_node_addr)); > > if ((error = iwn_hw_prepare(sc)) != 0) { > printf("%s: hardware not ready\n", sc->sc_dev.dv_xname); > Index: if_iwnreg.h > =================================================================== > RCS file: /cvs/src/sys/dev/pci/if_iwnreg.h,v > retrieving revision 1.54 > diff -u -p -r1.54 if_iwnreg.h > --- if_iwnreg.h 12 Aug 2017 09:13:06 -0000 1.54 > +++ if_iwnreg.h 13 Dec 2017 15:58:45 -0000 > @@ -437,6 +437,7 @@ struct iwn_tx_cmd { > #define IWN5000_CMD_CALIB_CONFIG 101 > #define IWN_CMD_SET_POWER_MODE 119 > #define IWN_CMD_SCAN 128 > +#define IWN_CMD_SCAN_ABORT 129 > #define IWN_CMD_TXPOWER_DBM 149 > #define IWN_CMD_TXPOWER 151 > #define IWN5000_CMD_TX_ANT_CONFIG 152 > @@ -823,8 +824,8 @@ struct iwn_scan_hdr { > uint16_t quiet_threshold; > uint16_t crc_threshold; > uint16_t rxchain; > - uint32_t max_svc; /* background scans */ > - uint32_t pause_svc; /* background scans */ > + uint32_t max_out; /* (in usec) background scans */ > + uint32_t pause_scan; /* (in usec) background scans */ > uint32_t flags; > uint32_t filter; > > Index: if_iwnvar.h > =================================================================== > RCS file: /cvs/src/sys/dev/pci/if_iwnvar.h,v > retrieving revision 1.32 > diff -u -p -r1.32 if_iwnvar.h > --- if_iwnvar.h 7 Dec 2016 15:48:44 -0000 1.32 > +++ if_iwnvar.h 13 Dec 2017 13:09:32 -0000 > @@ -202,6 +202,7 @@ struct iwn_softc { > #define IWN_FLAG_HAS_11N (1 << 6) > #define IWN_FLAG_ENH_SENS (1 << 7) > #define IWN_FLAG_ADV_BT_COEX (1 << 8) > +#define IWN_FLAG_BGSCAN (1 << 9) > > uint8_t hw_type; > > @@ -256,6 +257,8 @@ struct iwn_softc { > struct iwn_fw_info fw; > struct iwn_calib_info calibcmd[5]; > uint32_t errptr; > + > + uint8_t bss_node_addr[IEEE80211_ADDR_LEN]; > > struct iwn_rx_stat last_rx_stat; > int last_rx_valid; -- Renato Aguiar