Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / drivers / net / wireless / ath / ath9k / main.c
index 974de20..6796d5c 100644 (file)
@@ -143,8 +143,10 @@ void ath9k_ps_restore(struct ath_softc *sc)
        if (--sc->ps_usecount != 0)
                goto unlock;
 
-       if (sc->ps_enabled &&
-           !(sc->ps_flags & (PS_WAIT_FOR_BEACON |
+       if (sc->ps_idle)
+               ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
+       else if (sc->ps_enabled &&
+                !(sc->ps_flags & (PS_WAIT_FOR_BEACON |
                              PS_WAIT_FOR_CAB |
                              PS_WAIT_FOR_PSPOLL_DATA |
                              PS_WAIT_FOR_TX_ACK)))
@@ -204,7 +206,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
        r = ath9k_hw_reset(ah, hchan, fastcc);
        if (r) {
                ath_print(common, ATH_DBG_FATAL,
-                         "Unable to reset channel (%u Mhz) "
+                         "Unable to reset channel (%u MHz), "
                          "reset status %d\n",
                          channel->center_freq, r);
                spin_unlock_bh(&sc->sc_resetlock);
@@ -867,7 +869,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
        r = ath9k_hw_reset(ah, ah->curchan, false);
        if (r) {
                ath_print(common, ATH_DBG_FATAL,
-                         "Unable to reset channel %u (%uMhz) ",
+                         "Unable to reset channel (%u MHz), "
                          "reset status %d\n",
                          channel->center_freq, r);
        }
@@ -922,7 +924,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
        r = ath9k_hw_reset(ah, ah->curchan, false);
        if (r) {
                ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
-                         "Unable to reset channel %u (%uMhz) "
+                         "Unable to reset channel (%u MHz), "
                          "reset status %d\n",
                          channel->center_freq, r);
        }
@@ -944,6 +946,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
        /* Stop ANI */
        del_timer_sync(&common->ani.timer);
 
+       ieee80211_stop_queues(hw);
+
        ath9k_hw_set_interrupts(ah, 0);
        ath_drain_all_txq(sc, retry_tx);
        ath_stoprecv(sc);
@@ -985,6 +989,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
                }
        }
 
+       ieee80211_wake_queues(hw);
+
        /* Start ANI */
        ath_start_ani(common);
 
@@ -1466,10 +1472,10 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
            (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
                ath9k_ps_wakeup(sc);
                ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
-               ath_beacon_return(sc, avp);
                ath9k_ps_restore(sc);
        }
 
+       ath_beacon_return(sc, avp);
        sc->sc_flags &= ~SC_OP_BEACONS;
 
        for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
@@ -1524,6 +1530,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                spin_unlock_bh(&sc->wiphy_lock);
 
                if (enable_radio) {
+                       sc->ps_idle = false;
                        ath_radio_enable(sc, hw);
                        ath_print(common, ATH_DBG_CONFIG,
                                  "not-idle: enabling radio\n");
@@ -1577,6 +1584,14 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                }
        }
 
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               if (conf->flags & IEEE80211_CONF_MONITOR) {
+                       ath_print(common, ATH_DBG_CONFIG,
+                                 "HW opmode set to Monitor mode\n");
+                       sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR;
+               }
+       }
+
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                struct ieee80211_channel *curchan = hw->conf.channel;
                int pos = curchan->hw_value;
@@ -1612,8 +1627,10 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        }
 
 skip_chan_change:
-       if (changed & IEEE80211_CONF_CHANGE_POWER)
+       if (changed & IEEE80211_CONF_CHANGE_POWER) {
                sc->config.txpowlimit = 2 * conf->power_level;
+               ath_update_txpow(sc);
+       }
 
        spin_lock_bh(&sc->wiphy_lock);
        disable_radio = ath9k_all_wiphys_idle(sc);
@@ -1621,6 +1638,7 @@ skip_chan_change:
 
        if (disable_radio) {
                ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
+               sc->ps_idle = true;
                ath_radio_disable(sc, hw);
        }
 
@@ -1777,6 +1795,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_vif *avp = (void *)vif->drv_priv;
+       int slottime;
        int error;
 
        mutex_lock(&sc->mutex);
@@ -1812,6 +1831,25 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                        ath_beacon_config(sc, vif);
        }
 
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               if (bss_conf->use_short_slot)
+                       slottime = 9;
+               else
+                       slottime = 20;
+               if (vif->type == NL80211_IFTYPE_AP) {
+                       /*
+                        * Defer update, so that connected stations can adjust
+                        * their settings at the same time.
+                        * See beacon.c for more details
+                        */
+                       sc->beacon.slottime = slottime;
+                       sc->beacon.updateslot = UPDATE;
+               } else {
+                       ah->slottime = slottime;
+                       ath9k_hw_init_global_settings(ah);
+               }
+       }
+
        /* Disable transmission of beacons */
        if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon)
                ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
@@ -1982,6 +2020,18 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
        mutex_unlock(&sc->mutex);
 }
 
+static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
+{
+       struct ath_wiphy *aphy = hw->priv;
+       struct ath_softc *sc = aphy->sc;
+       struct ath_hw *ah = sc->sc_ah;
+
+       mutex_lock(&sc->mutex);
+       ah->coverage_class = coverage_class;
+       ath9k_hw_init_global_settings(ah);
+       mutex_unlock(&sc->mutex);
+}
+
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
@@ -2001,4 +2051,5 @@ struct ieee80211_ops ath9k_ops = {
        .sw_scan_start      = ath9k_sw_scan_start,
        .sw_scan_complete   = ath9k_sw_scan_complete,
        .rfkill_poll        = ath9k_rfkill_poll_state,
+       .set_coverage_class = ath9k_set_coverage_class,
 };