OSDN Git Service

Merge ath-next from ath.git
authorKalle Valo <kvalo@codeaurora.org>
Thu, 5 Mar 2015 09:01:38 +0000 (11:01 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 5 Mar 2015 09:01:48 +0000 (11:01 +0200)
Major changes in ath10k:

* qca6174: enable STA transmit beamforming (TxBF) support
* disable multi-vif power save by default

12 files changed:
drivers/net/wireless/ath/ath10k/ce.h
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/wmi-ops.h
drivers/net/wireless/ath/ath10k/wmi-tlv.c
drivers/net/wireless/ath/ath10k/wmi-tlv.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h

index c18647b..0eddb20 100644 (file)
@@ -39,7 +39,7 @@ struct ath10k_ce_pipe;
 #define CE_DESC_FLAGS_GATHER         (1 << 0)
 #define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
 #define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
-#define CE_DESC_FLAGS_META_DATA_LSB  3
+#define CE_DESC_FLAGS_META_DATA_LSB  2
 
 struct ce_desc {
        __le32 addr;
index 310e12b..c0e454b 100644 (file)
@@ -436,16 +436,16 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
 
 static void ath10k_core_free_firmware_files(struct ath10k *ar)
 {
-       if (ar->board && !IS_ERR(ar->board))
+       if (!IS_ERR(ar->board))
                release_firmware(ar->board);
 
-       if (ar->otp && !IS_ERR(ar->otp))
+       if (!IS_ERR(ar->otp))
                release_firmware(ar->otp);
 
-       if (ar->firmware && !IS_ERR(ar->firmware))
+       if (!IS_ERR(ar->firmware))
                release_firmware(ar->firmware);
 
-       if (ar->cal_file && !IS_ERR(ar->cal_file))
+       if (!IS_ERR(ar->cal_file))
                release_firmware(ar->cal_file);
 
        ar->board = NULL;
index d60e46f..f65310c 100644 (file)
@@ -159,6 +159,25 @@ struct ath10k_fw_stats_peer {
        u32 peer_rx_rate; /* 10x only */
 };
 
+struct ath10k_fw_stats_vdev {
+       struct list_head list;
+
+       u32 vdev_id;
+       u32 beacon_snr;
+       u32 data_snr;
+       u32 num_tx_frames[4];
+       u32 num_rx_frames;
+       u32 num_tx_frames_retries[4];
+       u32 num_tx_frames_failures[4];
+       u32 num_rts_fail;
+       u32 num_rts_success;
+       u32 num_rx_err;
+       u32 num_rx_discard;
+       u32 num_tx_not_acked;
+       u32 tx_rate_history[10];
+       u32 beacon_rssi_history[10];
+};
+
 struct ath10k_fw_stats_pdev {
        struct list_head list;
 
@@ -220,6 +239,7 @@ struct ath10k_fw_stats_pdev {
 
 struct ath10k_fw_stats {
        struct list_head pdevs;
+       struct list_head vdevs;
        struct list_head peers;
 };
 
@@ -288,6 +308,7 @@ struct ath10k_vif {
        bool is_started;
        bool is_up;
        bool spectral_enabled;
+       bool ps;
        u32 aid;
        u8 bssid[ETH_ALEN];
 
@@ -413,6 +434,12 @@ enum ath10k_fw_features {
         */
        ATH10K_FW_FEATURE_WMI_10_2 = 4,
 
+       /* Some firmware revisions lack proper multi-interface client powersave
+        * implementation. Enabling PS could result in connection drops,
+        * traffic stalls, etc.
+        */
+       ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT = 5,
+
        /* keep last */
        ATH10K_FW_FEATURE_COUNT,
 };
index d2281e5..301081d 100644 (file)
@@ -243,6 +243,16 @@ static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head)
        }
 }
 
+static void ath10k_debug_fw_stats_vdevs_free(struct list_head *head)
+{
+       struct ath10k_fw_stats_vdev *i, *tmp;
+
+       list_for_each_entry_safe(i, tmp, head, list) {
+               list_del(&i->list);
+               kfree(i);
+       }
+}
+
 static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
 {
        struct ath10k_fw_stats_peer *i, *tmp;
@@ -258,6 +268,7 @@ static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
        spin_lock_bh(&ar->data_lock);
        ar->debug.fw_stats_done = false;
        ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
+       ath10k_debug_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
        ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
        spin_unlock_bh(&ar->data_lock);
 }
@@ -273,14 +284,27 @@ static size_t ath10k_debug_fw_stats_num_peers(struct list_head *head)
        return num;
 }
 
+static size_t ath10k_debug_fw_stats_num_vdevs(struct list_head *head)
+{
+       struct ath10k_fw_stats_vdev *i;
+       size_t num = 0;
+
+       list_for_each_entry(i, head, list)
+               ++num;
+
+       return num;
+}
+
 void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ath10k_fw_stats stats = {};
        bool is_start, is_started, is_end;
        size_t num_peers;
+       size_t num_vdevs;
        int ret;
 
        INIT_LIST_HEAD(&stats.pdevs);
+       INIT_LIST_HEAD(&stats.vdevs);
        INIT_LIST_HEAD(&stats.peers);
 
        spin_lock_bh(&ar->data_lock);
@@ -308,6 +332,7 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
        }
 
        num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
+       num_vdevs = ath10k_debug_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs);
        is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
                    !list_empty(&stats.pdevs));
        is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
@@ -330,7 +355,13 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
                        goto free;
                }
 
+               if (num_vdevs >= BITS_PER_LONG) {
+                       ath10k_warn(ar, "dropping fw vdev stats\n");
+                       goto free;
+               }
+
                list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
+               list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);
        }
 
        complete(&ar->debug.fw_stats_complete);
@@ -340,6 +371,7 @@ free:
         * resources if that is not the case.
         */
        ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
+       ath10k_debug_fw_stats_vdevs_free(&stats.vdevs);
        ath10k_debug_fw_stats_peers_free(&stats.peers);
 
 unlock:
@@ -363,7 +395,10 @@ static int ath10k_debug_fw_stats_request(struct ath10k *ar)
 
                reinit_completion(&ar->debug.fw_stats_complete);
 
-               ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+               ret = ath10k_wmi_request_stats(ar,
+                                              WMI_STAT_PDEV |
+                                              WMI_STAT_VDEV |
+                                              WMI_STAT_PEER);
                if (ret) {
                        ath10k_warn(ar, "could not request stats (%d)\n", ret);
                        return ret;
@@ -395,8 +430,11 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
        unsigned int len = 0;
        unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
        const struct ath10k_fw_stats_pdev *pdev;
+       const struct ath10k_fw_stats_vdev *vdev;
        const struct ath10k_fw_stats_peer *peer;
        size_t num_peers;
+       size_t num_vdevs;
+       int i;
 
        spin_lock_bh(&ar->data_lock);
 
@@ -408,6 +446,7 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
        }
 
        num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
+       num_vdevs = ath10k_debug_fw_stats_num_vdevs(&fw_stats->vdevs);
 
        len += scnprintf(buf + len, buf_len - len, "\n");
        len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -531,6 +570,65 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
 
        len += scnprintf(buf + len, buf_len - len, "\n");
        len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                        "ath10k VDEV stats", num_vdevs);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       list_for_each_entry(vdev, &fw_stats->vdevs, list) {
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "vdev id", vdev->vdev_id);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "beacon snr", vdev->beacon_snr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "data snr", vdev->data_snr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rx frames", vdev->num_rx_frames);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rts fail", vdev->num_rts_fail);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rts success", vdev->num_rts_success);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rx err", vdev->num_rx_err);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rx discard", vdev->num_rx_discard);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num tx not acked", vdev->num_tx_not_acked);
+
+               for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                        "num tx frames", i,
+                                        vdev->num_tx_frames[i]);
+
+               for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                        "num tx frames retries", i,
+                                        vdev->num_tx_frames_retries[i]);
+
+               for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                        "num tx frames failures", i,
+                                        vdev->num_tx_frames_failures[i]);
+
+               for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] 0x%08x\n",
+                                        "tx rate history", i,
+                                        vdev->tx_rate_history[i]);
+
+               for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                        "beacon rssi history", i,
+                                        vdev->beacon_rssi_history[i]);
+
+               len += scnprintf(buf + len, buf_len - len, "\n");
+       }
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
                         "ath10k PEER stats", num_peers);
        len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
                                 "=================");
@@ -1900,6 +1998,7 @@ int ath10k_debug_create(struct ath10k *ar)
                return -ENOMEM;
 
        INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
+       INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
        INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
 
        return 0;
index c1da44f..01a2b38 100644 (file)
@@ -176,7 +176,7 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
         * automatically balances load wrt to CPU power.
         *
         * This probably comes at a cost of lower maximum throughput but
-        * improves the avarage and stability. */
+        * improves the average and stability. */
        spin_lock_bh(&htt->rx_ring.lock);
        num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
        num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
index d6d2f0f..8ef64a6 100644 (file)
@@ -611,7 +611,7 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret) {
-               ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i: %d\n",
+               ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i start: %d\n",
                            vdev_id, ret);
                return ret;
        }
@@ -658,7 +658,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar)
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret)
-               ath10k_warn(ar, "failed to synchronise monitor vdev %i: %d\n",
+               ath10k_warn(ar, "failed to synchronize monitor vdev %i stop: %d\n",
                            ar->monitor_vdev_id, ret);
 
        ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
@@ -927,8 +927,9 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, bool restart)
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret) {
-               ath10k_warn(ar, "failed to synchronise setup for vdev %i: %d\n",
-                           arg.vdev_id, ret);
+               ath10k_warn(ar,
+                           "failed to synchronize setup for vdev %i restart %d: %d\n",
+                           arg.vdev_id, restart, ret);
                return ret;
        }
 
@@ -966,7 +967,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret) {
-               ath10k_warn(ar, "failed to syncronise setup for vdev %i: %d\n",
+               ath10k_warn(ar, "failed to synchronize setup for vdev %i stop: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -1253,6 +1254,20 @@ static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif)
        return 0;
 }
 
+static int ath10k_mac_ps_vif_count(struct ath10k *ar)
+{
+       struct ath10k_vif *arvif;
+       int num = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       list_for_each_entry(arvif, &ar->arvifs, list)
+               if (arvif->ps)
+                       num++;
+
+       return num;
+}
+
 static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 {
        struct ath10k *ar = arvif->ar;
@@ -1262,13 +1277,24 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
        enum wmi_sta_ps_mode psmode;
        int ret;
        int ps_timeout;
+       bool enable_ps;
 
        lockdep_assert_held(&arvif->ar->conf_mutex);
 
        if (arvif->vif->type != NL80211_IFTYPE_STATION)
                return 0;
 
-       if (vif->bss_conf.ps) {
+       enable_ps = arvif->ps;
+
+       if (enable_ps && ath10k_mac_ps_vif_count(ar) > 1 &&
+           !test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT,
+                     ar->fw_features)) {
+               ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n",
+                           arvif->vdev_id);
+               enable_ps = false;
+       }
+
+       if (enable_ps) {
                psmode = WMI_STA_PS_MODE_ENABLED;
                param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
 
@@ -1781,6 +1807,68 @@ static int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif,
                                         ath10k_smps_map[smps]);
 }
 
+static int ath10k_mac_vif_recalc_txbf(struct ath10k *ar,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_sta_vht_cap vht_cap)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+       u32 param;
+       u32 value;
+
+       if (!(ar->vht_cap_info &
+             (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+              IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+              IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+              IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
+               return 0;
+
+       param = ar->wmi.vdev_param->txbf;
+       value = 0;
+
+       if (WARN_ON(param == WMI_VDEV_PARAM_UNSUPPORTED))
+               return 0;
+
+       /* The following logic is correct. If a remote STA advertises support
+        * for being a beamformer then we should enable us being a beamformee.
+        */
+
+       if (ar->vht_cap_info &
+           (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+            IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
+               if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
+                       value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
+
+               if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
+                       value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE;
+       }
+
+       if (ar->vht_cap_info &
+           (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+            IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
+               if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
+                       value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
+
+               if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
+                       value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER;
+       }
+
+       if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFEE)
+               value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
+
+       if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFER)
+               value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
+
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, value);
+       if (ret) {
+               ath10k_warn(ar, "failed to submit vdev param txbf 0x%x: %d\n",
+                           value, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /* can be called only in mac80211 callbacks due to `key_count` usage */
 static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
@@ -1789,6 +1877,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        struct ieee80211_sta_ht_cap ht_cap;
+       struct ieee80211_sta_vht_cap vht_cap;
        struct wmi_peer_assoc_complete_arg peer_arg;
        struct ieee80211_sta *ap_sta;
        int ret;
@@ -1811,6 +1900,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        /* ap_sta must be accessed only within rcu section which must be left
         * before calling ath10k_setup_peer_smps() which might sleep. */
        ht_cap = ap_sta->ht_cap;
+       vht_cap = ap_sta->vht_cap;
 
        ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
        if (ret) {
@@ -1836,6 +1926,13 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                return;
        }
 
+       ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
+       if (ret) {
+               ath10k_warn(ar, "failed to recalc txbf for vdev %i on bss %pM: %d\n",
+                           arvif->vdev_id, bss_conf->bssid, ret);
+               return;
+       }
+
        ath10k_dbg(ar, ATH10K_DBG_MAC,
                   "mac vdev %d up (associated) bssid %pM aid %d\n",
                   arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
@@ -1853,6 +1950,18 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        }
 
        arvif->is_up = true;
+
+       /* Workaround: Some firmware revisions (tested with qca6174
+        * WLAN.RM.2.0-00073) have buggy powersave state machine and must be
+        * poked with peer param command.
+        */
+       ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, arvif->bssid,
+                                       WMI_PEER_DUMMY_VAR, 1);
+       if (ret) {
+               ath10k_warn(ar, "failed to poke peer %pM param for ps workaround on vdev %i: %d\n",
+                           arvif->bssid, arvif->vdev_id, ret);
+               return;
+       }
 }
 
 static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
@@ -1860,6 +1969,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ieee80211_sta_vht_cap vht_cap = {};
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
@@ -1874,6 +1984,13 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
 
        arvif->def_wep_key_idx = -1;
 
+       ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
+       if (ret) {
+               ath10k_warn(ar, "failed to recalc txbf for vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return;
+       }
+
        arvif->is_up = false;
 }
 
@@ -2554,6 +2671,17 @@ static int ath10k_start_scan(struct ath10k *ar,
                return -ETIMEDOUT;
        }
 
+       /* If we failed to start the scan, return error code at
+        * this point.  This is probably due to some issue in the
+        * firmware, but no need to wedge the driver due to that...
+        */
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.state == ATH10K_SCAN_IDLE) {
+               spin_unlock_bh(&ar->data_lock);
+               return -EINVAL;
+       }
+       spin_unlock_bh(&ar->data_lock);
+
        /* Add a 200ms margin to account for event/command processing */
        ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
                                     msecs_to_jiffies(arg->max_scan_time+200));
@@ -3323,9 +3451,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        list_del(&arvif->list);
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
-               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
+               ret = ath10k_wmi_peer_delete(arvif->ar, arvif->vdev_id,
+                                            vif->addr);
                if (ret)
-                       ath10k_warn(ar, "failed to remove peer for AP vdev %i: %d\n",
+                       ath10k_warn(ar, "failed to submit AP self-peer removal on vdev %i: %d\n",
                                    arvif->vdev_id, ret);
 
                kfree(arvif->u.ap.noa_data);
@@ -3339,6 +3468,21 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
                ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
                            arvif->vdev_id, ret);
 
+       /* Some firmware revisions don't notify host about self-peer removal
+        * until after associated vdev is deleted.
+        */
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath10k_wait_for_peer_deleted(ar, arvif->vdev_id,
+                                                  vif->addr);
+               if (ret)
+                       ath10k_warn(ar, "failed to remove AP self-peer on vdev %i: %d\n",
+                                   arvif->vdev_id, ret);
+
+               spin_lock_bh(&ar->data_lock);
+               ar->num_peers--;
+               spin_unlock_bh(&ar->data_lock);
+       }
+
        ath10k_peer_cleanup(ar, arvif->vdev_id);
 
        mutex_unlock(&ar->conf_mutex);
@@ -3534,7 +3678,9 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        if (changed & BSS_CHANGED_PS) {
-               ret = ath10k_mac_vif_setup_ps(arvif);
+               arvif->ps = vif->bss_conf.ps;
+
+               ret = ath10k_config_ps(ar);
                if (ret)
                        ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n",
                                    arvif->vdev_id, ret);
index e6972b0..7681237 100644 (file)
@@ -104,7 +104,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
        {
                .flags = CE_ATTR_FLAGS,
                .src_nentries = 0,
-               .src_sz_max = 512,
+               .src_sz_max = 2048,
                .dest_nentries = 512,
        },
 
@@ -174,7 +174,7 @@ static const struct ce_pipe_config target_ce_config_wlan[] = {
                .pipenum = __cpu_to_le32(1),
                .pipedir = __cpu_to_le32(PIPEDIR_IN),
                .nentries = __cpu_to_le32(32),
-               .nbytes_max = __cpu_to_le32(512),
+               .nbytes_max = __cpu_to_le32(2048),
                .flags = __cpu_to_le32(CE_ATTR_FLAGS),
                .reserved = __cpu_to_le32(0),
        },
index 04dc4b9..c8b64e7 100644 (file)
@@ -110,8 +110,7 @@ struct wmi_ops {
                                          bool deliver_cab);
        struct sk_buff *(*gen_pdev_set_wmm)(struct ath10k *ar,
                                            const struct wmi_wmm_params_all_arg *arg);
-       struct sk_buff *(*gen_request_stats)(struct ath10k *ar,
-                                            enum wmi_stats_id stats_id);
+       struct sk_buff *(*gen_request_stats)(struct ath10k *ar, u32 stats_mask);
        struct sk_buff *(*gen_force_fw_hang)(struct ath10k *ar,
                                             enum wmi_force_fw_hang_type type,
                                             u32 delay_ms);
@@ -816,14 +815,14 @@ ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
 }
 
 static inline int
-ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
+ath10k_wmi_request_stats(struct ath10k *ar, u32 stats_mask)
 {
        struct sk_buff *skb;
 
        if (!ar->wmi.ops->gen_request_stats)
                return -EOPNOTSUPP;
 
-       skb = ar->wmi.ops->gen_request_stats(ar, stats_id);
+       skb = ar->wmi.ops->gen_request_stats(ar, stats_mask);
        if (IS_ERR(skb))
                return PTR_ERR(skb);
 
index 71614ba..ee0c5f6 100644 (file)
@@ -869,16 +869,57 @@ static int ath10k_wmi_tlv_op_pull_rdy_ev(struct ath10k *ar,
        return 0;
 }
 
+static void ath10k_wmi_tlv_pull_vdev_stats(const struct wmi_tlv_vdev_stats *src,
+                                          struct ath10k_fw_stats_vdev *dst)
+{
+       int i;
+
+       dst->vdev_id = __le32_to_cpu(src->vdev_id);
+       dst->beacon_snr = __le32_to_cpu(src->beacon_snr);
+       dst->data_snr = __le32_to_cpu(src->data_snr);
+       dst->num_rx_frames = __le32_to_cpu(src->num_rx_frames);
+       dst->num_rts_fail = __le32_to_cpu(src->num_rts_fail);
+       dst->num_rts_success = __le32_to_cpu(src->num_rts_success);
+       dst->num_rx_err = __le32_to_cpu(src->num_rx_err);
+       dst->num_rx_discard = __le32_to_cpu(src->num_rx_discard);
+       dst->num_tx_not_acked = __le32_to_cpu(src->num_tx_not_acked);
+
+       for (i = 0; i < ARRAY_SIZE(src->num_tx_frames); i++)
+               dst->num_tx_frames[i] =
+                       __le32_to_cpu(src->num_tx_frames[i]);
+
+       for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_retries); i++)
+               dst->num_tx_frames_retries[i] =
+                       __le32_to_cpu(src->num_tx_frames_retries[i]);
+
+       for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_failures); i++)
+               dst->num_tx_frames_failures[i] =
+                       __le32_to_cpu(src->num_tx_frames_failures[i]);
+
+       for (i = 0; i < ARRAY_SIZE(src->tx_rate_history); i++)
+               dst->tx_rate_history[i] =
+                       __le32_to_cpu(src->tx_rate_history[i]);
+
+       for (i = 0; i < ARRAY_SIZE(src->beacon_rssi_history); i++)
+               dst->beacon_rssi_history[i] =
+                       __le32_to_cpu(src->beacon_rssi_history[i]);
+}
+
 static int ath10k_wmi_tlv_op_pull_fw_stats(struct ath10k *ar,
                                           struct sk_buff *skb,
                                           struct ath10k_fw_stats *stats)
 {
        const void **tb;
-       const struct wmi_stats_event *ev;
+       const struct wmi_tlv_stats_ev *ev;
        const void *data;
-       u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
+       u32 num_pdev_stats;
+       u32 num_vdev_stats;
+       u32 num_peer_stats;
+       u32 num_bcnflt_stats;
+       u32 num_chan_stats;
        size_t data_len;
        int ret;
+       int i;
 
        tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
        if (IS_ERR(tb)) {
@@ -899,8 +940,73 @@ static int ath10k_wmi_tlv_op_pull_fw_stats(struct ath10k *ar,
        num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
        num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
        num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+       num_bcnflt_stats = __le32_to_cpu(ev->num_bcnflt_stats);
+       num_chan_stats = __le32_to_cpu(ev->num_chan_stats);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "wmi tlv stats update pdev %i vdev %i peer %i bcnflt %i chan %i\n",
+                  num_pdev_stats, num_vdev_stats, num_peer_stats,
+                  num_bcnflt_stats, num_chan_stats);
+
+       for (i = 0; i < num_pdev_stats; i++) {
+               const struct wmi_pdev_stats *src;
+               struct ath10k_fw_stats_pdev *dst;
+
+               src = data;
+               if (data_len < sizeof(*src))
+                       return -EPROTO;
+
+               data += sizeof(*src);
+               data_len -= sizeof(*src);
+
+               dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+               if (!dst)
+                       continue;
+
+               ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
+               ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
+               ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+               list_add_tail(&dst->list, &stats->pdevs);
+       }
+
+       for (i = 0; i < num_vdev_stats; i++) {
+               const struct wmi_tlv_vdev_stats *src;
+               struct ath10k_fw_stats_vdev *dst;
+
+               src = data;
+               if (data_len < sizeof(*src))
+                       return -EPROTO;
+
+               data += sizeof(*src);
+               data_len -= sizeof(*src);
+
+               dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+               if (!dst)
+                       continue;
 
-       WARN_ON(1); /* FIXME: not implemented yet */
+               ath10k_wmi_tlv_pull_vdev_stats(src, dst);
+               list_add_tail(&dst->list, &stats->vdevs);
+       }
+
+       for (i = 0; i < num_peer_stats; i++) {
+               const struct wmi_10x_peer_stats *src;
+               struct ath10k_fw_stats_peer *dst;
+
+               src = data;
+               if (data_len < sizeof(*src))
+                       return -EPROTO;
+
+               data += sizeof(*src);
+               data_len -= sizeof(*src);
+
+               dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+               if (!dst)
+                       continue;
+
+               ath10k_wmi_pull_peer_stats(&src->old, dst);
+               dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+               list_add_tail(&dst->list, &stats->peers);
+       }
 
        kfree(tb);
        return 0;
@@ -1604,14 +1710,12 @@ ath10k_wmi_tlv_op_gen_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
                                    const struct wmi_wmm_params_all_arg *arg)
 {
        struct wmi_tlv_vdev_set_wmm_cmd *cmd;
-       struct wmi_wmm_params *wmm;
        struct wmi_tlv *tlv;
        struct sk_buff *skb;
        size_t len;
        void *ptr;
 
-       len = (sizeof(*tlv) + sizeof(*cmd)) +
-             (4 * (sizeof(*tlv) + sizeof(*wmm)));
+       len = sizeof(*tlv) + sizeof(*cmd);
        skb = ath10k_wmi_alloc_skb(ar, len);
        if (!skb)
                return ERR_PTR(-ENOMEM);
@@ -1623,13 +1727,10 @@ ath10k_wmi_tlv_op_gen_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
        cmd = (void *)tlv->value;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
 
-       ptr += sizeof(*tlv);
-       ptr += sizeof(*cmd);
-
-       ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_be);
-       ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_bk);
-       ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vi);
-       ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vo);
+       ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[0].params, &arg->ac_be);
+       ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[1].params, &arg->ac_bk);
+       ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[2].params, &arg->ac_vi);
+       ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[3].params, &arg->ac_vo);
 
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev wmm conf\n");
        return skb;
@@ -2080,8 +2181,7 @@ ath10k_wmi_tlv_op_gen_pdev_set_wmm(struct ath10k *ar,
 }
 
 static struct sk_buff *
-ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar,
-                                   enum wmi_stats_id stats_id)
+ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
 {
        struct wmi_request_stats_cmd *cmd;
        struct wmi_tlv *tlv;
@@ -2095,7 +2195,7 @@ ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar,
        tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD);
        tlv->len = __cpu_to_le16(sizeof(*cmd));
        cmd = (void *)tlv->value;
-       cmd->stats_id = __cpu_to_le32(stats_id);
+       cmd->stats_id = __cpu_to_le32(stats_mask);
 
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv request stats\n");
        return skb;
index de68fe7..a6c8280 100644 (file)
@@ -1302,8 +1302,14 @@ struct wmi_tlv_pdev_set_wmm_cmd {
        __le32 dg_type; /* no idea.. */
 } __packed;
 
+struct wmi_tlv_vdev_wmm_params {
+       __le32 dummy;
+       struct wmi_wmm_params params;
+} __packed;
+
 struct wmi_tlv_vdev_set_wmm_cmd {
        __le32 vdev_id;
+       struct wmi_tlv_vdev_wmm_params vdev_wmm_params[4];
 } __packed;
 
 struct wmi_tlv_phyerr_ev {
@@ -1439,6 +1445,15 @@ struct wmi_tlv_sta_keepalive_cmd {
        __le32 interval; /* in seconds */
 } __packed;
 
+struct wmi_tlv_stats_ev {
+       __le32 stats_id; /* WMI_STAT_ */
+       __le32 num_pdev_stats;
+       __le32 num_vdev_stats;
+       __le32 num_peer_stats;
+       __le32 num_bcnflt_stats;
+       __le32 num_chan_stats;
+} __packed;
+
 void ath10k_wmi_tlv_attach(struct ath10k *ar);
 
 #endif
index aeea1c7..c7ea77e 100644 (file)
@@ -1125,6 +1125,25 @@ static void ath10k_wmi_event_scan_started(struct ath10k *ar)
        }
 }
 
+static void ath10k_wmi_event_scan_start_failed(struct ath10k *ar)
+{
+       lockdep_assert_held(&ar->data_lock);
+
+       switch (ar->scan.state) {
+       case ATH10K_SCAN_IDLE:
+       case ATH10K_SCAN_RUNNING:
+       case ATH10K_SCAN_ABORTING:
+               ath10k_warn(ar, "received scan start failed event in an invalid scan state: %s (%d)\n",
+                           ath10k_scan_state_str(ar->scan.state),
+                           ar->scan.state);
+               break;
+       case ATH10K_SCAN_STARTING:
+               complete(&ar->scan.started);
+               __ath10k_scan_finish(ar);
+               break;
+       }
+}
+
 static void ath10k_wmi_event_scan_completed(struct ath10k *ar)
 {
        lockdep_assert_held(&ar->data_lock);
@@ -1292,6 +1311,7 @@ int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
                break;
        case WMI_SCAN_EVENT_START_FAILED:
                ath10k_warn(ar, "received scan start failure event\n");
+               ath10k_wmi_event_scan_start_failed(ar);
                break;
        case WMI_SCAN_EVENT_DEQUEUED:
        case WMI_SCAN_EVENT_PREEMPTED:
@@ -4954,7 +4974,7 @@ ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar,
 }
 
 static struct sk_buff *
-ath10k_wmi_op_gen_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
+ath10k_wmi_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
 {
        struct wmi_request_stats_cmd *cmd;
        struct sk_buff *skb;
@@ -4964,9 +4984,10 @@ ath10k_wmi_op_gen_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
                return ERR_PTR(-ENOMEM);
 
        cmd = (struct wmi_request_stats_cmd *)skb->data;
-       cmd->stats_id = __cpu_to_le32(stats_id);
+       cmd->stats_id = __cpu_to_le32(stats_mask);
 
-       ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
+       ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats 0x%08x\n",
+                  stats_mask);
        return skb;
 }
 
index 20ce360..adf935b 100644 (file)
@@ -3057,8 +3057,12 @@ struct wmi_pdev_stats_peer {
 } __packed;
 
 enum wmi_stats_id {
-       WMI_REQUEST_PEER_STAT   = 0x01,
-       WMI_REQUEST_AP_STAT     = 0x02
+       WMI_STAT_PEER = BIT(0),
+       WMI_STAT_AP = BIT(1),
+       WMI_STAT_PDEV = BIT(2),
+       WMI_STAT_VDEV = BIT(3),
+       WMI_STAT_BCNFLT = BIT(4),
+       WMI_STAT_VDEV_RATE = BIT(5),
 };
 
 struct wlan_inst_rssi_args {
@@ -3093,7 +3097,7 @@ struct wmi_pdev_suspend_cmd {
 } __packed;
 
 struct wmi_stats_event {
-       __le32 stats_id; /* %WMI_REQUEST_ */
+       __le32 stats_id; /* WMI_STAT_ */
        /*
         * number of pdev stats event structures
         * (wmi_pdev_stats) 0 or 1
@@ -3745,6 +3749,11 @@ enum wmi_10x_vdev_param {
        WMI_10X_VDEV_PARAM_VHT80_RATEMASK,
 };
 
+#define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0)
+#define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1)
+#define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2)
+#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
+
 /* slot time long */
 #define WMI_VDEV_SLOT_TIME_LONG                0x1
 /* slot time short */
@@ -4436,7 +4445,8 @@ enum wmi_peer_param {
        WMI_PEER_AUTHORIZE  = 0x3,
        WMI_PEER_CHAN_WIDTH = 0x4,
        WMI_PEER_NSS        = 0x5,
-       WMI_PEER_USE_4ADDR  = 0x6
+       WMI_PEER_USE_4ADDR  = 0x6,
+       WMI_PEER_DUMMY_VAR  = 0xff, /* dummy parameter for STA PS workaround */
 };
 
 struct wmi_peer_set_param_cmd {