ath10k: fix AP/IBSS CSA with template based fw
authorMichal Kazior <michal.kazior@tieto.com>
Thu, 5 Mar 2015 14:02:17 +0000 (16:02 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Sat, 7 Mar 2015 07:39:16 +0000 (09:39 +0200)
qca6174 with wmi-tlv firmware uses offloaded
beaconing scheme (i.e. templates). This requires a
little different approach when implementing CSA.

Add missing code to update CS count and report CSA
completion to mac80211. Without it channel switch
was never finished.

To avoid races during interface teardown data_lock
has been used to protect is_up and is_started so
they can be compared against before scheduling
count down work.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/wmi-tlv.c

index f65310c3ba5fe8d660cd4139f93407a22045b8aa..9b8f4864e01bf01fa160ca65f078d7cdfc84c5ba 100644 (file)
@@ -341,6 +341,7 @@ struct ath10k_vif {
        int num_legacy_stations;
        int txpower;
        struct wmi_wmm_params_all_arg wmm_params;
+       struct work_struct ap_csa_work;
 };
 
 struct ath10k_vif_iter {
index 8ef64a6d931e68af78c455a8476de2e80f23a31f..8b5e2c10fe6ab0026ecf1caa32fc10aa66082f03 100644 (file)
@@ -1056,6 +1056,10 @@ static int ath10k_mac_setup_bcn_tmpl(struct ath10k_vif *arvif)
        if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
                return 0;
 
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+           arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+               return 0;
+
        bcn = ieee80211_beacon_get_template(hw, vif, &offs);
        if (!bcn) {
                ath10k_warn(ar, "failed to get beacon template from mac80211\n");
@@ -1101,6 +1105,9 @@ static int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif)
        if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
                return 0;
 
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
+               return 0;
+
        prb = ieee80211_proberesp_get(hw, vif);
        if (!prb) {
                ath10k_warn(ar, "failed to get probe resp template from mac80211\n");
@@ -1130,10 +1137,9 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
        if (!info->enable_beacon) {
                ath10k_vdev_stop(arvif);
 
+               spin_lock_bh(&arvif->ar->data_lock);
                arvif->is_started = false;
                arvif->is_up = false;
-
-               spin_lock_bh(&arvif->ar->data_lock);
                ath10k_mac_vif_beacon_free(arvif);
                spin_unlock_bh(&arvif->ar->data_lock);
 
@@ -1361,6 +1367,49 @@ static int ath10k_mac_vif_disable_keepalive(struct ath10k_vif *arvif)
        return 0;
 }
 
+static void ath10k_mac_vif_ap_csa_count_down(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_vif *vif = arvif->vif;
+       int ret;
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
+               return;
+
+       if (!vif->csa_active)
+               return;
+
+       if (!arvif->is_up)
+               return;
+
+       if (!ieee80211_csa_is_complete(vif)) {
+               ieee80211_csa_update_counter(vif);
+
+               ret = ath10k_mac_setup_bcn_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n",
+                                   ret);
+
+               ret = ath10k_mac_setup_prb_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n",
+                                   ret);
+       } else {
+               ieee80211_csa_finish(vif);
+       }
+}
+
+static void ath10k_mac_vif_ap_csa_work(struct work_struct *work)
+{
+       struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
+                                               ap_csa_work);
+       struct ath10k *ar = arvif->ar;
+
+       mutex_lock(&ar->conf_mutex);
+       ath10k_mac_vif_ap_csa_count_down(arvif);
+       mutex_unlock(&ar->conf_mutex);
+}
+
 /**********************/
 /* Station management */
 /**********************/
@@ -1949,7 +1998,9 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                return;
        }
 
+       spin_lock_bh(&arvif->ar->data_lock);
        arvif->is_up = true;
+       spin_unlock_bh(&arvif->ar->data_lock);
 
        /* Workaround: Some firmware revisions (tested with qca6174
         * WLAN.RM.2.0-00073) have buggy powersave state machine and must be
@@ -1991,7 +2042,9 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
                return;
        }
 
+       spin_lock_bh(&arvif->ar->data_lock);
        arvif->is_up = false;
+       spin_unlock_bh(&arvif->ar->data_lock);
 }
 
 static int ath10k_station_assoc(struct ath10k *ar,
@@ -3059,6 +3112,16 @@ static void ath10k_config_chan(struct ath10k *ar)
                if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
                        continue;
 
+               ret = ath10k_mac_setup_bcn_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n",
+                                   ret);
+
+               ret = ath10k_mac_setup_prb_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n",
+                                   ret);
+
                ret = ath10k_vdev_restart(arvif);
                if (ret) {
                        ath10k_warn(ar, "failed to restart vdev %d: %d\n",
@@ -3219,6 +3282,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        arvif->vif = vif;
 
        INIT_LIST_HEAD(&arvif->list);
+       INIT_WORK(&arvif->ap_csa_work, ath10k_mac_vif_ap_csa_work);
 
        if (ar->free_vdev_map == 0) {
                ath10k_warn(ar, "Free vdev map is empty, no more interfaces allowed.\n");
@@ -3436,6 +3500,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret;
 
+       cancel_work_sync(&arvif->ap_csa_work);
+
        mutex_lock(&ar->conf_mutex);
 
        spin_lock_bh(&ar->data_lock);
index ee0c5f602e297424b3f5eb143cdda542b1231291..5721d1fc21c76ce67defbdebd9364fd0a31e91cd 100644 (file)
@@ -17,6 +17,7 @@
 #include "core.h"
 #include "debug.h"
 #include "hw.h"
+#include "mac.h"
 #include "wmi.h"
 #include "wmi-ops.h"
 #include "wmi-tlv.h"
@@ -168,6 +169,7 @@ static int ath10k_wmi_tlv_event_bcn_tx_status(struct ath10k *ar,
 {
        const void **tb;
        const struct wmi_tlv_bcn_tx_status_ev *ev;
+       struct ath10k_vif *arvif;
        u32 vdev_id, tx_status;
        int ret;
 
@@ -201,6 +203,12 @@ static int ath10k_wmi_tlv_event_bcn_tx_status(struct ath10k *ar,
                break;
        }
 
+       spin_lock_bh(&ar->data_lock);
+       arvif = ath10k_get_arvif(ar, vdev_id);
+       if (arvif && arvif->is_up)
+               ieee80211_queue_work(ar->hw, &arvif->ap_csa_work);
+       spin_unlock_bh(&ar->data_lock);
+
        kfree(tb);
        return 0;
 }