Author: kevans
Date: Thu Jan 24 03:45:55 2019
New Revision: 343381
URL: https://svnweb.freebsd.org/changeset/base/343381

Log:
  iwm - Track firmware state better, and improve handling in iwm_newstate().
  
  * This avoids firmware resets in all the cases in iwm_newstate(). Instead
  iwm_bring_down_firmware() is called, which tears down all the STA
  connection state, according to the sc->sc_firmware_state value.
  
  * Improve the behaviour of the LED blinking a bit, so it only blinks when
  there really is a wireless scan going on.
  
  * Print the newstate arg in debug output of iwm_newstate(), to help in
  debugging.
  
  This is inspired by the firmware state maintaining change in OpenBSD's iwm,
  by s...@openbsd.org (OpenBSD Git 0ddb056fb7370664b1d4b84392697cb17d1a414a).
  
  Submitted by: Augustin Cavalier <waddlespl...@gmail.com> (Haiku)
  Obtained from:        DragonFlyBSD (8a41b10ac639d0609878696808387a6799d39b57)

Modified:
  head/sys/dev/iwm/if_iwm.c
  head/sys/dev/iwm/if_iwm_mac_ctxt.c
  head/sys/dev/iwm/if_iwm_sta.c
  head/sys/dev/iwm/if_iwmvar.h

Modified: head/sys/dev/iwm/if_iwm.c
==============================================================================
--- head/sys/dev/iwm/if_iwm.c   Thu Jan 24 03:45:24 2019        (r343380)
+++ head/sys/dev/iwm/if_iwm.c   Thu Jan 24 03:45:55 2019        (r343381)
@@ -350,7 +350,6 @@ static int  iwm_raw_xmit(struct ieee80211_node *, struc
                             const struct ieee80211_bpf_params *);
 static int     iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_vap *);
 static int     iwm_auth(struct ieee80211vap *, struct iwm_softc *);
-static int     iwm_release(struct iwm_softc *, struct iwm_node *);
 static struct ieee80211_node *
                iwm_node_alloc(struct ieee80211vap *,
                               const uint8_t[IEEE80211_ADDR_LEN]);
@@ -1263,6 +1262,7 @@ iwm_stop_device(struct iwm_softc *sc)
                iv->phy_ctxt = NULL;
                iv->is_uploaded = 0;
        }
+       sc->sc_firmware_state = 0;
 
        /* device going down, Stop using ICT table */
        sc->sc_flags &= ~IWM_FLAG_USE_ICT;
@@ -3951,8 +3951,11 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *s
            __func__,
            vap,
            ni);
+       IWM_DPRINTF(sc, IWM_DEBUG_STATE, "%s: Current node bssid: %s\n",
+           __func__, ether_sprintf(ni->ni_bssid));
 
        in->in_assoc = 0;
+       iv->iv_auth = 1;
 
        /*
         * Firmware bug - it'll crash if the beacon interval is less
@@ -4004,6 +4007,7 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *s
                        goto out;
                }
        }
+       sc->sc_firmware_state = 1;
 
        if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
            in->in_ni.ni_chan, 1, 1)) != 0) {
@@ -4018,6 +4022,7 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *s
                    "%s: binding update cmd\n", __func__);
                goto out;
        }
+       sc->sc_firmware_state = 2;
        /*
         * Authentication becomes unreliable when powersaving is left enabled
         * here. Powersaving will be activated again when association has
@@ -4037,6 +4042,7 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *s
                    "%s: failed to add sta\n", __func__);
                goto out;
        }
+       sc->sc_firmware_state = 3;
 
        /*
         * Prevent the FW from wandering off channel during association
@@ -4049,81 +4055,12 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *s
 
        error = 0;
 out:
+       if (error != 0)
+               iv->iv_auth = 0;
        ieee80211_free_node(ni);
        return (error);
 }
 
-static int
-iwm_release(struct iwm_softc *sc, struct iwm_node *in)
-{
-       uint32_t tfd_msk;
-
-       /*
-        * Ok, so *technically* the proper set of calls for going
-        * from RUN back to SCAN is:
-        *
-        * iwm_mvm_power_mac_disable(sc, in);
-        * iwm_mvm_mac_ctxt_changed(sc, vap);
-        * iwm_mvm_rm_sta(sc, in);
-        * iwm_mvm_update_quotas(sc, NULL);
-        * iwm_mvm_mac_ctxt_changed(sc, in);
-        * iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap));
-        * iwm_mvm_mac_ctxt_remove(sc, in);
-        *
-        * However, that freezes the device not matter which permutations
-        * and modifications are attempted.  Obviously, this driver is missing
-        * something since it works in the Linux driver, but figuring out what
-        * is missing is a little more complicated.  Now, since we're going
-        * back to nothing anyway, we'll just do a complete device reset.
-        * Up your's, device!
-        */
-       /*
-        * Just using 0xf for the queues mask is fine as long as we only
-        * get here from RUN state.
-        */
-       tfd_msk = 0xf;
-       iwm_xmit_queue_drain(sc);
-       iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC);
-       /*
-        * We seem to get away with just synchronously sending the
-        * IWM_TXPATH_FLUSH command.
-        */
-//     iwm_trans_wait_tx_queue_empty(sc, tfd_msk);
-       iwm_stop_device(sc);
-       iwm_init_hw(sc);
-       if (in)
-               in->in_assoc = 0;
-       return 0;
-
-#if 0
-       int error;
-
-       iwm_mvm_power_mac_disable(sc, in);
-
-       if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
-               device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error);
-               return error;
-       }
-
-       if ((error = iwm_mvm_rm_sta(sc, in)) != 0) {
-               device_printf(sc->sc_dev, "sta remove fail %d\n", error);
-               return error;
-       }
-       error = iwm_mvm_rm_sta(sc, in);
-       in->in_assoc = 0;
-       iwm_mvm_update_quotas(sc, NULL);
-       if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
-               device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error);
-               return error;
-       }
-       iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap));
-
-       iwm_mvm_mac_ctxt_remove(sc, in);
-
-       return error;
-#endif
-}
-
 static struct ieee80211_node *
 iwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
 {
@@ -4282,7 +4219,98 @@ iwm_media_change(struct ifnet *ifp)
        return error;
 }
 
+static void
+iwm_bring_down_firmware(struct iwm_softc *sc, struct ieee80211vap *vap)
+{
+       struct iwm_vap *ivp = IWM_VAP(vap);
+       int error;
 
+       ivp->iv_auth = 0;
+       if (sc->sc_firmware_state == 3) {
+               iwm_xmit_queue_drain(sc);
+//             iwm_mvm_flush_tx_path(sc, 0xf, IWM_CMD_SYNC);
+               error = iwm_mvm_rm_sta(sc, vap, TRUE);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to remove station: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state == 3) {
+               error = iwm_mvm_mac_ctxt_changed(sc, vap);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to change mac context: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state == 3) {
+               error = iwm_mvm_sf_update(sc, vap, FALSE);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to update smart FIFO: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state == 3) {
+               error = iwm_mvm_rm_sta_id(sc, vap);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to remove station id: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state == 3) {
+               error = iwm_mvm_update_quotas(sc, NULL);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to update PHY quota: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state == 3) {
+               /* XXX Might need to specify bssid correctly. */
+               error = iwm_mvm_mac_ctxt_changed(sc, vap);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to change mac context: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state == 3) {
+               sc->sc_firmware_state = 2;
+       }
+       if (sc->sc_firmware_state > 1) {
+               error = iwm_mvm_binding_remove_vif(sc, ivp);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to remove channel ctx: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state > 1) {
+               sc->sc_firmware_state = 1;
+       }
+       ivp->phy_ctxt = NULL;
+       if (sc->sc_firmware_state > 0) {
+               error = iwm_mvm_mac_ctxt_changed(sc, vap);
+               if (error) {
+                       device_printf(sc->sc_dev,
+                           "%s: Failed to change mac context: %d\n",
+                           __func__, error);
+               }
+       }
+       if (sc->sc_firmware_state > 0) {
+               error = iwm_mvm_power_update_mac(sc);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "%s: failed to update power management\n",
+                           __func__);
+               }
+       }
+       sc->sc_firmware_state = 0;
+}
+
 static int
 iwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
 {
@@ -4293,115 +4321,64 @@ iwm_newstate(struct ieee80211vap *vap, enum ieee80211_
        int error;
 
        IWM_DPRINTF(sc, IWM_DEBUG_STATE,
-           "switching state %s -> %s\n",
+           "switching state %s -> %s arg=0x%x\n",
            ieee80211_state_name[vap->iv_state],
-           ieee80211_state_name[nstate]);
+           ieee80211_state_name[nstate],
+           arg);
+
        IEEE80211_UNLOCK(ic);
        IWM_LOCK(sc);
 
-       if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state)
+       if ((sc->sc_flags & IWM_FLAG_SCAN_RUNNING) &&
+           (nstate == IEEE80211_S_AUTH ||
+            nstate == IEEE80211_S_ASSOC ||
+            nstate == IEEE80211_S_RUN)) {
+               /* Stop blinking for a scan, when authenticating. */
                iwm_led_blink_stop(sc);
+       }
 
-       /* disable beacon filtering if we're hopping out of RUN */
-       if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) {
+       if (vap->iv_state == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN) {
+               iwm_mvm_led_disable(sc);
+               /* disable beacon filtering if we're hopping out of RUN */
                iwm_mvm_disable_beacon_filter(sc);
-
                if (((in = IWM_NODE(vap->iv_bss)) != NULL))
                        in->in_assoc = 0;
+       }
 
-               if (nstate == IEEE80211_S_INIT) {
-                       IWM_UNLOCK(sc);
-                       IEEE80211_LOCK(ic);
-                       error = ivp->iv_newstate(vap, nstate, arg);
-                       IEEE80211_UNLOCK(ic);
-                       IWM_LOCK(sc);
-                       iwm_release(sc, NULL);
-                       IWM_UNLOCK(sc);
-                       IEEE80211_LOCK(ic);
-                       return error;
-               }
-
+       if ((vap->iv_state == IEEE80211_S_RUN ||
+            vap->iv_state == IEEE80211_S_ASSOC) &&
+           nstate == IEEE80211_S_INIT) {
                /*
-                * It's impossible to directly go RUN->SCAN. If we iwm_release()
-                * above then the card will be completely reinitialized,
-                * so the driver must do everything necessary to bring the card
-                * from INIT to SCAN.
-                *
-                * Additionally, upon receiving deauth frame from AP,
-                * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH
-                * state. This will also fail with this driver, so bring the FSM
-                * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as 
well.
-                *
-                * XXX TODO: fix this for FreeBSD!
+                * In this case, iv_newstate() wants to send an 80211 frame on
+                * the network that we are leaving. So we need to call it,
+                * before tearing down all the firmware state.
                 */
-               if (nstate == IEEE80211_S_SCAN ||
-                   nstate == IEEE80211_S_AUTH ||
-                   nstate == IEEE80211_S_ASSOC) {
-                       IWM_DPRINTF(sc, IWM_DEBUG_STATE,
-                           "Force transition to INIT; MGT=%d\n", arg);
-                       IWM_UNLOCK(sc);
-                       IEEE80211_LOCK(ic);
-                       /* Always pass arg as -1 since we can't Tx right now. */
-                       /*
-                        * XXX arg is just ignored anyway when transitioning
-                        *     to IEEE80211_S_INIT.
-                        */
-                       vap->iv_newstate(vap, IEEE80211_S_INIT, -1);
-                       IWM_DPRINTF(sc, IWM_DEBUG_STATE,
-                           "Going INIT->SCAN\n");
-                       nstate = IEEE80211_S_SCAN;
-                       IEEE80211_UNLOCK(ic);
-                       IWM_LOCK(sc);
-               }
+               IWM_UNLOCK(sc);
+               IEEE80211_LOCK(ic);
+               ivp->iv_newstate(vap, nstate, arg);
+               IEEE80211_UNLOCK(ic);
+               IWM_LOCK(sc);
+               iwm_bring_down_firmware(sc, vap);
+               IWM_UNLOCK(sc);
+               IEEE80211_LOCK(ic);
+               return 0;
        }
 
        switch (nstate) {
        case IEEE80211_S_INIT:
        case IEEE80211_S_SCAN:
-               if (vap->iv_state == IEEE80211_S_AUTH ||
-                   vap->iv_state == IEEE80211_S_ASSOC) {
-                       int myerr;
-                       IWM_UNLOCK(sc);
-                       IEEE80211_LOCK(ic);
-                       myerr = ivp->iv_newstate(vap, nstate, arg);
-                       IEEE80211_UNLOCK(ic);
-                       IWM_LOCK(sc);
-                       error = iwm_mvm_rm_sta(sc, vap, FALSE);
-                        if (error) {
-                                device_printf(sc->sc_dev,
-                                   "%s: Failed to remove station: %d\n",
-                                   __func__, error);
-                       }
-                       error = iwm_mvm_mac_ctxt_changed(sc, vap);
-                        if (error) {
-                                device_printf(sc->sc_dev,
-                                    "%s: Failed to change mac context: %d\n",
-                                    __func__, error);
-                        }
-                        error = iwm_mvm_binding_remove_vif(sc, ivp);
-                        if (error) {
-                                device_printf(sc->sc_dev,
-                                    "%s: Failed to remove channel ctx: %d\n",
-                                    __func__, error);
-                        }
-                       ivp->phy_ctxt = NULL;
-                       error = iwm_mvm_power_update_mac(sc);
-                       if (error != 0) {
-                               device_printf(sc->sc_dev,
-                                   "%s: failed to update power management\n",
-                                   __func__);
-                       }
-                       IWM_UNLOCK(sc);
-                       IEEE80211_LOCK(ic);
-                       return myerr;
-               }
                break;
 
        case IEEE80211_S_AUTH:
+               iwm_bring_down_firmware(sc, vap);
                if ((error = iwm_auth(vap, sc)) != 0) {
                        device_printf(sc->sc_dev,
                            "%s: could not move to auth state: %d\n",
                            __func__, error);
+                       iwm_bring_down_firmware(sc, vap);
+                       IWM_UNLOCK(sc);
+                       IEEE80211_LOCK(ic);
+                       return 1;
                }
                break;
 
@@ -5566,6 +5543,8 @@ iwm_intr(void *arg)
                device_printf(sc->sc_dev,
                    "  802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state);
 
+               /* Reset our firmware state tracking. */
+               sc->sc_firmware_state = 0;
                /* Don't stop the device; just do a VAP restart */
                IWM_UNLOCK(sc);
 

Modified: head/sys/dev/iwm/if_iwm_mac_ctxt.c
==============================================================================
--- head/sys/dev/iwm/if_iwm_mac_ctxt.c  Thu Jan 24 03:45:24 2019        
(r343380)
+++ head/sys/dev/iwm/if_iwm_mac_ctxt.c  Thu Jan 24 03:45:55 2019        
(r343381)
@@ -309,7 +309,12 @@ iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *sc, stru
         *     iwm_mvm_mac_ctxt_changed() when already authenticating or
         *     associating, ni->ni_bssid should always make sense here.
         */
-       IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
+       if (ivp->iv_auth) {
+               IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
+       } else {
+               /* XXX Or maybe all zeroes address? */
+               IEEE80211_ADDR_COPY(cmd->bssid_addr, ieee80211broadcastaddr);
+       }
 #endif
 
        /*

Modified: head/sys/dev/iwm/if_iwm_sta.c
==============================================================================
--- head/sys/dev/iwm/if_iwm_sta.c       Thu Jan 24 03:45:24 2019        
(r343380)
+++ head/sys/dev/iwm/if_iwm_sta.c       Thu Jan 24 03:45:55 2019        
(r343381)
@@ -286,7 +286,7 @@ iwm_mvm_rm_sta(struct iwm_softc *sc, struct ieee80211v
        for (ac = 0; ac < WME_NUM_AC; ac++) {
                tfd_queue_msk |= htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
        }
-       ret = iwm_mvm_flush_tx_path(sc, tfd_queue_msk, 0);
+       ret = iwm_mvm_flush_tx_path(sc, tfd_queue_msk, IWM_CMD_SYNC);
        if (ret)
                return ret;
 #ifdef notyet /* function not yet implemented */

Modified: head/sys/dev/iwm/if_iwmvar.h
==============================================================================
--- head/sys/dev/iwm/if_iwmvar.h        Thu Jan 24 03:45:24 2019        
(r343380)
+++ head/sys/dev/iwm/if_iwmvar.h        Thu Jan 24 03:45:55 2019        
(r343381)
@@ -365,6 +365,7 @@ struct iwm_bf_data {
 struct iwm_vap {
        struct ieee80211vap     iv_vap;
        int                     is_uploaded;
+       int                     iv_auth;
 
        int                     (*iv_newstate)(struct ieee80211vap *,
                                    enum ieee80211_state, int);
@@ -558,6 +559,9 @@ struct iwm_softc {
        boolean_t               sc_ps_disabled;
 
        int                     sc_ltr_enabled;
+
+       /* Track firmware state for STA association. */
+       int                     sc_firmware_state;
 };
 
 #define IWM_LOCK_INIT(_sc) \
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to