iwn: some initialization / RF switch state change fixes.
- Check return code from initialization path; otherwise, vap state may be wrong after an error. - Do not try to run iwn_stop() / iwn_init() multiple times. - Merge iwn_radio_on/off() and move RFKILL bit check into the task. - Try to handle possible RF switch state change in S3 state (PR 181694). PR: 181694 Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D9797
This commit is contained in:
parent
3092c53fe7
commit
fa0604f793
@ -217,7 +217,7 @@ static void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, int, int,
|
||||
static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
|
||||
static void iwn_notif_intr(struct iwn_softc *);
|
||||
static void iwn_wakeup_intr(struct iwn_softc *);
|
||||
static void iwn_rftoggle_intr(struct iwn_softc *);
|
||||
static void iwn_rftoggle_task(void *, int);
|
||||
static void iwn_fatal_intr(struct iwn_softc *);
|
||||
static void iwn_intr(void *);
|
||||
static void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
|
||||
@ -334,11 +334,9 @@ static int iwn5000_nic_config(struct iwn_softc *);
|
||||
static int iwn_hw_prepare(struct iwn_softc *);
|
||||
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(struct iwn_softc *);
|
||||
static int iwn_init_locked(struct iwn_softc *);
|
||||
static int iwn_init(struct iwn_softc *);
|
||||
static void iwn_stop_locked(struct iwn_softc *);
|
||||
static void iwn_stop(struct iwn_softc *);
|
||||
static void iwn_scan_start(struct ieee80211com *);
|
||||
@ -679,8 +677,7 @@ iwn_attach(device_t dev)
|
||||
callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
|
||||
callout_init_mtx(&sc->scan_timeout, &sc->sc_mtx, 0);
|
||||
callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
|
||||
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_rftoggle_task, 0, iwn_rftoggle_task, sc);
|
||||
TASK_INIT(&sc->sc_panic_task, 0, iwn_panicked, sc);
|
||||
TASK_INIT(&sc->sc_xmit_task, 0, iwn_xmit_task, sc);
|
||||
|
||||
@ -1401,8 +1398,6 @@ iwn_detach(device_t dev)
|
||||
iwn_xmit_queue_drain(sc);
|
||||
IWN_UNLOCK(sc);
|
||||
|
||||
ieee80211_draintask(&sc->sc_ic, &sc->sc_radioon_task);
|
||||
ieee80211_draintask(&sc->sc_ic, &sc->sc_radiooff_task);
|
||||
iwn_stop(sc);
|
||||
|
||||
taskqueue_drain_all(sc->sc_tq);
|
||||
@ -4013,19 +4008,28 @@ iwn_wakeup_intr(struct iwn_softc *sc)
|
||||
}
|
||||
|
||||
static void
|
||||
iwn_rftoggle_intr(struct iwn_softc *sc)
|
||||
iwn_rftoggle_task(void *arg, int npending)
|
||||
{
|
||||
struct iwn_softc *sc = arg;
|
||||
struct ieee80211com *ic = &sc->sc_ic;
|
||||
uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
|
||||
uint32_t tmp;
|
||||
|
||||
IWN_LOCK_ASSERT(sc);
|
||||
IWN_LOCK(sc);
|
||||
tmp = IWN_READ(sc, IWN_GP_CNTRL);
|
||||
IWN_UNLOCK(sc);
|
||||
|
||||
device_printf(sc->sc_dev, "RF switch: radio %s\n",
|
||||
(tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
|
||||
if (tmp & IWN_GP_CNTRL_RFKILL)
|
||||
ieee80211_runtask(ic, &sc->sc_radioon_task);
|
||||
else
|
||||
ieee80211_runtask(ic, &sc->sc_radiooff_task);
|
||||
if (!(tmp & IWN_GP_CNTRL_RFKILL)) {
|
||||
ieee80211_suspend_all(ic);
|
||||
|
||||
/* Enable interrupts to get RF toggle notification. */
|
||||
IWN_LOCK(sc);
|
||||
IWN_WRITE(sc, IWN_INT, 0xffffffff);
|
||||
IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
|
||||
IWN_UNLOCK(sc);
|
||||
} else
|
||||
ieee80211_resume_all(ic);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4139,7 +4143,7 @@ iwn_intr(void *arg)
|
||||
IWN_WRITE(sc, IWN_FH_INT, r2);
|
||||
|
||||
if (r1 & IWN_INT_RF_TOGGLED) {
|
||||
iwn_rftoggle_intr(sc);
|
||||
taskqueue_enqueue(sc->sc_tq, &sc->sc_rftoggle_task);
|
||||
goto done;
|
||||
}
|
||||
if (r1 & IWN_INT_CT_REACHED) {
|
||||
@ -5084,25 +5088,28 @@ static void
|
||||
iwn_parent(struct ieee80211com *ic)
|
||||
{
|
||||
struct iwn_softc *sc = ic->ic_softc;
|
||||
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
|
||||
int startall = 0, stop = 0;
|
||||
|
||||
IWN_LOCK(sc);
|
||||
struct ieee80211vap *vap;
|
||||
int error;
|
||||
|
||||
if (ic->ic_nrunning > 0) {
|
||||
if (!(sc->sc_flags & IWN_FLAG_RUNNING)) {
|
||||
iwn_init_locked(sc);
|
||||
if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
|
||||
startall = 1;
|
||||
else
|
||||
stop = 1;
|
||||
error = iwn_init(sc);
|
||||
|
||||
switch (error) {
|
||||
case 0:
|
||||
ieee80211_start_all(ic);
|
||||
break;
|
||||
case EAGAIN:
|
||||
/* radio is disabled via RFkill switch */
|
||||
taskqueue_enqueue(sc->sc_tq, &sc->sc_rftoggle_task);
|
||||
break;
|
||||
default:
|
||||
vap = TAILQ_FIRST(&ic->ic_vaps);
|
||||
if (vap != NULL)
|
||||
ieee80211_stop(vap);
|
||||
break;
|
||||
}
|
||||
} else if (sc->sc_flags & IWN_FLAG_RUNNING)
|
||||
iwn_stop_locked(sc);
|
||||
IWN_UNLOCK(sc);
|
||||
if (startall)
|
||||
ieee80211_start_all(ic);
|
||||
else if (vap != NULL && stop)
|
||||
ieee80211_stop(vap);
|
||||
} else
|
||||
iwn_stop(sc);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -8680,41 +8687,6 @@ iwn_hw_stop(struct iwn_softc *sc)
|
||||
iwn_apm_stop(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
iwn_radio_on(void *arg0, int pending)
|
||||
{
|
||||
struct iwn_softc *sc = arg0;
|
||||
struct ieee80211com *ic = &sc->sc_ic;
|
||||
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
|
||||
|
||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
|
||||
|
||||
if (vap != NULL) {
|
||||
iwn_init(sc);
|
||||
ieee80211_init(vap);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iwn_radio_off(void *arg0, int pending)
|
||||
{
|
||||
struct iwn_softc *sc = arg0;
|
||||
struct ieee80211com *ic = &sc->sc_ic;
|
||||
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
|
||||
|
||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
|
||||
|
||||
iwn_stop(sc);
|
||||
if (vap != NULL)
|
||||
ieee80211_stop(vap);
|
||||
|
||||
/* Enable interrupts to get RF toggle notification. */
|
||||
IWN_LOCK(sc);
|
||||
IWN_WRITE(sc, IWN_INT, 0xffffffff);
|
||||
IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
|
||||
IWN_UNLOCK(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
iwn_panicked(void *arg0, int pending)
|
||||
{
|
||||
@ -8746,7 +8718,11 @@ iwn_panicked(void *arg0, int pending)
|
||||
IWN_LOCK(sc);
|
||||
|
||||
iwn_stop_locked(sc);
|
||||
iwn_init_locked(sc);
|
||||
if ((error = iwn_init_locked(sc)) != 0) {
|
||||
device_printf(sc->sc_dev,
|
||||
"%s: could not init hardware\n", __func__);
|
||||
goto unlock;
|
||||
}
|
||||
if (vap->iv_state >= IEEE80211_S_AUTH &&
|
||||
(error = iwn_auth(sc, vap)) != 0) {
|
||||
device_printf(sc->sc_dev,
|
||||
@ -8758,11 +8734,12 @@ iwn_panicked(void *arg0, int pending)
|
||||
"%s: could not move to run state\n", __func__);
|
||||
}
|
||||
|
||||
unlock:
|
||||
IWN_UNLOCK(sc);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
iwn_init_locked(struct iwn_softc *sc)
|
||||
{
|
||||
int error;
|
||||
@ -8771,6 +8748,9 @@ iwn_init_locked(struct iwn_softc *sc)
|
||||
|
||||
IWN_LOCK_ASSERT(sc);
|
||||
|
||||
if (sc->sc_flags & IWN_FLAG_RUNNING)
|
||||
goto end;
|
||||
|
||||
sc->sc_flags |= IWN_FLAG_RUNNING;
|
||||
|
||||
if ((error = iwn_hw_prepare(sc)) != 0) {
|
||||
@ -8785,12 +8765,8 @@ iwn_init_locked(struct iwn_softc *sc)
|
||||
|
||||
/* Check that the radio is not disabled by hardware switch. */
|
||||
if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
|
||||
device_printf(sc->sc_dev,
|
||||
"radio is disabled by hardware switch\n");
|
||||
/* Enable interrupts to get RF toggle notifications. */
|
||||
IWN_WRITE(sc, IWN_INT, 0xffffffff);
|
||||
IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
|
||||
return;
|
||||
error = EAGAIN;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Read firmware images from the filesystem. */
|
||||
@ -8821,26 +8797,29 @@ iwn_init_locked(struct iwn_softc *sc)
|
||||
|
||||
callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
|
||||
|
||||
end:
|
||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
|
||||
|
||||
return;
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
sc->sc_flags &= ~IWN_FLAG_RUNNING;
|
||||
iwn_stop_locked(sc);
|
||||
|
||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
iwn_init(struct iwn_softc *sc)
|
||||
{
|
||||
int error;
|
||||
|
||||
IWN_LOCK(sc);
|
||||
iwn_init_locked(sc);
|
||||
error = iwn_init_locked(sc);
|
||||
IWN_UNLOCK(sc);
|
||||
|
||||
if (sc->sc_flags & IWN_FLAG_RUNNING)
|
||||
ieee80211_start_all(&sc->sc_ic);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -8849,6 +8828,9 @@ iwn_stop_locked(struct iwn_softc *sc)
|
||||
|
||||
IWN_LOCK_ASSERT(sc);
|
||||
|
||||
if (!(sc->sc_flags & IWN_FLAG_RUNNING))
|
||||
return;
|
||||
|
||||
sc->sc_is_scanning = 0;
|
||||
sc->sc_tx_timer = 0;
|
||||
callout_stop(&sc->watchdog_to);
|
||||
|
@ -305,8 +305,7 @@ struct iwn_softc {
|
||||
int sc_cap_off; /* PCIe Capabilities. */
|
||||
|
||||
/* Tasks used by the driver */
|
||||
struct task sc_radioon_task;
|
||||
struct task sc_radiooff_task;
|
||||
struct task sc_rftoggle_task;
|
||||
struct task sc_panic_task;
|
||||
struct task sc_xmit_task;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user