ath10k: decouple suspend code
[cascardo/linux.git] / drivers / net / wireless / ath / ath10k / mac.c
index da5c333..febba7d 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 
+#include "hif.h"
 #include "core.h"
 #include "debug.h"
 #include "wmi.h"
@@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                .macaddr = macaddr,
        };
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
                arg.key_flags = WMI_KEY_PAIRWISE;
        else
@@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
        struct ath10k *ar = arvif->ar;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        INIT_COMPLETION(ar->install_key_done);
 
        ret = ath10k_send_key(arvif, key, cmd, macaddr);
@@ -372,6 +377,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
 {
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = wait_for_completion_timeout(&ar->vdev_setup_done,
                                          ATH10K_VDEV_SETUP_TIMEOUT_HZ);
        if (ret == 0)
@@ -605,6 +612,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->enable_beacon) {
                ath10k_vdev_stop(arvif);
                return;
@@ -631,6 +640,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->ibss_joined) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
                if (ret)
@@ -680,6 +691,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        enum wmi_sta_ps_mode psmode;
        int ret;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (vif->type != NL80211_IFTYPE_STATION)
                return;
 
@@ -722,6 +735,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
                                      struct ieee80211_bss_conf *bss_conf,
                                      struct wmi_peer_assoc_complete_arg *arg)
 {
+       lockdep_assert_held(&ar->conf_mutex);
+
        memcpy(arg->addr, sta->addr, ETH_ALEN);
        arg->vdev_id = arvif->vdev_id;
        arg->peer_aid = sta->aid;
@@ -764,6 +779,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
        const u8 *rsnie = NULL;
        const u8 *wpaie = NULL;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
                               info->bssid, NULL, 0, 0, 0);
        if (bss) {
@@ -804,6 +821,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
        u32 ratemask;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
        ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
        rates = sband->bitrates;
@@ -827,6 +846,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
        int smps;
        int i, n;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (!ht_cap->ht_supported)
                return;
 
@@ -905,6 +926,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
        u32 uapsd = 0;
        u32 max_sp = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (sta->wme)
                arg->peer_flags |= WMI_PEER_QOS;
 
@@ -1056,6 +1079,8 @@ static int ath10k_peer_assoc(struct ath10k *ar,
 {
        struct wmi_peer_assoc_complete_arg arg;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
 
        ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
@@ -1079,6 +1104,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        struct ieee80211_sta *ap_sta;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        rcu_read_lock();
 
        ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
@@ -1119,6 +1146,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        /*
         * For some reason, calling VDEV-DOWN before VDEV-STOP
         * makes the FW to send frames via HTT after disassociation.
@@ -1152,6 +1181,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
        if (ret) {
                ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
@@ -1172,6 +1203,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_clear_peer_keys(arvif, sta->addr);
        if (ret) {
                ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
@@ -1198,6 +1231,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        int ret;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bands = hw->wiphy->bands;
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                if (!bands[band])
@@ -1276,21 +1311,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-                               struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct reg_dmn_pair_mapping *regpair;
-       struct ath10k *ar = hw->priv;
        int ret;
 
-       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+       lockdep_assert_held(&ar->conf_mutex);
 
        ret = ath10k_update_channel_list(ar);
        if (ret)
                ath10k_warn("could not update channel list (%d)\n", ret);
 
        regpair = ar->ath_common.regulatory.regpair;
+
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1303,6 +1336,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
                ath10k_warn("could not set pdev regdomain (%d)\n", ret);
 }
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+                               struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct ath10k *ar = hw->priv;
+
+       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+       mutex_lock(&ar->conf_mutex);
+       if (ar->state == ATH10K_STATE_ON)
+               ath10k_regd_update(ar);
+       mutex_unlock(&ar->conf_mutex);
+}
+
 /***************/
 /* TX handlers */
 /***************/
@@ -1397,15 +1444,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
        int ret;
 
        if (ieee80211_is_mgmt(hdr->frame_control))
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else if (ieee80211_is_nullfunc(hdr->frame_control))
                /* FW does not report tx status properly for NullFunc frames
                 * unless they are sent through mgmt tx path. mac80211 sends
                 * those frames when it detects link/beacon loss and depends on
                 * the tx status to be correct. */
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else
-               ret = ath10k_htt_tx(ar->htt, skb);
+               ret = ath10k_htt_tx(&ar->htt, skb);
 
        if (ret) {
                ath10k_warn("tx failed (%d). dropping packet.\n", ret);
@@ -1552,6 +1599,10 @@ static int ath10k_abort_scan(struct ath10k *ar)
        ret = ath10k_wmi_stop_scan(ar, &arg);
        if (ret) {
                ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               ath10k_offchan_tx_purge(ar);
+               spin_unlock_bh(&ar->data_lock);
                return -EIO;
        }
 
@@ -1678,6 +1729,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
        struct ath10k *ar = hw->priv;
        int ret;
 
+       mutex_lock(&ar->conf_mutex);
+
        ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
        if (ret)
                ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
@@ -1688,6 +1741,10 @@ static int ath10k_start(struct ieee80211_hw *hw)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
 
+       ar->state = ATH10K_STATE_ON;
+       ath10k_regd_update(ar);
+
+       mutex_unlock(&ar->conf_mutex);
        return 0;
 }
 
@@ -1695,9 +1752,12 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
 
-       /* avoid leaks in case FW never confirms scan for offchannel */
-       cancel_work_sync(&ar->offchan_tx_work);
+       mutex_lock(&ar->conf_mutex);
        ath10k_offchan_tx_purge(ar);
+       ar->state = ATH10K_STATE_OFF;
+       mutex_unlock(&ar->conf_mutex);
+
+       cancel_work_sync(&ar->offchan_tx_work);
 }
 
 static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
@@ -1756,7 +1816,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        enum wmi_sta_powersave_param param;
        int ret = 0;
-       u32 value;
+       u32 value, rts, frag;
        int bit;
 
        mutex_lock(&ar->conf_mutex);
@@ -1859,6 +1919,24 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
        }
 
+       rts = min_t(u32, ar->hw->wiphy->rts_threshold, ATH10K_RTS_MAX);
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                        WMI_VDEV_PARAM_RTS_THRESHOLD,
+                                        rts);
+       if (ret)
+               ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
+       frag = clamp_t(u32, ar->hw->wiphy->frag_threshold,
+                      ATH10K_FRAGMT_THRESHOLD_MIN,
+                      ATH10K_FRAGMT_THRESHOLD_MAX);
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+                                       frag);
+       if (ret)
+               ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
                ar->monitor_present = true;
 
@@ -2363,6 +2441,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
        u32 value = 0;
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
                return 0;
 
@@ -2558,6 +2638,8 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        rts = min_t(u32, rts, ATH10K_RTS_MAX);
 
        ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
@@ -2581,8 +2663,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_rts_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_rts_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2595,6 +2678,8 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
        int ret;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        frag = clamp_t(u32, frag,
                       ATH10K_FRAGMT_THRESHOLD_MIN,
                       ATH10K_FRAGMT_THRESHOLD_MAX);
@@ -2622,8 +2707,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_frag_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_frag_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2639,16 +2725,20 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
        if (drop)
                return;
 
-       ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+       mutex_lock(&ar->conf_mutex);
+
+       ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
                        bool empty;
-                       spin_lock_bh(&ar->htt->tx_lock);
-                       empty = bitmap_empty(ar->htt->used_msdu_ids,
-                                            ar->htt->max_num_pending_tx);
-                       spin_unlock_bh(&ar->htt->tx_lock);
+                       spin_lock_bh(&ar->htt.tx_lock);
+                       empty = bitmap_empty(ar->htt.used_msdu_ids,
+                                            ar->htt.max_num_pending_tx);
+                       spin_unlock_bh(&ar->htt.tx_lock);
                        (empty);
                }), ATH10K_FLUSH_TIMEOUT_HZ);
        if (ret <= 0)
                ath10k_warn("tx not flushed\n");
+
+       mutex_unlock(&ar->conf_mutex);
 }
 
 /* TODO: Implement this function properly
@@ -2660,6 +2750,67 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
        return 1;
 }
 
+#ifdef CONFIG_PM
+static int ath10k_suspend(struct ieee80211_hw *hw,
+                         struct cfg80211_wowlan *wowlan)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ar->is_target_paused = false;
+
+       ret = ath10k_wmi_pdev_suspend_target(ar);
+       if (ret) {
+               ath10k_warn("could not suspend target (%d)\n", ret);
+               return 1;
+       }
+
+       ret = wait_event_interruptible_timeout(ar->event_queue,
+                                              ar->is_target_paused == true,
+                                              1 * HZ);
+       if (ret < 0) {
+               ath10k_warn("suspend interrupted (%d)\n", ret);
+               goto resume;
+       } else if (ret == 0) {
+               ath10k_warn("suspend timed out - target pause event never came\n");
+               goto resume;
+       }
+
+       ret = ath10k_hif_suspend(ar);
+       if (ret) {
+               ath10k_warn("could not suspend hif (%d)\n", ret);
+               goto resume;
+       }
+
+       return 0;
+resume:
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret)
+               ath10k_warn("could not resume target (%d)\n", ret);
+       return 1;
+}
+
+static int ath10k_resume(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ret = ath10k_hif_resume(ar);
+       if (ret) {
+               ath10k_warn("could not resume hif (%d)\n", ret);
+               return 1;
+       }
+
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret) {
+               ath10k_warn("could not resume target (%d)\n", ret);
+               return 1;
+       }
+
+       return 0;
+}
+#endif
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -2680,6 +2831,10 @@ static const struct ieee80211_ops ath10k_ops = {
        .set_frag_threshold             = ath10k_set_frag_threshold,
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
+#ifdef CONFIG_PM
+       .suspend                        = ath10k_suspend,
+       .resume                         = ath10k_resume,
+#endif
 };
 
 #define RATETAB_ENT(_rate, _rateid, _flags) { \