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 stsp@openbsd.org (OpenBSD Git 0ddb056fb7370664b1d4b84392697cb17d1a414a). Submitted by: Augustin Cavalier <waddlesplash@gmail.com> (Haiku) Obtained from: DragonFlyBSD (8a41b10ac639d0609878696808387a6799d39b57)
This commit is contained in:
parent
ef217a3417
commit
8d969c53d2
@ -350,7 +350,6 @@ static int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *,
|
||||
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 *sc)
|
||||
__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 *sc)
|
||||
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 *sc)
|
||||
"%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 *sc)
|
||||
"%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 *sc)
|
||||
|
||||
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,6 +4219,97 @@ 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_state nstate, int arg)
|
||||
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);
|
||||
|
||||
|
@ -309,7 +309,12 @@ iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in,
|
||||
* 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
|
||||
|
||||
/*
|
||||
|
@ -286,7 +286,7 @@ iwm_mvm_rm_sta(struct iwm_softc *sc, struct ieee80211vap *vap,
|
||||
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 */
|
||||
|
@ -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) \
|
||||
|
Loading…
Reference in New Issue
Block a user