If iwn(4) is in associated state (status: active), and I run:

        ifconfig iwn0 scan

it loses link (status: no network) and never comes back while the
LED keeps blinking. I need to run 'ifconfig iwn0 down' to recover.

What happens is the driver goes to SCAN state and sends a scan
command to the firmware and waits for a STOP_SCAN response.
This happens while the firmware is in associated state, with a
scan command which does *not* run a background scan. It seems the
firmware doesn't like this and never signals STOP_SCAN, so the
driver remains in SCAN state until the interface gets reset.

I can fix this problem by making the driver move the firmware out
of associated state during RUN->SCAN transitions. For completeness
we can do the same for RUN->{ASSOC,AUTH} and AUTH->ASSOC. i.e. always
keep the firmware's association state in sync with the driver's state
machine. The firmware should only be associated in RUN state.

The diff below does that, and also adds support for SCAN->SCAN
transitions to the state machine (which are a no-op in this driver).

This seems to be working well here with:
iwn0 at pci3 dev 0 function 0 "Intel Centrino Advanced-N 6200" rev 0x35: msi, 
MIMO 2T2R, MoW, address xx:xx:xx:xx:xx:xx

OK to commit? Does anybody else want to test this beforehand?

Index: if_iwn.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v
retrieving revision 1.198
diff -u -p -r1.198 if_iwn.c
--- if_iwn.c    9 Jan 2018 10:00:12 -0000       1.198
+++ if_iwn.c    31 Jan 2018 12:13:41 -0000
@@ -1772,6 +1772,25 @@ iwn_newstate(struct ieee80211com *ic, en
                        iwn_scan_abort(sc);
        }
 
+       if (ic->ic_state == IEEE80211_S_SCAN) {
+               if (nstate == IEEE80211_S_SCAN)
+                       return 0;
+               /* Turn LED off when leaving scan state. */
+               iwn_set_led(sc, IWN_LED_LINK, 1, 0);
+       }
+
+       if (ic->ic_state >= IEEE80211_S_ASSOC &&
+           nstate <= IEEE80211_S_ASSOC) {
+               /* Reset state to handle re- and disassociations. */
+               sc->rxon.associd = 0;
+               sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
+               sc->calib.state = IWN_CALIB_STATE_INIT;
+               error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
+               if (error != 0)
+                       printf("%s: RXON command failed\n",
+                           sc->sc_dev.dv_xname);
+       }                
+
        switch (nstate) {
        case IEEE80211_S_SCAN:
                /* Make the link LED blink while we're scanning. */
@@ -1790,11 +1809,6 @@ iwn_newstate(struct ieee80211com *ic, en
                        break;
                /* FALLTHROUGH */
        case IEEE80211_S_AUTH:
-               /* Reset state to handle reassociations correctly. */
-               sc->rxon.associd = 0;
-               sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
-               sc->calib.state = IWN_CALIB_STATE_INIT;
-
                if ((error = iwn_auth(sc, arg)) != 0) {
                        printf("%s: could not move to auth state\n",
                            sc->sc_dev.dv_xname);

Reply via email to