diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c index b2a6e99..3424abf 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,15 @@ 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); + + sc->sc_tq = taskqueue_create("iwn_taskq", M_WAITOK, + taskqueue_thread_enqueue, &sc->sc_tq); + error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "iwn_taskq"); + if (error != 0) { + device_printf(dev, "can't start threads, error %d\n", error); + goto fail; + } iwn_sysctlattach(sc); @@ -1326,6 +1336,10 @@ iwn_detach(device_t dev) ieee80211_draintask(ic, &sc->sc_radiooff_task); iwn_stop(sc); + + taskqueue_drain_all(sc->sc_tq); + taskqueue_free(sc->sc_tq); + callout_drain(&sc->watchdog_to); callout_drain(&sc->calib_to); ieee80211_ifdetach(ic); @@ -3860,8 +3874,8 @@ iwn_intr(void *arg) #endif /* Dump firmware error log and stop. */ iwn_fatal_intr(sc); - ifp->if_flags &= ~IFF_UP; - iwn_stop_locked(sc); + + taskqueue_enqueue(sc->sc_tq, &sc->sc_panic_task); goto done; } if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || @@ -8315,6 +8329,38 @@ iwn_radio_off(void *arg0, int pending) } static void +iwn_panicked(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + int error; + + if (vap == NULL) { + printf("%s: null vap\n", __func__); + return; + } + + device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; " + "resetting...\n", __func__, vap->iv_state); + + iwn_stop(sc); + iwn_init(sc); + iwn_start(sc->sc_ifp); + if (vap->iv_state >= IEEE80211_S_AUTH && + (error = iwn_auth(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to auth state\n", __func__); + } + if (vap->iv_state >= IEEE80211_S_RUN && + (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..c69f997 100644 --- a/sys/dev/iwn/if_iwnvar.h +++ b/sys/dev/iwn/if_iwnvar.h @@ -308,6 +308,10 @@ struct iwn_softc { struct task sc_reinit_task; struct task sc_radioon_task; struct task sc_radiooff_task; + struct task sc_panic_task; + + /* Taskqueue */ + struct taskqueue *sc_tq; /* Calibration information */ struct callout calib_to;