ath9k: enable CSA functionality in ath9k
authorSimon Wunderlich <simon.wunderlich@s2003.tu-chemnitz.de>
Wed, 14 Aug 2013 06:01:38 +0000 (08:01 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 16 Aug 2013 18:17:50 +0000 (14:17 -0400)
CSA is only enabled for one interface, but the same limitation applies
for mac80211 too. It checks whether the beacon has been sent (different
approaches for non-EDMA-enabled and EDMA-enabled devices), and completes
the channel switch after that.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c

index df1c4957e3f09c92d9919b5a5a0249e8f1835440..8519e75a2e790f4c6616756536ce407b5ae79076 100644 (file)
@@ -420,6 +420,7 @@ void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_set_beacon(struct ath_softc *sc);
+bool ath9k_csa_is_finished(struct ath_softc *sc);
 
 /*******************/
 /* Link Monitoring */
@@ -756,6 +757,7 @@ struct ath_softc {
 #endif
 
        struct ath_descdma txsdma;
+       struct ieee80211_vif *csa_vif;
 
        struct ath_ant_comb ant_comb;
        u8 ant_tx, ant_rx;
index 1a17732bb089ca4b58e7d02880f4817b5f95a093..b5c16b3a37b953133038d0450616d3c5b2be16e3 100644 (file)
@@ -291,6 +291,23 @@ void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
                (unsigned long long)tsfadjust, avp->av_bslot);
 }
 
+bool ath9k_csa_is_finished(struct ath_softc *sc)
+{
+       struct ieee80211_vif *vif;
+
+       vif = sc->csa_vif;
+       if (!vif || !vif->csa_active)
+               return false;
+
+       if (!ieee80211_csa_is_complete(vif))
+               return false;
+
+       ieee80211_csa_finish(vif);
+
+       sc->csa_vif = NULL;
+       return true;
+}
+
 void ath9k_beacon_tasklet(unsigned long data)
 {
        struct ath_softc *sc = (struct ath_softc *)data;
@@ -336,6 +353,10 @@ void ath9k_beacon_tasklet(unsigned long data)
                return;
        }
 
+       /* EDMA devices check that in the tx completion function. */
+       if (!edma && ath9k_csa_is_finished(sc))
+               return;
+
        slot = ath9k_beacon_choose_slot(sc);
        vif = sc->beacon.bslot[slot];
 
index 60bb4d6f1d8219a6c892f8d88af4ac827553d86f..abf1eb5d97ad9103fb06d1f3357ef61c76d6e9ba 100644 (file)
@@ -861,6 +861,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
        hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
        hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
+       hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
 #ifdef CONFIG_PM_SLEEP
        if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
index ba382a8c8b9a3d08604c177b33f4bedc05a5781f..ac9f18fa072964f3c3f6e867b7f4e2bf01102527 100644 (file)
@@ -1032,6 +1032,9 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
 
+       if (sc->csa_vif == vif)
+               sc->csa_vif = NULL;
+
        ath9k_ps_wakeup(sc);
        ath9k_calculate_summary_state(hw, NULL);
        ath9k_ps_restore(sc);
@@ -2318,6 +2321,19 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
        clear_bit(SC_OP_SCANNING, &sc->sc_flags);
 }
 
+static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct cfg80211_chan_def *chandef)
+{
+       struct ath_softc *sc = hw->priv;
+
+       /* mac80211 does not support CSA in multi-if cases (yet) */
+       if (WARN_ON(sc->csa_vif))
+               return;
+
+       sc->csa_vif = vif;
+}
+
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
@@ -2365,4 +2381,5 @@ struct ieee80211_ops ath9k_ops = {
 #endif
        .sw_scan_start      = ath9k_sw_scan_start,
        .sw_scan_complete   = ath9k_sw_scan_complete,
+       .channel_switch_beacon     = ath9k_channel_switch_beacon,
 };
index 7223e303f3a18f4be61fe8cf1af41cfef4f1c7d4..35b515fe3ffa41e00dc614b6590cc2eabead9de4 100644 (file)
@@ -2559,6 +2559,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
                if (ts.qid == sc->beacon.beaconq) {
                        sc->beacon.tx_processed = true;
                        sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
+
+                       ath9k_csa_is_finished(sc);
                        continue;
                }