Author: avos
Date: Sat Feb 25 00:40:50 2017
New Revision: 314234
URL: https://svnweb.freebsd.org/changeset/base/314234

Log:
  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

Modified:
  head/sys/dev/iwn/if_iwn.c
  head/sys/dev/iwn/if_iwnvar.h

Modified: head/sys/dev/iwn/if_iwn.c
==============================================================================
--- head/sys/dev/iwn/if_iwn.c   Sat Feb 25 00:12:29 2017        (r314233)
+++ head/sys/dev/iwn/if_iwn.c   Sat Feb 25 00:40:50 2017        (r314234)
@@ -217,7 +217,7 @@ static void iwn_ampdu_tx_done(struct iwn
 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
 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);
 }
 
 /*
@@ -8681,41 +8688,6 @@ iwn_hw_stop(struct iwn_softc *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)
 {
        struct iwn_softc *sc = arg0;
@@ -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);

Modified: head/sys/dev/iwn/if_iwnvar.h
==============================================================================
--- head/sys/dev/iwn/if_iwnvar.h        Sat Feb 25 00:12:29 2017        
(r314233)
+++ head/sys/dev/iwn/if_iwnvar.h        Sat Feb 25 00:40:50 2017        
(r314234)
@@ -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;
 
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to