Merge tag 'wireless-drivers-next-for-davem-2015-02-07' of git://git.kernel.org/pub...
[linux-2.6-block.git] / drivers / net / wireless / ath / ath10k / mac.c
index 60a524b732079c6487a68b5494d8ad74c938815f..d6d2f0f00caad18ec00ba69f5635c79f161b2ba7 100644 (file)
@@ -37,7 +37,7 @@
 static int ath10k_send_key(struct ath10k_vif *arvif,
                           struct ieee80211_key_conf *key,
                           enum set_key_cmd cmd,
-                          const u8 *macaddr)
+                          const u8 *macaddr, bool def_idx)
 {
        struct ath10k *ar = arvif->ar;
        struct wmi_vdev_install_key_arg arg = {
@@ -58,10 +58,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
        switch (key->cipher) {
        case WLAN_CIPHER_SUITE_CCMP:
                arg.key_cipher = WMI_CIPHER_AES_CCM;
-               if (arvif->vdev_type == WMI_VDEV_TYPE_AP)
-                       key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
-               else
-                       key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                arg.key_cipher = WMI_CIPHER_TKIP;
@@ -75,6 +72,9 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                 * Otherwise pairwise key must be set */
                if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
                        arg.key_flags = WMI_KEY_PAIRWISE;
+
+               if (def_idx)
+                       arg.key_flags |= WMI_KEY_TX_USAGE;
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
                /* this one needs to be done in software */
@@ -95,7 +95,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
 static int ath10k_install_key(struct ath10k_vif *arvif,
                              struct ieee80211_key_conf *key,
                              enum set_key_cmd cmd,
-                             const u8 *macaddr)
+                             const u8 *macaddr, bool def_idx)
 {
        struct ath10k *ar = arvif->ar;
        int ret;
@@ -104,7 +104,7 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
 
        reinit_completion(&ar->install_key_done);
 
-       ret = ath10k_send_key(arvif, key, cmd, macaddr);
+       ret = ath10k_send_key(arvif, key, cmd, macaddr, def_idx);
        if (ret)
                return ret;
 
@@ -122,6 +122,7 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
        struct ath10k_peer *peer;
        int ret;
        int i;
+       bool def_idx;
 
        lockdep_assert_held(&ar->conf_mutex);
 
@@ -135,9 +136,14 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
        for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
                if (arvif->wep_keys[i] == NULL)
                        continue;
+               /* set TX_USAGE flag for default key id */
+               if (arvif->def_wep_key_idx == i)
+                       def_idx = true;
+               else
+                       def_idx = false;
 
                ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
-                                        addr);
+                                        addr, def_idx);
                if (ret)
                        return ret;
 
@@ -171,8 +177,9 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
                if (peer->keys[i] == NULL)
                        continue;
 
+               /* key flags are not required to delete the key */
                ret = ath10k_install_key(arvif, peer->keys[i],
-                                        DISABLE_KEY, addr);
+                                        DISABLE_KEY, addr, false);
                if (ret && first_errno == 0)
                        first_errno = ret;
 
@@ -246,8 +253,8 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
 
                if (i == ARRAY_SIZE(peer->keys))
                        break;
-
-               ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
+               /* key flags are not required to delete the key */
+               ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, false);
                if (ret && first_errno == 0)
                        first_errno = ret;
 
@@ -527,10 +534,14 @@ void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif)
                dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr,
                                 arvif->beacon->len, DMA_TO_DEVICE);
 
+       if (WARN_ON(arvif->beacon_state != ATH10K_BEACON_SCHEDULED &&
+                   arvif->beacon_state != ATH10K_BEACON_SENT))
+               return;
+
        dev_kfree_skb_any(arvif->beacon);
 
        arvif->beacon = NULL;
-       arvif->beacon_sent = false;
+       arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
 }
 
 static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
@@ -970,6 +981,143 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
        return ret;
 }
 
+static int ath10k_mac_setup_bcn_p2p_ie(struct ath10k_vif *arvif,
+                                      struct sk_buff *bcn)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_mgmt *mgmt;
+       const u8 *p2p_ie;
+       int ret;
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
+               return 0;
+
+       if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+               return 0;
+
+       mgmt = (void *)bcn->data;
+       p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
+                                        mgmt->u.beacon.variable,
+                                        bcn->len - (mgmt->u.beacon.variable -
+                                                    bcn->data));
+       if (!p2p_ie)
+               return -ENOENT;
+
+       ret = ath10k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie);
+       if (ret) {
+               ath10k_warn(ar, "failed to submit p2p go bcn ie for vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath10k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui,
+                                      u8 oui_type, size_t ie_offset)
+{
+       size_t len;
+       const u8 *next;
+       const u8 *end;
+       u8 *ie;
+
+       if (WARN_ON(skb->len < ie_offset))
+               return -EINVAL;
+
+       ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
+                                          skb->data + ie_offset,
+                                          skb->len - ie_offset);
+       if (!ie)
+               return -ENOENT;
+
+       len = ie[1] + 2;
+       end = skb->data + skb->len;
+       next = ie + len;
+
+       if (WARN_ON(next > end))
+               return -EINVAL;
+
+       memmove(ie, next, end - next);
+       skb_trim(skb, skb->len - len);
+
+       return 0;
+}
+
+static int ath10k_mac_setup_bcn_tmpl(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_hw *hw = ar->hw;
+       struct ieee80211_vif *vif = arvif->vif;
+       struct ieee80211_mutable_offsets offs = {};
+       struct sk_buff *bcn;
+       int ret;
+
+       if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
+               return 0;
+
+       bcn = ieee80211_beacon_get_template(hw, vif, &offs);
+       if (!bcn) {
+               ath10k_warn(ar, "failed to get beacon template from mac80211\n");
+               return -EPERM;
+       }
+
+       ret = ath10k_mac_setup_bcn_p2p_ie(arvif, bcn);
+       if (ret) {
+               ath10k_warn(ar, "failed to setup p2p go bcn ie: %d\n", ret);
+               kfree_skb(bcn);
+               return ret;
+       }
+
+       /* P2P IE is inserted by firmware automatically (as configured above)
+        * so remove it from the base beacon template to avoid duplicate P2P
+        * IEs in beacon frames.
+        */
+       ath10k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
+                                   offsetof(struct ieee80211_mgmt,
+                                            u.beacon.variable));
+
+       ret = ath10k_wmi_bcn_tmpl(ar, arvif->vdev_id, offs.tim_offset, bcn, 0,
+                                 0, NULL, 0);
+       kfree_skb(bcn);
+
+       if (ret) {
+               ath10k_warn(ar, "failed to submit beacon template command: %d\n",
+                           ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_hw *hw = ar->hw;
+       struct ieee80211_vif *vif = arvif->vif;
+       struct sk_buff *prb;
+       int ret;
+
+       if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
+               return 0;
+
+       prb = ieee80211_proberesp_get(hw, vif);
+       if (!prb) {
+               ath10k_warn(ar, "failed to get probe resp template from mac80211\n");
+               return -EPERM;
+       }
+
+       ret = ath10k_wmi_prb_tmpl(ar, arvif->vdev_id, prb);
+       kfree_skb(prb);
+
+       if (ret) {
+               ath10k_warn(ar, "failed to submit probe resp template command: %d\n",
+                           ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static void ath10k_control_beaconing(struct ath10k_vif *arvif,
                                     struct ieee80211_bss_conf *info)
 {
@@ -1155,6 +1303,38 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
        return 0;
 }
 
+static int ath10k_mac_vif_disable_keepalive(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct wmi_sta_keepalive_arg arg = {};
+       int ret;
+
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+               return 0;
+
+       if (!test_bit(WMI_SERVICE_STA_KEEP_ALIVE, ar->wmi.svc_map))
+               return 0;
+
+       /* Some firmware revisions have a bug and ignore the `enabled` field.
+        * Instead use the interval to disable the keepalive.
+        */
+       arg.vdev_id = arvif->vdev_id;
+       arg.enabled = 1;
+       arg.method = WMI_STA_KEEPALIVE_METHOD_NULL_FRAME;
+       arg.interval = WMI_STA_KEEPALIVE_INTERVAL_DISABLE;
+
+       ret = ath10k_wmi_sta_keepalive(ar, &arg);
+       if (ret) {
+               ath10k_warn(ar, "failed to submit keepalive on vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /**********************/
 /* Station management */
 /**********************/
@@ -1423,6 +1603,10 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
                return;
 
        arg->peer_flags |= WMI_PEER_VHT;
+
+       if (ar->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)
+               arg->peer_flags |= WMI_PEER_VHT_2G;
+
        arg->peer_vht_caps = vht_cap->cap;
 
        ampdu_factor = (vht_cap->cap &
@@ -1501,7 +1685,12 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
 
        switch (ar->hw->conf.chandef.chan->band) {
        case IEEE80211_BAND_2GHZ:
-               if (sta->ht_cap.ht_supported) {
+               if (sta->vht_cap.vht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11AC_VHT40;
+                       else
+                               phymode = MODE_11AC_VHT20;
+               } else if (sta->ht_cap.ht_supported) {
                        if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
                                phymode = MODE_11NG_HT40;
                        else
@@ -1683,7 +1872,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
                ath10k_warn(ar, "faield to down vdev %i: %d\n",
                            arvif->vdev_id, ret);
 
-       arvif->def_wep_key_idx = 0;
+       arvif->def_wep_key_idx = -1;
+
        arvif->is_up = false;
 }
 
@@ -1742,11 +1932,14 @@ static int ath10k_station_assoc(struct ath10k *ar,
                        }
                }
 
-               ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
-               if (ret) {
-                       ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
-                                   arvif->vdev_id, ret);
-                       return ret;
+               /* Plumb cached keys only for static WEP */
+               if (arvif->def_wep_key_idx != -1) {
+                       ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+                       if (ret) {
+                               ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
+                                           arvif->vdev_id, ret);
+                               return ret;
+                       }
                }
        }
 
@@ -2011,75 +2204,13 @@ static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb)
         * used only for CQM purposes (e.g. hostapd station keepalive ping) so
         * it is safe to downgrade to NullFunc.
         */
+       hdr = (void *)skb->data;
        if (ieee80211_is_qos_nullfunc(hdr->frame_control)) {
                hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
                cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
        }
 }
 
-static void ath10k_tx_wep_key_work(struct work_struct *work)
-{
-       struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
-                                               wep_key_work);
-       struct ath10k *ar = arvif->ar;
-       int ret, keyidx = arvif->def_wep_key_newidx;
-
-       mutex_lock(&arvif->ar->conf_mutex);
-
-       if (arvif->ar->state != ATH10K_STATE_ON)
-               goto unlock;
-
-       if (arvif->def_wep_key_idx == keyidx)
-               goto unlock;
-
-       ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
-                  arvif->vdev_id, keyidx);
-
-       ret = ath10k_wmi_vdev_set_param(arvif->ar,
-                                       arvif->vdev_id,
-                                       arvif->ar->wmi.vdev_param->def_keyid,
-                                       keyidx);
-       if (ret) {
-               ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n",
-                           arvif->vdev_id,
-                           ret);
-               goto unlock;
-       }
-
-       arvif->def_wep_key_idx = keyidx;
-
-unlock:
-       mutex_unlock(&arvif->ar->conf_mutex);
-}
-
-static void ath10k_tx_h_update_wep_key(struct ieee80211_vif *vif,
-                                      struct ieee80211_key_conf *key,
-                                      struct sk_buff *skb)
-{
-       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-       struct ath10k *ar = arvif->ar;
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-       if (!ieee80211_has_protected(hdr->frame_control))
-               return;
-
-       if (!key)
-               return;
-
-       if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
-           key->cipher != WLAN_CIPHER_SUITE_WEP104)
-               return;
-
-       if (key->keyidx == arvif->def_wep_key_idx)
-               return;
-
-       /* FIXME: Most likely a few frames will be TXed with an old key. Simply
-        * queueing frames until key index is updated is not an option because
-        * sk_buff may need more processing to be done, e.g. offchannel */
-       arvif->def_wep_key_newidx = key->keyidx;
-       ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
-}
-
 static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
                                       struct ieee80211_vif *vif,
                                       struct sk_buff *skb)
@@ -2231,7 +2362,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
 
                ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
                                                  3 * HZ);
-               if (ret <= 0)
+               if (ret == 0)
                        ath10k_warn(ar, "timed out waiting for offchannel skb %p\n",
                                    skb);
 
@@ -2293,6 +2424,7 @@ void __ath10k_scan_finish(struct ath10k *ar)
        case ATH10K_SCAN_RUNNING:
                if (ar->scan.is_roc)
                        ieee80211_remain_on_channel_expired(ar->hw);
+               /* fall through */
        case ATH10K_SCAN_ABORTING:
                if (!ar->scan.is_roc)
                        ieee80211_scan_completed(ar->hw,
@@ -2439,7 +2571,6 @@ static void ath10k_tx(struct ieee80211_hw *hw,
        struct ath10k *ar = hw->priv;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_vif *vif = info->control.vif;
-       struct ieee80211_key_conf *key = info->control.hw_key;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
        /* We should disable CCK RATE due to P2P */
@@ -2453,7 +2584,6 @@ static void ath10k_tx(struct ieee80211_hw *hw,
        /* it makes no sense to process injected frames like that */
        if (vif && vif->type != NL80211_IFTYPE_MONITOR) {
                ath10k_tx_h_nwifi(hw, skb);
-               ath10k_tx_h_update_wep_key(vif, key, skb);
                ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
                ath10k_tx_h_seq_no(vif, skb);
        }
@@ -2960,7 +3090,6 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        arvif->ar = ar;
        arvif->vif = vif;
 
-       INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
        INIT_LIST_HEAD(&arvif->list);
 
        if (ar->free_vdev_map == 0) {
@@ -3049,15 +3178,18 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
        list_add(&arvif->list, &ar->arvifs);
 
-       vdev_param = ar->wmi.vdev_param->def_keyid;
-       ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
-                                       arvif->def_wep_key_idx);
+       /* It makes no sense to have firmware do keepalives. mac80211 already
+        * takes care of this with idle connection polling.
+        */
+       ret = ath10k_mac_vif_disable_keepalive(arvif);
        if (ret) {
-               ath10k_warn(ar, "failed to set vdev %i default key id: %d\n",
+               ath10k_warn(ar, "failed to disable keepalive on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                goto err_vdev_delete;
        }
 
+       arvif->def_wep_key_idx = -1;
+
        vdev_param = ar->wmi.vdev_param->tx_encap_type;
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                        ATH10K_HW_TXRX_NATIVE_WIFI);
@@ -3176,8 +3308,6 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret;
 
-       cancel_work_sync(&arvif->wep_key_work);
-
        mutex_lock(&ar->conf_mutex);
 
        spin_lock_bh(&ar->data_lock);
@@ -3288,9 +3418,21 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                if (ret)
                        ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
+
+               ret = ath10k_mac_setup_bcn_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to update beacon template: %d\n",
+                                   ret);
+       }
+
+       if (changed & BSS_CHANGED_AP_PROBE_RESP) {
+               ret = ath10k_mac_setup_prb_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to setup probe resp template on vdev %i: %d\n",
+                                   arvif->vdev_id, ret);
        }
 
-       if (changed & BSS_CHANGED_BEACON_INFO) {
+       if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
                arvif->dtim_period = info->dtim_period;
 
                ath10k_dbg(ar, ATH10K_DBG_MAC,
@@ -3537,6 +3679,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        const u8 *peer_addr;
        bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
                      key->cipher == WLAN_CIPHER_SUITE_WEP104;
+       bool def_idx = false;
        int ret = 0;
 
        if (key->keyidx > WMI_MAX_KEY_INDEX)
@@ -3582,7 +3725,14 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                        ath10k_clear_vdev_key(arvif, key);
        }
 
-       ret = ath10k_install_key(arvif, key, cmd, peer_addr);
+       /* set TX_USAGE flag for all the keys incase of dot1x-WEP. For
+        * static WEP, do not set this flag for the keys whose key id
+        * is  greater than default key id.
+        */
+       if (arvif->def_wep_key_idx == -1)
+               def_idx = true;
+
+       ret = ath10k_install_key(arvif, key, cmd, peer_addr, def_idx);
        if (ret) {
                ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n",
                            arvif->vdev_id, peer_addr, ret);
@@ -3607,6 +3757,39 @@ exit:
        return ret;
 }
 
+static void ath10k_set_default_unicast_key(struct ieee80211_hw *hw,
+                                          struct ieee80211_vif *vif,
+                                          int keyidx)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+
+       mutex_lock(&arvif->ar->conf_mutex);
+
+       if (arvif->ar->state != ATH10K_STATE_ON)
+               goto unlock;
+
+       ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+                  arvif->vdev_id, keyidx);
+
+       ret = ath10k_wmi_vdev_set_param(arvif->ar,
+                                       arvif->vdev_id,
+                                       arvif->ar->wmi.vdev_param->def_keyid,
+                                       keyidx);
+
+       if (ret) {
+               ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n",
+                           arvif->vdev_id,
+                           ret);
+               goto unlock;
+       }
+
+       arvif->def_wep_key_idx = keyidx;
+unlock:
+       mutex_unlock(&arvif->ar->conf_mutex);
+}
+
 static void ath10k_sta_rc_update_wk(struct work_struct *wk)
 {
        struct ath10k *ar;
@@ -3842,6 +4025,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
                                u16 ac, bool enable)
 {
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_sta_uapsd_auto_trig_arg arg = {};
+       u32 prio = 0, acc = 0;
        u32 value = 0;
        int ret = 0;
 
@@ -3854,18 +4039,26 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
        case IEEE80211_AC_VO:
                value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
                        WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
+               prio = 7;
+               acc = 3;
                break;
        case IEEE80211_AC_VI:
                value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
                        WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
+               prio = 5;
+               acc = 2;
                break;
        case IEEE80211_AC_BE:
                value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
                        WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
+               prio = 2;
+               acc = 1;
                break;
        case IEEE80211_AC_BK:
                value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
                        WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
+               prio = 0;
+               acc = 0;
                break;
        }
 
@@ -3907,6 +4100,29 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
                return ret;
        }
 
+       if (test_bit(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, ar->wmi.svc_map) ||
+           test_bit(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, ar->wmi.svc_map)) {
+               /* Only userspace can make an educated decision when to send
+                * trigger frame. The following effectively disables u-UAPSD
+                * autotrigger in firmware (which is enabled by default
+                * provided the autotrigger service is available).
+                */
+
+               arg.wmm_ac = acc;
+               arg.user_priority = prio;
+               arg.service_interval = 0;
+               arg.suspend_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
+               arg.delay_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
+
+               ret = ath10k_wmi_vdev_sta_uapsd(ar, arvif->vdev_id,
+                                               arvif->bssid, &arg, 1);
+               if (ret) {
+                       ath10k_warn(ar, "failed to set uapsd auto trigger %d\n",
+                                   ret);
+                       return ret;
+               }
+       }
+
 exit:
        return ret;
 }
@@ -3916,6 +4132,7 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
                          const struct ieee80211_tx_queue_params *params)
 {
        struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        struct wmi_wmm_params_arg *p = NULL;
        int ret;
 
@@ -3923,16 +4140,16 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
 
        switch (ac) {
        case IEEE80211_AC_VO:
-               p = &ar->wmm_params.ac_vo;
+               p = &arvif->wmm_params.ac_vo;
                break;
        case IEEE80211_AC_VI:
-               p = &ar->wmm_params.ac_vi;
+               p = &arvif->wmm_params.ac_vi;
                break;
        case IEEE80211_AC_BE:
-               p = &ar->wmm_params.ac_be;
+               p = &arvif->wmm_params.ac_be;
                break;
        case IEEE80211_AC_BK:
-               p = &ar->wmm_params.ac_bk;
+               p = &arvif->wmm_params.ac_bk;
                break;
        }
 
@@ -3952,11 +4169,23 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
         */
        p->txop = params->txop * 32;
 
-       /* FIXME: FW accepts wmm params per hw, not per vif */
-       ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
-       if (ret) {
-               ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
-               goto exit;
+       if (ar->wmi.ops->gen_vdev_wmm_conf) {
+               ret = ath10k_wmi_vdev_wmm_conf(ar, arvif->vdev_id,
+                                              &arvif->wmm_params);
+               if (ret) {
+                       ath10k_warn(ar, "failed to set vdev wmm params on vdev %i: %d\n",
+                                   arvif->vdev_id, ret);
+                       goto exit;
+               }
+       } else {
+               /* This won't work well with multi-interface cases but it's
+                * better than nothing.
+                */
+               ret = ath10k_wmi_pdev_set_wmm_params(ar, &arvif->wmm_params);
+               if (ret) {
+                       ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
+                       goto exit;
+               }
        }
 
        ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
@@ -4726,6 +4955,7 @@ static const struct ieee80211_ops ath10k_ops = {
        .hw_scan                        = ath10k_hw_scan,
        .cancel_hw_scan                 = ath10k_cancel_hw_scan,
        .set_key                        = ath10k_set_key,
+       .set_default_unicast_key        = ath10k_set_default_unicast_key,
        .sta_state                      = ath10k_sta_state,
        .conf_tx                        = ath10k_conf_tx,
        .remain_on_channel              = ath10k_remain_on_channel,
@@ -4751,6 +4981,9 @@ static const struct ieee80211_ops ath10k_ops = {
        .suspend                        = ath10k_suspend,
        .resume                         = ath10k_resume,
 #endif
+#ifdef CONFIG_MAC80211_DEBUGFS
+       .sta_add_debugfs                = ath10k_sta_add_debugfs,
+#endif
 };
 
 #define RATETAB_ENT(_rate, _rateid, _flags) { \
@@ -5074,7 +5307,8 @@ int ath10k_mac_register(struct ath10k *ar)
                band->bitrates = ath10k_g_rates;
                band->ht_cap = ht_cap;
 
-               /* vht is not supported in 2.4 GHz */
+               /* Enable the VHT support at 2.4 GHz */
+               band->vht_cap = vht_cap;
 
                ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
        }
@@ -5139,6 +5373,19 @@ int ath10k_mac_register(struct ath10k *ar)
 
        ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
 
+       if (test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) {
+               ar->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+
+               /* Firmware delivers WPS/P2P Probe Requests frames to driver so
+                * that userspace (e.g. wpa_supplicant/hostapd) can generate
+                * correct Probe Responses. This is more of a hack advert..
+                */
+               ar->hw->wiphy->probe_resp_offload |=
+                       NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
+                       NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+                       NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
+       }
+
        ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
        ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        ar->hw->wiphy->max_remain_on_channel_duration = 5000;