ath9k: Make sure we have current channel in ah_curchan before rf disable/enable
[pandora-kernel.git] / drivers / net / wireless / ath / ath9k / main.c
index 1839b55..6516a42 100644 (file)
@@ -35,14 +35,14 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
 #define CHAN2G(_freq, _idx)  { \
        .center_freq = (_freq), \
        .hw_value = (_idx), \
-       .max_power = 30, \
+       .max_power = 20, \
 }
 
 #define CHAN5G(_freq, _idx) { \
        .band = IEEE80211_BAND_5GHZ, \
        .center_freq = (_freq), \
        .hw_value = (_idx), \
-       .max_power = 30, \
+       .max_power = 20, \
 }
 
 /* Some 2 GHz radios are actually tunable on 2312-2732
@@ -231,6 +231,19 @@ static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
        }
 }
 
+static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
+                                               struct ieee80211_hw *hw)
+{
+       struct ieee80211_channel *curchan = hw->conf.channel;
+       struct ath9k_channel *channel;
+       u8 chan_idx;
+
+       chan_idx = curchan->hw_value;
+       channel = &sc->sc_ah->channels[chan_idx];
+       ath9k_update_ichannel(sc, hw, channel);
+       return channel;
+}
+
 /*
  * Set/change channels.  If the channel is really being changed, it's done
  * by reseting the chip.  To accomplish this we must first cleanup any pending
@@ -487,7 +500,7 @@ static void ath9k_tasklet(unsigned long data)
                 * the next Beacon.
                 */
                DPRINTF(sc, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n");
-               sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
+               sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC;
        }
 
        /* re-enable hardware interrupt */
@@ -914,6 +927,13 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
                if (avp->av_opmode == NL80211_IFTYPE_STATION) {
                        sc->curaid = bss_conf->aid;
                        ath9k_hw_write_associd(sc);
+
+                       /*
+                        * Request a re-configuration of Beacon related timers
+                        * on the receipt of the first Beacon frame (i.e.,
+                        * after time sync with the AP).
+                        */
+                       sc->sc_flags |= SC_OP_BEACON_SYNC;
                }
 
                /* Configure the beacon */
@@ -1103,6 +1123,9 @@ void ath_radio_enable(struct ath_softc *sc)
        ath9k_ps_wakeup(sc);
        ath9k_hw_configpcipowersave(ah, 0);
 
+       if (!ah->curchan)
+               ah->curchan = ath_get_curchannel(sc, sc->hw);
+
        spin_lock_bh(&sc->sc_resetlock);
        r = ath9k_hw_reset(ah, ah->curchan, false);
        if (r) {
@@ -1155,6 +1178,9 @@ void ath_radio_disable(struct ath_softc *sc)
        ath_stoprecv(sc);               /* turn off frame recv */
        ath_flushrecv(sc);              /* flush recv queue */
 
+       if (!ah->curchan)
+               ah->curchan = ath_get_curchannel(sc, sc->hw);
+
        spin_lock_bh(&sc->sc_resetlock);
        r = ath9k_hw_reset(ah, ah->curchan, false);
        if (r) {
@@ -1185,120 +1211,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc)
                                  ah->rfkill_polarity;
 }
 
-/* h/w rfkill poll function */
-static void ath_rfkill_poll(struct work_struct *work)
+/* s/w rfkill handlers */
+static int ath_rfkill_set_block(void *data, bool blocked)
 {
-       struct ath_softc *sc = container_of(work, struct ath_softc,
-                                           rf_kill.rfkill_poll.work);
-       bool radio_on;
-
-       if (sc->sc_flags & SC_OP_INVALID)
-               return;
-
-       radio_on = !ath_is_rfkill_set(sc);
-
-       /*
-        * enable/disable radio only when there is a
-        * state change in RF switch
-        */
-       if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) {
-               enum rfkill_state state;
-
-               if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) {
-                       state = radio_on ? RFKILL_STATE_SOFT_BLOCKED
-                               : RFKILL_STATE_HARD_BLOCKED;
-               } else if (radio_on) {
-                       ath_radio_enable(sc);
-                       state = RFKILL_STATE_UNBLOCKED;
-               } else {
-                       ath_radio_disable(sc);
-                       state = RFKILL_STATE_HARD_BLOCKED;
-               }
-
-               if (state == RFKILL_STATE_HARD_BLOCKED)
-                       sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED;
-               else
-                       sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED;
+       struct ath_softc *sc = data;
 
-               rfkill_force_state(sc->rf_kill.rfkill, state);
-       }
+       if (blocked)
+               ath_radio_disable(sc);
+       else
+               ath_radio_enable(sc);
 
-       queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll,
-                          msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL));
+       return 0;
 }
 
-/* s/w rfkill handler */
-static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
+static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data)
 {
        struct ath_softc *sc = data;
+       bool blocked = !!ath_is_rfkill_set(sc);
 
-       switch (state) {
-       case RFKILL_STATE_SOFT_BLOCKED:
-               if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED |
-                   SC_OP_RFKILL_SW_BLOCKED)))
-                       ath_radio_disable(sc);
-               sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED;
-               return 0;
-       case RFKILL_STATE_UNBLOCKED:
-               if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) {
-                       sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED;
-                       if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) {
-                               DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the"
-                                       "radio as it is disabled by h/w\n");
-                               return -EPERM;
-                       }
-                       ath_radio_enable(sc);
-               }
-               return 0;
-       default:
-               return -EINVAL;
-       }
+       if (rfkill_set_hw_state(rfkill, blocked))
+               ath_radio_disable(sc);
+       else
+               ath_radio_enable(sc);
 }
 
 /* Init s/w rfkill */
 static int ath_init_sw_rfkill(struct ath_softc *sc)
 {
-       sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy),
-                                            RFKILL_TYPE_WLAN);
+       sc->rf_kill.ops.set_block = ath_rfkill_set_block;
+       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+               sc->rf_kill.ops.poll = ath_rfkill_poll_state;
+
+       snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
+               "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy));
+
+       sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name,
+                                         wiphy_dev(sc->hw->wiphy),
+                                         RFKILL_TYPE_WLAN,
+                                         &sc->rf_kill.ops, sc);
        if (!sc->rf_kill.rfkill) {
                DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n");
                return -ENOMEM;
        }
 
-       snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
-               "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy));
-       sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name;
-       sc->rf_kill.rfkill->data = sc;
-       sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio;
-       sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED;
-
        return 0;
 }
 
 /* Deinitialize rfkill */
 static void ath_deinit_rfkill(struct ath_softc *sc)
 {
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
-
        if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) {
                rfkill_unregister(sc->rf_kill.rfkill);
+               rfkill_destroy(sc->rf_kill.rfkill);
                sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED;
-               sc->rf_kill.rfkill = NULL;
        }
 }
 
 static int ath_start_rfkill_poll(struct ath_softc *sc)
 {
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               queue_delayed_work(sc->hw->workqueue,
-                                  &sc->rf_kill.rfkill_poll, 0);
-
        if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
                if (rfkill_register(sc->rf_kill.rfkill)) {
                        DPRINTF(sc, ATH_DBG_FATAL,
                                "Unable to register rfkill\n");
-                       rfkill_free(sc->rf_kill.rfkill);
+                       rfkill_destroy(sc->rf_kill.rfkill);
 
                        /* Deinitialize the device */
                        ath_cleanup(sc);
@@ -1671,10 +1646,6 @@ int ath_attach(u16 devid, struct ath_softc *sc)
                goto error_attach;
 
 #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-       /* Initialze h/w Rfkill */
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll);
-
        /* Initialize s/w rfkill */
        error = ath_init_sw_rfkill(sc);
        if (error)
@@ -1968,7 +1939,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        struct ath_softc *sc = aphy->sc;
        struct ieee80211_channel *curchan = hw->conf.channel;
        struct ath9k_channel *init_channel;
-       int r, pos;
+       int r;
 
        DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with "
                "initial channel: %d MHz\n", curchan->center_freq);
@@ -1998,11 +1969,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
 
        /* setup initial channel */
 
-       pos = curchan->hw_value;
+       sc->chan_idx = curchan->hw_value;
 
-       sc->chan_idx = pos;
-       init_channel = &sc->sc_ah->channels[pos];
-       ath9k_update_ichannel(sc, hw, init_channel);
+       init_channel = ath_get_curchannel(sc, hw);
 
        /* Reset SERDES registers */
        ath9k_hw_configpcipowersave(sc->sc_ah, 0);
@@ -2207,10 +2176,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        } else
                sc->rx.rxlink = NULL;
 
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
-#endif
+       rfkill_pause_polling(sc->rf_kill.rfkill);
+
        /* disable HAL and put h/w to sleep */
        ath9k_hw_disable(sc->sc_ah);
        ath9k_hw_configpcipowersave(sc->sc_ah, 1);