diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c index b2a6e99..7cd42d0 100644 --- a/sys/dev/iwn/if_iwn.c +++ b/sys/dev/iwn/if_iwn.c @@ -331,6 +331,7 @@ static int iwn_hw_init(struct iwn_softc *); static void iwn_hw_stop(struct iwn_softc *); static void iwn_radio_on(void *, int); static void iwn_radio_off(void *, int); +static void iwn_panicked(void *, int); static void iwn_init_locked(struct iwn_softc *); static void iwn_init(void *); static void iwn_stop_locked(struct iwn_softc *); @@ -670,6 +671,7 @@ iwn_attach(device_t dev) TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc); TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc); TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc); + TASK_INIT(&sc->sc_panic_task, 0, iwn_panicked, sc); iwn_sysctlattach(sc); @@ -1324,6 +1326,7 @@ iwn_detach(device_t dev) ieee80211_draintask(ic, &sc->sc_reinit_task); ieee80211_draintask(ic, &sc->sc_radioon_task); ieee80211_draintask(ic, &sc->sc_radiooff_task); + ieee80211_draintask(ic, &sc->sc_panic_task); iwn_stop(sc); callout_drain(&sc->watchdog_to); @@ -3804,6 +3807,7 @@ iwn_intr(void *arg) { struct iwn_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; uint32_t r1, r2, tmp; IWN_LOCK(sc); @@ -3860,8 +3864,10 @@ iwn_intr(void *arg) #endif /* Dump firmware error log and stop. */ iwn_fatal_intr(sc); - ifp->if_flags &= ~IFF_UP; - iwn_stop_locked(sc); + + device_printf(sc->sc_dev, "%s: reinit; %p\n", + __func__, &sc->fwname); + ieee80211_runtask(ic, &sc->sc_panic_task); goto done; } if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || @@ -8315,6 +8321,39 @@ iwn_radio_off(void *arg0, int pending) } static void +iwn_panicked(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp; + struct ieee80211com *ic; + struct ieee80211vap *vap; + int error; + + ifp = sc->sc_ifp; + ic = ifp->if_l2com; + vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap == NULL) { + printf("%s: null vap\n", __func__); + return; + } + + device_printf(sc->sc_dev, "%s: controller panicked; " + "resetting...\n", __func__); + iwn_stop(sc); + iwn_init(sc); + iwn_start(sc->sc_ifp); + if ((error = iwn_auth(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to auth state\n", __func__); + } + if ((error = iwn_run(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to run state\n", __func__); + } +} + +static void iwn_init_locked(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; diff --git a/sys/dev/iwn/if_iwnvar.h b/sys/dev/iwn/if_iwnvar.h index f146feb..83c1d78 100644 --- a/sys/dev/iwn/if_iwnvar.h +++ b/sys/dev/iwn/if_iwnvar.h @@ -308,6 +308,7 @@ struct iwn_softc { struct task sc_reinit_task; struct task sc_radioon_task; struct task sc_radiooff_task; + struct task sc_panic_task; /* Calibration information */ struct callout calib_to;