Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/ath/ath10k/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 265 kB image not shown  

Quelle  mac.c   Sprache: C

 
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */


#include "mac.h"

#include <linux/export.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/bitfield.h>
#include <linux/random.h>

#include "hif.h"
#include "core.h"
#include "debug.h"
#include "wmi.h"
#include "htt.h"
#include "txrx.h"
#include "testmode.h"
#include "wmi-tlv.h"
#include "wmi-ops.h"
#include "wow.h"
#include "leds.h"

/*********/
/* Rates */
/*********/

static struct ieee80211_rate ath10k_rates[] = {
 { .bitrate = 10,
   .hw_value = ATH10K_HW_RATE_CCK_LP_1M },
 { .bitrate = 20,
   .hw_value = ATH10K_HW_RATE_CCK_LP_2M,
   .hw_value_short = ATH10K_HW_RATE_CCK_SP_2M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 55,
   .hw_value = ATH10K_HW_RATE_CCK_LP_5_5M,
   .hw_value_short = ATH10K_HW_RATE_CCK_SP_5_5M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 110,
   .hw_value = ATH10K_HW_RATE_CCK_LP_11M,
   .hw_value_short = ATH10K_HW_RATE_CCK_SP_11M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },

 { .bitrate = 60, .hw_value = ATH10K_HW_RATE_OFDM_6M },
 { .bitrate = 90, .hw_value = ATH10K_HW_RATE_OFDM_9M },
 { .bitrate = 120, .hw_value = ATH10K_HW_RATE_OFDM_12M },
 { .bitrate = 180, .hw_value = ATH10K_HW_RATE_OFDM_18M },
 { .bitrate = 240, .hw_value = ATH10K_HW_RATE_OFDM_24M },
 { .bitrate = 360, .hw_value = ATH10K_HW_RATE_OFDM_36M },
 { .bitrate = 480, .hw_value = ATH10K_HW_RATE_OFDM_48M },
 { .bitrate = 540, .hw_value = ATH10K_HW_RATE_OFDM_54M },
};

static struct ieee80211_rate ath10k_rates_rev2[] = {
 { .bitrate = 10,
   .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_1M },
 { .bitrate = 20,
   .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_2M,
   .hw_value_short = ATH10K_HW_RATE_REV2_CCK_SP_2M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 55,
   .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_5_5M,
   .hw_value_short = ATH10K_HW_RATE_REV2_CCK_SP_5_5M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 110,
   .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_11M,
   .hw_value_short = ATH10K_HW_RATE_REV2_CCK_SP_11M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },

 { .bitrate = 60, .hw_value = ATH10K_HW_RATE_OFDM_6M },
 { .bitrate = 90, .hw_value = ATH10K_HW_RATE_OFDM_9M },
 { .bitrate = 120, .hw_value = ATH10K_HW_RATE_OFDM_12M },
 { .bitrate = 180, .hw_value = ATH10K_HW_RATE_OFDM_18M },
 { .bitrate = 240, .hw_value = ATH10K_HW_RATE_OFDM_24M },
 { .bitrate = 360, .hw_value = ATH10K_HW_RATE_OFDM_36M },
 { .bitrate = 480, .hw_value = ATH10K_HW_RATE_OFDM_48M },
 { .bitrate = 540, .hw_value = ATH10K_HW_RATE_OFDM_54M },
};

static const struct cfg80211_sar_freq_ranges ath10k_sar_freq_ranges[] = {
 {.start_freq = 2402, .end_freq = 2494 },
 {.start_freq = 5170, .end_freq = 5875 },
};

static const struct cfg80211_sar_capa ath10k_sar_capa = {
 .type = NL80211_SAR_TYPE_POWER,
 .num_freq_ranges = (ARRAY_SIZE(ath10k_sar_freq_ranges)),
 .freq_ranges = &ath10k_sar_freq_ranges[0],
};

#define ATH10K_MAC_FIRST_OFDM_RATE_IDX 4

#define ath10k_a_rates (ath10k_rates + ATH10K_MAC_FIRST_OFDM_RATE_IDX)
#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - \
        ATH10K_MAC_FIRST_OFDM_RATE_IDX)
#define ath10k_g_rates (ath10k_rates + 0)
#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates))

#define ath10k_g_rates_rev2 (ath10k_rates_rev2 + 0)
#define ath10k_g_rates_rev2_size (ARRAY_SIZE(ath10k_rates_rev2))

#define ath10k_wmi_legacy_rates ath10k_rates

static bool ath10k_mac_bitrate_is_cck(int bitrate)
{
 switch (bitrate) {
 case 10:
 case 20:
 case 55:
 case 110:
  return true;
 }

 return false;
}

static u8 ath10k_mac_bitrate_to_rate(int bitrate)
{
 return DIV_ROUND_UP(bitrate, 5) |
        (ath10k_mac_bitrate_is_cck(bitrate) ? BIT(7) : 0);
}

u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
        u8 hw_rate, bool cck)
{
 const struct ieee80211_rate *rate;
 int i;

 for (i = 0; i < sband->n_bitrates; i++) {
  rate = &sband->bitrates[i];

  if (ath10k_mac_bitrate_is_cck(rate->bitrate) != cck)
   continue;

  if (rate->hw_value == hw_rate)
   return i;
  else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE &&
    rate->hw_value_short == hw_rate)
   return i;
 }

 return 0;
}

u8 ath10k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband,
        u32 bitrate)
{
 int i;

 for (i = 0; i < sband->n_bitrates; i++)
  if (sband->bitrates[i].bitrate == bitrate)
   return i;

 return 0;
}

static int ath10k_mac_get_rate_hw_value(int bitrate)
{
 int i;
 u8 hw_value_prefix = 0;

 if (ath10k_mac_bitrate_is_cck(bitrate))
  hw_value_prefix = WMI_RATE_PREAMBLE_CCK << 6;

 for (i = 0; i < ARRAY_SIZE(ath10k_rates); i++) {
  if (ath10k_rates[i].bitrate == bitrate)
   return hw_value_prefix | ath10k_rates[i].hw_value;
 }

 return -EINVAL;
}

static int ath10k_mac_get_max_vht_mcs_map(u16 mcs_map, int nss)
{
 switch ((mcs_map >> (2 * nss)) & 0x3) {
 case IEEE80211_VHT_MCS_SUPPORT_0_7: return BIT(8) - 1;
 case IEEE80211_VHT_MCS_SUPPORT_0_8: return BIT(9) - 1;
 case IEEE80211_VHT_MCS_SUPPORT_0_9: return BIT(10) - 1;
 }
 return 0;
}

static u32
ath10k_mac_max_ht_nss(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
{
 int nss;

 for (nss = IEEE80211_HT_MCS_MASK_LEN - 1; nss >= 0; nss--)
  if (ht_mcs_mask[nss])
   return nss + 1;

 return 1;
}

static u32
ath10k_mac_max_vht_nss(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
{
 int nss;

 for (nss = NL80211_VHT_NSS_MAX - 1; nss >= 0; nss--)
  if (vht_mcs_mask[nss])
   return nss + 1;

 return 1;
}

int ath10k_mac_ext_resource_config(struct ath10k *ar, u32 val)
{
 enum wmi_host_platform_type platform_type;
 int ret;

 if (test_bit(WMI_SERVICE_TX_MODE_DYNAMIC, ar->wmi.svc_map))
  platform_type = WMI_HOST_PLATFORM_LOW_PERF;
 else
  platform_type = WMI_HOST_PLATFORM_HIGH_PERF;

 ret = ath10k_wmi_ext_resource_config(ar, platform_type, val);

 if (ret && ret != -EOPNOTSUPP) {
  ath10k_warn(ar, "failed to configure ext resource: %d\n", ret);
  return ret;
 }

 return 0;
}

/**********/
/* Crypto */
/**********/

static int ath10k_send_key(struct ath10k_vif *arvif,
      struct ieee80211_key_conf *key,
      enum set_key_cmd cmd,
      const u8 *macaddr, u32 flags)
{
 struct ath10k *ar = arvif->ar;
 struct wmi_vdev_install_key_arg arg = {
  .vdev_id = arvif->vdev_id,
  .key_idx = key->keyidx,
  .key_len = key->keylen,
  .key_data = key->key,
  .key_flags = flags,
  .macaddr = macaddr,
 };

 lockdep_assert_held(&arvif->ar->conf_mutex);

 switch (key->cipher) {
 case WLAN_CIPHER_SUITE_CCMP:
  arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_CCM];
  key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
  break;
 case WLAN_CIPHER_SUITE_TKIP:
  arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_TKIP];
  arg.key_txmic_len = 8;
  arg.key_rxmic_len = 8;
  break;
 case WLAN_CIPHER_SUITE_WEP40:
 case WLAN_CIPHER_SUITE_WEP104:
  arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_WEP];
  break;
 case WLAN_CIPHER_SUITE_CCMP_256:
  arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_CCM];
  break;
 case WLAN_CIPHER_SUITE_GCMP:
 case WLAN_CIPHER_SUITE_GCMP_256:
  arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_GCM];
  key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
  break;
 case WLAN_CIPHER_SUITE_BIP_GMAC_128:
 case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 case WLAN_CIPHER_SUITE_AES_CMAC:
  WARN_ON(1);
  return -EINVAL;
 default:
  ath10k_warn(ar, "cipher %d is not supported\n", key->cipher);
  return -EOPNOTSUPP;
 }

 if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
  key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;

 if (cmd == DISABLE_KEY) {
  if (flags & WMI_KEY_GROUP) {
   /* Not all hardware handles group-key deletion operation
 * correctly. Replace the key with a junk value to invalidate it.
 */

   get_random_bytes(key->key, key->keylen);
  } else {
   arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_NONE];
   arg.key_data = NULL;
  }
 }

 return ath10k_wmi_vdev_install_key(arvif->ar, &arg);
}

static int ath10k_install_key(struct ath10k_vif *arvif,
         struct ieee80211_key_conf *key,
         enum set_key_cmd cmd,
         const u8 *macaddr, u32 flags)
{
 struct ath10k *ar = arvif->ar;
 int ret;
 unsigned long time_left;

 lockdep_assert_held(&ar->conf_mutex);

 reinit_completion(&ar->install_key_done);

 if (arvif->nohwcrypt)
  return 1;

 ret = ath10k_send_key(arvif, key, cmd, macaddr, flags);
 if (ret)
  return ret;

 time_left = wait_for_completion_timeout(&ar->install_key_done, 3 * HZ);
 if (time_left == 0)
  return -ETIMEDOUT;

 return 0;
}

static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
     const u8 *addr)
{
 struct ath10k *ar = arvif->ar;
 struct ath10k_peer *peer;
 int ret;
 int i;
 u32 flags;

 lockdep_assert_held(&ar->conf_mutex);

 if (WARN_ON(arvif->vif->type != NL80211_IFTYPE_AP &&
      arvif->vif->type != NL80211_IFTYPE_ADHOC &&
      arvif->vif->type != NL80211_IFTYPE_MESH_POINT))
  return -EINVAL;

 spin_lock_bh(&ar->data_lock);
 peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
 spin_unlock_bh(&ar->data_lock);

 if (!peer)
  return -ENOENT;

 for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
  if (arvif->wep_keys[i] == NULL)
   continue;

  switch (arvif->vif->type) {
  case NL80211_IFTYPE_AP:
   flags = WMI_KEY_PAIRWISE;

   if (arvif->def_wep_key_idx == i)
    flags |= WMI_KEY_TX_USAGE;

   ret = ath10k_install_key(arvif, arvif->wep_keys[i],
       SET_KEY, addr, flags);
   if (ret < 0)
    return ret;
   break;
  case NL80211_IFTYPE_ADHOC:
   ret = ath10k_install_key(arvif, arvif->wep_keys[i],
       SET_KEY, addr,
       WMI_KEY_PAIRWISE);
   if (ret < 0)
    return ret;

   ret = ath10k_install_key(arvif, arvif->wep_keys[i],
       SET_KEY, addr, WMI_KEY_GROUP);
   if (ret < 0)
    return ret;
   break;
  default:
   WARN_ON(1);
   return -EINVAL;
  }

  spin_lock_bh(&ar->data_lock);
  peer->keys[i] = arvif->wep_keys[i];
  spin_unlock_bh(&ar->data_lock);
 }

 /* In some cases (notably with static WEP IBSS with multiple keys)
 * multicast Tx becomes broken. Both pairwise and groupwise keys are
 * installed already. Using WMI_KEY_TX_USAGE in different combinations
 * didn't seem help. Using def_keyid vdev parameter seems to be
 * effective so use that.
 *
 * FIXME: Revisit. Perhaps this can be done in a less hacky way.
 */

 if (arvif->vif->type != NL80211_IFTYPE_ADHOC)
  return 0;

 if (arvif->def_wep_key_idx == -1)
  return 0;

 ret = ath10k_wmi_vdev_set_param(arvif->ar,
     arvif->vdev_id,
     arvif->ar->wmi.vdev_param->def_keyid,
     arvif->def_wep_key_idx);
 if (ret) {
  ath10k_warn(ar, "failed to re-set def wpa key idxon vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
      const u8 *addr)
{
 struct ath10k *ar = arvif->ar;
 struct ath10k_peer *peer;
 int first_errno = 0;
 int ret;
 int i;
 u32 flags = 0;

 lockdep_assert_held(&ar->conf_mutex);

 spin_lock_bh(&ar->data_lock);
 peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
 spin_unlock_bh(&ar->data_lock);

 if (!peer)
  return -ENOENT;

 for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
  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, flags);
  if (ret < 0 && first_errno == 0)
   first_errno = ret;

  if (ret < 0)
   ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
        i, ret);

  spin_lock_bh(&ar->data_lock);
  peer->keys[i] = NULL;
  spin_unlock_bh(&ar->data_lock);
 }

 return first_errno;
}

bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
        u8 keyidx)
{
 struct ath10k_peer *peer;
 int i;

 lockdep_assert_held(&ar->data_lock);

 /* We don't know which vdev this peer belongs to,
 * since WMI doesn't give us that information.
 *
 * FIXME: multi-bss needs to be handled.
 */

 peer = ath10k_peer_find(ar, 0, addr);
 if (!peer)
  return false;

 for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
  if (peer->keys[i] && peer->keys[i]->keyidx == keyidx)
   return true;
 }

 return false;
}

static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
     struct ieee80211_key_conf *key)
{
 struct ath10k *ar = arvif->ar;
 struct ath10k_peer *peer;
 u8 addr[ETH_ALEN];
 int first_errno = 0;
 int ret;
 int i;
 u32 flags = 0;

 lockdep_assert_held(&ar->conf_mutex);

 for (;;) {
  /* since ath10k_install_key we can't hold data_lock all the
 * time, so we try to remove the keys incrementally
 */

  spin_lock_bh(&ar->data_lock);
  i = 0;
  list_for_each_entry(peer, &ar->peers, list) {
   for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
    if (peer->keys[i] == key) {
     ether_addr_copy(addr, peer->addr);
     peer->keys[i] = NULL;
     break;
    }
   }

   if (i < ARRAY_SIZE(peer->keys))
    break;
  }
  spin_unlock_bh(&ar->data_lock);

  if (i == ARRAY_SIZE(peer->keys))
   break;
  /* key flags are not required to delete the key */
  ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, flags);
  if (ret < 0 && first_errno == 0)
   first_errno = ret;

  if (ret)
   ath10k_warn(ar, "failed to remove key for %pM: %d\n",
        addr, ret);
 }

 return first_errno;
}

static int ath10k_mac_vif_update_wep_key(struct ath10k_vif *arvif,
      struct ieee80211_key_conf *key)
{
 struct ath10k *ar = arvif->ar;
 struct ath10k_peer *peer;
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 list_for_each_entry(peer, &ar->peers, list) {
  if (ether_addr_equal(peer->addr, arvif->vif->addr))
   continue;

  if (ether_addr_equal(peer->addr, arvif->bssid))
   continue;

  if (peer->keys[key->keyidx] == key)
   continue;

  ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vif vdev %i update key %i needs update\n",
      arvif->vdev_id, key->keyidx);

  ret = ath10k_install_peer_wep_keys(arvif, peer->addr);
  if (ret) {
   ath10k_warn(ar, "failed to update wep keys on vdev %i for peer %pM: %d\n",
        arvif->vdev_id, peer->addr, ret);
   return ret;
  }
 }

 return 0;
}

/*********************/
/* General utilities */
/*********************/

static inline enum wmi_phy_mode
chan_to_phymode(const struct cfg80211_chan_def *chandef)
{
 enum wmi_phy_mode phymode = MODE_UNKNOWN;

 switch (chandef->chan->band) {
 case NL80211_BAND_2GHZ:
  switch (chandef->width) {
  case NL80211_CHAN_WIDTH_20_NOHT:
   if (chandef->chan->flags & IEEE80211_CHAN_NO_OFDM)
    phymode = MODE_11B;
   else
    phymode = MODE_11G;
   break;
  case NL80211_CHAN_WIDTH_20:
   phymode = MODE_11NG_HT20;
   break;
  case NL80211_CHAN_WIDTH_40:
   phymode = MODE_11NG_HT40;
   break;
  default:
   phymode = MODE_UNKNOWN;
   break;
  }
  break;
 case NL80211_BAND_5GHZ:
  switch (chandef->width) {
  case NL80211_CHAN_WIDTH_20_NOHT:
   phymode = MODE_11A;
   break;
  case NL80211_CHAN_WIDTH_20:
   phymode = MODE_11NA_HT20;
   break;
  case NL80211_CHAN_WIDTH_40:
   phymode = MODE_11NA_HT40;
   break;
  case NL80211_CHAN_WIDTH_80:
   phymode = MODE_11AC_VHT80;
   break;
  case NL80211_CHAN_WIDTH_160:
   phymode = MODE_11AC_VHT160;
   break;
  case NL80211_CHAN_WIDTH_80P80:
   phymode = MODE_11AC_VHT80_80;
   break;
  default:
   phymode = MODE_UNKNOWN;
   break;
  }
  break;
 default:
  break;
 }

 WARN_ON(phymode == MODE_UNKNOWN);
 return phymode;
}

static u8 ath10k_parse_mpdudensity(u8 mpdudensity)
{
/*
 * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
 *   0 for no restriction
 *   1 for 1/4 us
 *   2 for 1/2 us
 *   3 for 1 us
 *   4 for 2 us
 *   5 for 4 us
 *   6 for 8 us
 *   7 for 16 us
 */

 switch (mpdudensity) {
 case 0:
  return 0;
 case 1:
 case 2:
 case 3:
 /* Our lower layer calculations limit our precision to
 * 1 microsecond
 */

  return 1;
 case 4:
  return 2;
 case 5:
  return 4;
 case 6:
  return 8;
 case 7:
  return 16;
 default:
  return 0;
 }
}

int ath10k_mac_vif_chan(struct ieee80211_vif *vif,
   struct cfg80211_chan_def *def)
{
 struct ieee80211_chanctx_conf *conf;

 rcu_read_lock();
 conf = rcu_dereference(vif->bss_conf.chanctx_conf);
 if (!conf) {
  rcu_read_unlock();
  return -ENOENT;
 }

 *def = conf->def;
 rcu_read_unlock();

 return 0;
}

static void ath10k_mac_num_chanctxs_iter(struct ieee80211_hw *hw,
      struct ieee80211_chanctx_conf *conf,
      void *data)
{
 int *num = data;

 (*num)++;
}

static int ath10k_mac_num_chanctxs(struct ath10k *ar)
{
 int num = 0;

 ieee80211_iter_chan_contexts_atomic(ar->hw,
         ath10k_mac_num_chanctxs_iter,
         &num);

 return num;
}

static void
ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
    struct ieee80211_chanctx_conf *conf,
    void *data)
{
 struct cfg80211_chan_def **def = data;

 *def = &conf->def;
}

static void ath10k_wait_for_peer_delete_done(struct ath10k *ar, u32 vdev_id,
          const u8 *addr)
{
 unsigned long time_left;
 int ret;

 if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) {
  ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
  if (ret) {
   ath10k_warn(ar, "failed wait for peer deleted");
   return;
  }

  time_left = wait_for_completion_timeout(&ar->peer_delete_done,
       5 * HZ);
  if (!time_left)
   ath10k_warn(ar, "Timeout in receiving peer delete response\n");
 }
}

static int ath10k_peer_create(struct ath10k *ar,
         struct ieee80211_vif *vif,
         struct ieee80211_sta *sta,
         u32 vdev_id,
         const u8 *addr,
         enum wmi_peer_type peer_type)
{
 struct ath10k_peer *peer;
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 /* Each vdev consumes a peer entry as well. */
 if (ar->num_peers + list_count_nodes(&ar->arvifs) >= ar->max_num_peers)
  return -ENOBUFS;

 ret = ath10k_wmi_peer_create(ar, vdev_id, addr, peer_type);
 if (ret) {
  ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
       addr, vdev_id, ret);
  return ret;
 }

 ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
 if (ret) {
  ath10k_warn(ar, "failed to wait for created wmi peer %pM on vdev %i: %i\n",
       addr, vdev_id, ret);
  return ret;
 }

 spin_lock_bh(&ar->data_lock);

 peer = ath10k_peer_find(ar, vdev_id, addr);
 if (!peer) {
  spin_unlock_bh(&ar->data_lock);
  ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
       addr, vdev_id);
  ath10k_wait_for_peer_delete_done(ar, vdev_id, addr);
  return -ENOENT;
 }

 peer->vif = vif;
 peer->sta = sta;

 spin_unlock_bh(&ar->data_lock);

 ar->num_peers++;

 return 0;
}

static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 u32 param;
 int ret;

 param = ar->wmi.pdev_param->sta_kickout_th;
 ret = ath10k_wmi_pdev_set_param(ar, param,
     ATH10K_KICKOUT_THRESHOLD);
 if (ret) {
  ath10k_warn(ar, "failed to set kickout threshold on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs;
 ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
     ATH10K_KEEPALIVE_MIN_IDLE);
 if (ret) {
  ath10k_warn(ar, "failed to set keepalive minimum idle time on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs;
 ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
     ATH10K_KEEPALIVE_MAX_IDLE);
 if (ret) {
  ath10k_warn(ar, "failed to set keepalive maximum idle time on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs;
 ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
     ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
 if (ret) {
  ath10k_warn(ar, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
{
 struct ath10k *ar = arvif->ar;
 u32 vdev_param;

 vdev_param = ar->wmi.vdev_param->rts_threshold;
 return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
}

static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
 if (ret)
  return ret;

 ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
 if (ret)
  return ret;

 if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) {
  unsigned long time_left;

  time_left = wait_for_completion_timeout
       (&ar->peer_delete_done, 5 * HZ);

  if (!time_left) {
   ath10k_warn(ar, "Timeout in receiving peer delete response\n");
   return -ETIMEDOUT;
  }
 }

 ar->num_peers--;

 return 0;
}

static void ath10k_peer_map_cleanup(struct ath10k *ar, struct ath10k_peer *peer)
{
 int peer_id, i;

 lockdep_assert_held(&ar->conf_mutex);

 for_each_set_bit(peer_id, peer->peer_ids,
    ATH10K_MAX_NUM_PEER_IDS) {
  ar->peer_map[peer_id] = NULL;
 }

 /* Double check that peer is properly un-referenced from
 * the peer_map
 */

 for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
  if (ar->peer_map[i] == peer) {
   ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %p idx %d)\n",
        peer->addr, peer, i);
   ar->peer_map[i] = NULL;
  }
 }

 list_del(&peer->list);
 kfree(peer);
 ar->num_peers--;
}

static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
{
 struct ath10k_peer *peer, *tmp;

 lockdep_assert_held(&ar->conf_mutex);

 spin_lock_bh(&ar->data_lock);
 list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
  if (peer->vdev_id != vdev_id)
   continue;

  ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n",
       peer->addr, vdev_id);

  ath10k_peer_map_cleanup(ar, peer);
 }
 spin_unlock_bh(&ar->data_lock);
}

static void ath10k_peer_cleanup_all(struct ath10k *ar)
{
 struct ath10k_peer *peer, *tmp;
 int i;

 lockdep_assert_held(&ar->conf_mutex);

 spin_lock_bh(&ar->data_lock);
 list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
  list_del(&peer->list);
  kfree(peer);
 }

 for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++)
  ar->peer_map[i] = NULL;

 spin_unlock_bh(&ar->data_lock);

 ar->num_peers = 0;
 ar->num_stations = 0;
}

static int ath10k_mac_tdls_peer_update(struct ath10k *ar, u32 vdev_id,
           struct ieee80211_sta *sta,
           enum wmi_tdls_peer_state state)
{
 int ret;
 struct wmi_tdls_peer_update_cmd_arg arg = {};
 struct wmi_tdls_peer_capab_arg cap = {};
 struct wmi_channel_arg chan_arg = {};

 lockdep_assert_held(&ar->conf_mutex);

 arg.vdev_id = vdev_id;
 arg.peer_state = state;
 ether_addr_copy(arg.addr, sta->addr);

 cap.peer_max_sp = sta->max_sp;
 cap.peer_uapsd_queues = sta->uapsd_queues;

 if (state == WMI_TDLS_PEER_STATE_CONNECTED &&
     !sta->tdls_initiator)
  cap.is_peer_responder = 1;

 ret = ath10k_wmi_tdls_peer_update(ar, &arg, &cap, &chan_arg);
 if (ret) {
  ath10k_warn(ar, "failed to update tdls peer %pM on vdev %i: %i\n",
       arg.addr, vdev_id, ret);
  return ret;
 }

 return 0;
}

/************************/
/* Interface management */
/************************/

void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;

 lockdep_assert_held(&ar->data_lock);

 if (!arvif->beacon)
  return;

 if (!arvif->beacon_buf)
  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_state = ATH10K_BEACON_SCHEDULED;
}

static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;

 lockdep_assert_held(&ar->data_lock);

 ath10k_mac_vif_beacon_free(arvif);

 if (arvif->beacon_buf) {
  if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
   kfree(arvif->beacon_buf);
  else
   dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
       arvif->beacon_buf,
       arvif->beacon_paddr);
  arvif->beacon_buf = NULL;
 }
}

static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
{
 unsigned long time_left;

 lockdep_assert_held(&ar->conf_mutex);

 if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
  return -ESHUTDOWN;

 time_left = wait_for_completion_timeout(&ar->vdev_setup_done,
      ATH10K_VDEV_SETUP_TIMEOUT_HZ);
 if (time_left == 0)
  return -ETIMEDOUT;

 return ar->last_wmi_vdev_start_status;
}

static inline int ath10k_vdev_delete_sync(struct ath10k *ar)
{
 unsigned long time_left;

 lockdep_assert_held(&ar->conf_mutex);

 if (!test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map))
  return 0;

 if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
  return -ESHUTDOWN;

 time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
      ATH10K_VDEV_DELETE_TIMEOUT_HZ);
 if (time_left == 0)
  return -ETIMEDOUT;

 return 0;
}

static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
{
 struct cfg80211_chan_def *chandef = NULL;
 struct ieee80211_channel *channel = NULL;
 struct wmi_vdev_start_request_arg arg = {};
 int ret = 0;

 lockdep_assert_held(&ar->conf_mutex);

 ieee80211_iter_chan_contexts_atomic(ar->hw,
         ath10k_mac_get_any_chandef_iter,
         &chandef);
 if (WARN_ON_ONCE(!chandef))
  return -ENOENT;

 channel = chandef->chan;

 arg.vdev_id = vdev_id;
 arg.channel.freq = channel->center_freq;
 arg.channel.band_center_freq1 = chandef->center_freq1;
 arg.channel.band_center_freq2 = chandef->center_freq2;

 /* TODO setup this dynamically, what in case we
 * don't have any vifs?
 */

 arg.channel.mode = chan_to_phymode(chandef);
 arg.channel.chan_radar =
   !!(channel->flags & IEEE80211_CHAN_RADAR);

 arg.channel.min_power = 0;
 arg.channel.max_power = channel->max_power * 2;
 arg.channel.max_reg_power = channel->max_reg_power * 2;
 arg.channel.max_antenna_gain = channel->max_antenna_gain;

 reinit_completion(&ar->vdev_setup_done);
 reinit_completion(&ar->vdev_delete_done);

 ret = ath10k_wmi_vdev_start(ar, &arg);
 if (ret) {
  ath10k_warn(ar, "failed to request monitor vdev %i start: %d\n",
       vdev_id, ret);
  return ret;
 }

 ret = ath10k_vdev_setup_sync(ar);
 if (ret) {
  ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i start: %d\n",
       vdev_id, ret);
  return ret;
 }

 ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
 if (ret) {
  ath10k_warn(ar, "failed to put up monitor vdev %i: %d\n",
       vdev_id, ret);
  goto vdev_stop;
 }

 ar->monitor_vdev_id = vdev_id;

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i started\n",
     ar->monitor_vdev_id);
 return 0;

vdev_stop:
 ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
 if (ret)
  ath10k_warn(ar, "failed to stop monitor vdev %i after start failure: %d\n",
       ar->monitor_vdev_id, ret);

 return ret;
}

static int ath10k_monitor_vdev_stop(struct ath10k *ar)
{
 int ret = 0;

 lockdep_assert_held(&ar->conf_mutex);

 ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
 if (ret)
  ath10k_warn(ar, "failed to put down monitor vdev %i: %d\n",
       ar->monitor_vdev_id, ret);

 reinit_completion(&ar->vdev_setup_done);
 reinit_completion(&ar->vdev_delete_done);

 ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
 if (ret)
  ath10k_warn(ar, "failed to request monitor vdev %i stop: %d\n",
       ar->monitor_vdev_id, ret);

 ret = ath10k_vdev_setup_sync(ar);
 if (ret)
  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",
     ar->monitor_vdev_id);
 return ret;
}

static int ath10k_monitor_vdev_create(struct ath10k *ar)
{
 int bit, ret = 0;

 lockdep_assert_held(&ar->conf_mutex);

 if (ar->free_vdev_map == 0) {
  ath10k_warn(ar, "failed to find free vdev id for monitor vdev\n");
  return -ENOMEM;
 }

 bit = __ffs64(ar->free_vdev_map);

 ar->monitor_vdev_id = bit;

 ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
         WMI_VDEV_TYPE_MONITOR,
         0, ar->mac_addr);
 if (ret) {
  ath10k_warn(ar, "failed to request monitor vdev %i creation: %d\n",
       ar->monitor_vdev_id, ret);
  return ret;
 }

 ar->free_vdev_map &= ~(1LL << ar->monitor_vdev_id);
 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
     ar->monitor_vdev_id);

 return 0;
}

static int ath10k_monitor_vdev_delete(struct ath10k *ar)
{
 int ret = 0;

 lockdep_assert_held(&ar->conf_mutex);

 ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
 if (ret) {
  ath10k_warn(ar, "failed to request wmi monitor vdev %i removal: %d\n",
       ar->monitor_vdev_id, ret);
  return ret;
 }

 ar->free_vdev_map |= 1LL << ar->monitor_vdev_id;

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
     ar->monitor_vdev_id);
 return ret;
}

static int ath10k_monitor_start(struct ath10k *ar)
{
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 ret = ath10k_monitor_vdev_create(ar);
 if (ret) {
  ath10k_warn(ar, "failed to create monitor vdev: %d\n", ret);
  return ret;
 }

 ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
 if (ret) {
  ath10k_warn(ar, "failed to start monitor vdev: %d\n", ret);
  ath10k_monitor_vdev_delete(ar);
  return ret;
 }

 ar->monitor_started = true;
 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor started\n");

 return 0;
}

static int ath10k_monitor_stop(struct ath10k *ar)
{
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 ret = ath10k_monitor_vdev_stop(ar);
 if (ret) {
  ath10k_warn(ar, "failed to stop monitor vdev: %d\n", ret);
  return ret;
 }

 ret = ath10k_monitor_vdev_delete(ar);
 if (ret) {
  ath10k_warn(ar, "failed to delete monitor vdev: %d\n", ret);
  return ret;
 }

 ar->monitor_started = false;
 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor stopped\n");

 return 0;
}

static bool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar)
{
 int num_ctx;

 /* At least one chanctx is required to derive a channel to start
 * monitor vdev on.
 */

 num_ctx = ath10k_mac_num_chanctxs(ar);
 if (num_ctx == 0)
  return false;

 /* If there's already an existing special monitor interface then don't
 * bother creating another monitor vdev.
 */

 if (ar->monitor_arvif)
  return false;

 return ar->monitor ||
        (!test_bit(ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST,
     ar->running_fw->fw_file.fw_features) &&
  (ar->filter_flags & (FIF_OTHER_BSS | FIF_MCAST_ACTION))) ||
        test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
}

static bool ath10k_mac_monitor_vdev_is_allowed(struct ath10k *ar)
{
 int num_ctx;

 num_ctx = ath10k_mac_num_chanctxs(ar);

 /* FIXME: Current interface combinations and cfg80211/mac80211 code
 * shouldn't allow this but make sure to prevent handling the following
 * case anyway since multi-channel DFS hasn't been tested at all.
 */

 if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags) && num_ctx > 1)
  return false;

 return true;
}

static int ath10k_monitor_recalc(struct ath10k *ar)
{
 bool needed;
 bool allowed;
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 needed = ath10k_mac_monitor_vdev_is_needed(ar);
 allowed = ath10k_mac_monitor_vdev_is_allowed(ar);

 ath10k_dbg(ar, ATH10K_DBG_MAC,
     "mac monitor recalc started? %d needed? %d allowed? %d\n",
     ar->monitor_started, needed, allowed);

 if (WARN_ON(needed && !allowed)) {
  if (ar->monitor_started) {
   ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor stopping disallowed monitor\n");

   ret = ath10k_monitor_stop(ar);
   if (ret)
    ath10k_warn(ar, "failed to stop disallowed monitor: %d\n",
         ret);
    /* not serious */
  }

  return -EPERM;
 }

 if (needed == ar->monitor_started)
  return 0;

 if (needed)
  return ath10k_monitor_start(ar);
 else
  return ath10k_monitor_stop(ar);
}

static bool ath10k_mac_can_set_cts_prot(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;

 lockdep_assert_held(&ar->conf_mutex);

 if (!arvif->is_started) {
  ath10k_dbg(ar, ATH10K_DBG_MAC, "defer cts setup, vdev is not ready yet\n");
  return false;
 }

 return true;
}

static int ath10k_mac_set_cts_prot(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 u32 vdev_param;

 lockdep_assert_held(&ar->conf_mutex);

 vdev_param = ar->wmi.vdev_param->protection_mode;

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d cts_protection %d\n",
     arvif->vdev_id, arvif->use_cts_prot);

 return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
      arvif->use_cts_prot ? 1 : 0);
}

static int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 u32 vdev_param, rts_cts = 0;

 lockdep_assert_held(&ar->conf_mutex);

 vdev_param = ar->wmi.vdev_param->enable_rtscts;

 rts_cts |= SM(WMI_RTSCTS_ENABLED, WMI_RTSCTS_SET);

 if (arvif->num_legacy_stations > 0)
  rts_cts |= SM(WMI_RTSCTS_ACROSS_SW_RETRIES,
         WMI_RTSCTS_PROFILE);
 else
  rts_cts |= SM(WMI_RTSCTS_FOR_SECOND_RATESERIES,
         WMI_RTSCTS_PROFILE);

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d recalc rts/cts prot %d\n",
     arvif->vdev_id, rts_cts);

 return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
      rts_cts);
}

static int ath10k_start_cac(struct ath10k *ar)
{
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);

 ret = ath10k_monitor_recalc(ar);
 if (ret) {
  ath10k_warn(ar, "failed to start monitor (cac): %d\n", ret);
  clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n",
     ar->monitor_vdev_id);

 return 0;
}

static int ath10k_stop_cac(struct ath10k *ar)
{
 lockdep_assert_held(&ar->conf_mutex);

 /* CAC is not running - do nothing */
 if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
  return 0;

 clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 ath10k_monitor_stop(ar);

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac finished\n");

 return 0;
}

static void ath10k_mac_has_radar_iter(struct ieee80211_hw *hw,
          struct ieee80211_chanctx_conf *conf,
          void *data)
{
 bool *ret = data;

 if (!*ret && conf->radar_enabled)
  *ret = true;
}

static bool ath10k_mac_has_radar_enabled(struct ath10k *ar)
{
 bool has_radar = false;

 ieee80211_iter_chan_contexts_atomic(ar->hw,
         ath10k_mac_has_radar_iter,
         &has_radar);

 return has_radar;
}

static void ath10k_recalc_radar_detection(struct ath10k *ar)
{
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 ath10k_stop_cac(ar);

 if (!ath10k_mac_has_radar_enabled(ar))
  return;

 if (ar->num_started_vdevs > 0)
  return;

 ret = ath10k_start_cac(ar);
 if (ret) {
  /*
 * Not possible to start CAC on current channel so starting
 * radiation is not allowed, make this channel DFS_UNAVAILABLE
 * by indicating that radar was detected.
 */

  ath10k_warn(ar, "failed to start CAC: %d\n", ret);
  ieee80211_radar_detected(ar->hw, NULL);
 }
}

static int ath10k_vdev_stop(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 reinit_completion(&ar->vdev_setup_done);
 reinit_completion(&ar->vdev_delete_done);

 ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
 if (ret) {
  ath10k_warn(ar, "failed to stop WMI vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 ret = ath10k_vdev_setup_sync(ar);
 if (ret) {
  ath10k_warn(ar, "failed to synchronize setup for vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 WARN_ON(ar->num_started_vdevs == 0);

 if (ar->num_started_vdevs != 0) {
  ar->num_started_vdevs--;
  ath10k_recalc_radar_detection(ar);
 }

 return ret;
}

static int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
         const struct cfg80211_chan_def *chandef,
         bool restart)
{
 struct ath10k *ar = arvif->ar;
 struct wmi_vdev_start_request_arg arg = {};
 int ret = 0;

 lockdep_assert_held(&ar->conf_mutex);

 reinit_completion(&ar->vdev_setup_done);
 reinit_completion(&ar->vdev_delete_done);

 arg.vdev_id = arvif->vdev_id;
 arg.dtim_period = arvif->dtim_period;
 arg.bcn_intval = arvif->beacon_interval;

 arg.channel.freq = chandef->chan->center_freq;
 arg.channel.band_center_freq1 = chandef->center_freq1;
 arg.channel.band_center_freq2 = chandef->center_freq2;
 arg.channel.mode = chan_to_phymode(chandef);

 arg.channel.min_power = 0;
 arg.channel.max_power = chandef->chan->max_power * 2;
 arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
 arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain;

 if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
  arg.ssid = arvif->u.ap.ssid;
  arg.ssid_len = arvif->u.ap.ssid_len;
  arg.hidden_ssid = arvif->u.ap.hidden_ssid;

  /* For now allow DFS for AP mode */
  arg.channel.chan_radar =
   !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
 } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
  arg.ssid = arvif->vif->cfg.ssid;
  arg.ssid_len = arvif->vif->cfg.ssid_len;
 }

 ath10k_dbg(ar, ATH10K_DBG_MAC,
     "mac vdev %d start center_freq %d phymode %s\n",
     arg.vdev_id, arg.channel.freq,
     ath10k_wmi_phymode_str(arg.channel.mode));

 if (restart)
  ret = ath10k_wmi_vdev_restart(ar, &arg);
 else
  ret = ath10k_wmi_vdev_start(ar, &arg);

 if (ret) {
  ath10k_warn(ar, "failed to start WMI vdev %i: %d\n",
       arg.vdev_id, ret);
  return ret;
 }

 ret = ath10k_vdev_setup_sync(ar);
 if (ret) {
  ath10k_warn(ar,
       "failed to synchronize setup for vdev %i restart %d: %d\n",
       arg.vdev_id, restart, ret);
  return ret;
 }

 ar->num_started_vdevs++;
 ath10k_recalc_radar_detection(ar);

 return ret;
}

static int ath10k_vdev_start(struct ath10k_vif *arvif,
        const struct cfg80211_chan_def *def)
{
 return ath10k_vdev_start_restart(arvif, def, false);
}

static int ath10k_vdev_restart(struct ath10k_vif *arvif,
          const struct cfg80211_chan_def *def)
{
 return ath10k_vdev_start_restart(arvif, def, true);
}

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->vif->type != NL80211_IFTYPE_AP || !arvif->vif->p2p)
  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;

 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, 0);
 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;

 if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
  return 0;

  /* For mesh, probe response and beacon share the same template */
 if (ieee80211_vif_is_mesh(vif))
  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 int ath10k_mac_vif_fix_hidden_ssid(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 struct cfg80211_chan_def def;
 int ret;

 /* When originally vdev is started during assign_vif_chanctx() some
 * information is missing, notably SSID. Firmware revisions with beacon
 * offloading require the SSID to be provided during vdev (re)start to
 * handle hidden SSID properly.
 *
 * Vdev restart must be done after vdev has been both started and
 * upped. Otherwise some firmware revisions (at least 10.2) fail to
 * deliver vdev restart response event causing timeouts during vdev
 * syncing in ath10k.
 *
 * Note: The vdev down/up and template reinstallation could be skipped
 * since only wmi-tlv firmware are known to have beacon offload and
 * wmi-tlv doesn't seem to misbehave like 10.2 wrt vdev restart
 * response delivery. It's probably more robust to keep it as is.
 */

 if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
  return 0;

 if (WARN_ON(!arvif->is_started))
  return -EINVAL;

 if (WARN_ON(!arvif->is_up))
  return -EINVAL;

 if (WARN_ON(ath10k_mac_vif_chan(arvif->vif, &def)))
  return -EINVAL;

 ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
 if (ret) {
  ath10k_warn(ar, "failed to bring down ap vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 /* Vdev down reset beacon & presp templates. Reinstall them. Otherwise
 * firmware will crash upon vdev up.
 */


 ret = ath10k_mac_setup_bcn_tmpl(arvif);
 if (ret) {
  ath10k_warn(ar, "failed to update beacon template: %d\n", ret);
  return ret;
 }

 ret = ath10k_mac_setup_prb_tmpl(arvif);
 if (ret) {
  ath10k_warn(ar, "failed to update presp template: %d\n", ret);
  return ret;
 }

 ret = ath10k_vdev_restart(arvif, &def);
 if (ret) {
  ath10k_warn(ar, "failed to restart ap vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
     arvif->bssid);
 if (ret) {
  ath10k_warn(ar, "failed to bring up ap vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

static void ath10k_control_beaconing(struct ath10k_vif *arvif,
         struct ieee80211_bss_conf *info)
{
 struct ath10k *ar = arvif->ar;
 int ret = 0;

 lockdep_assert_held(&arvif->ar->conf_mutex);

 if (!info->enable_beacon) {
  ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
  if (ret)
   ath10k_warn(ar, "failed to down vdev_id %i: %d\n",
        arvif->vdev_id, ret);

  arvif->is_up = false;

  spin_lock_bh(&arvif->ar->data_lock);
  ath10k_mac_vif_beacon_free(arvif);
  spin_unlock_bh(&arvif->ar->data_lock);

  return;
 }

 arvif->tx_seq_no = 0x1000;

 arvif->aid = 0;
 ether_addr_copy(arvif->bssid, info->bssid);

 ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
     arvif->bssid);
 if (ret) {
  ath10k_warn(ar, "failed to bring up vdev %d: %i\n",
       arvif->vdev_id, ret);
  return;
 }

 arvif->is_up = true;

 ret = ath10k_mac_vif_fix_hidden_ssid(arvif);
 if (ret) {
  ath10k_warn(ar, "failed to fix hidden ssid for vdev %i, expect trouble: %d\n",
       arvif->vdev_id, ret);
  return;
 }

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
}

static void ath10k_control_ibss(struct ath10k_vif *arvif,
    struct ieee80211_vif *vif)
{
 struct ath10k *ar = arvif->ar;
 u32 vdev_param;
 int ret = 0;

 lockdep_assert_held(&arvif->ar->conf_mutex);

 if (!vif->cfg.ibss_joined) {
  if (is_zero_ether_addr(arvif->bssid))
   return;

  eth_zero_addr(arvif->bssid);

  return;
 }

 vdev_param = arvif->ar->wmi.vdev_param->atim_window;
 ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
     ATH10K_DEFAULT_ATIM);
 if (ret)
  ath10k_warn(ar, "failed to set IBSS ATIM for vdev %d: %d\n",
       arvif->vdev_id, ret);
}

static int ath10k_mac_vif_recalc_ps_wake_threshold(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 u32 param;
 u32 value;
 int ret;

 lockdep_assert_held(&arvif->ar->conf_mutex);

 if (arvif->u.sta.uapsd)
  value = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER;
 else
  value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;

 param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
 ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value);
 if (ret) {
  ath10k_warn(ar, "failed to submit ps wake threshold %u on vdev %i: %d\n",
       value, arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 u32 param;
 u32 value;
 int ret;

 lockdep_assert_held(&arvif->ar->conf_mutex);

 if (arvif->u.sta.uapsd)
  value = WMI_STA_PS_PSPOLL_COUNT_UAPSD;
 else
  value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;

 param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
 ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
       param, value);
 if (ret) {
  ath10k_warn(ar, "failed to submit ps poll count %u on vdev %i: %d\n",
       value, arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

static int ath10k_mac_num_vifs_started(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->is_started)
   num++;

 return num;
}

static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
{
 struct ath10k *ar = arvif->ar;
 struct ieee80211_vif *vif = arvif->vif;
 struct ieee80211_conf *conf = &ar->hw->conf;
 enum wmi_sta_powersave_param param;
 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;

 enable_ps = arvif->ps;

 if (enable_ps && ath10k_mac_num_vifs_started(ar) > 1 &&
     !test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT,
        ar->running_fw->fw_file.fw_features)) {
  ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n",
       arvif->vdev_id);
  enable_ps = false;
 }

 if (!arvif->is_started) {
  /* mac80211 can update vif powersave state while disconnected.
 * Firmware doesn't behave nicely and consumes more power than
 * necessary if PS is disabled on a non-started vdev. Hence
 * force-enable PS for non-running vdevs.
 */

  psmode = WMI_STA_PS_MODE_ENABLED;
 } else if (enable_ps) {
  psmode = WMI_STA_PS_MODE_ENABLED;
  param = WMI_STA_PS_PARAM_INACTIVITY_TIME;

  ps_timeout = conf->dynamic_ps_timeout;
  if (ps_timeout == 0) {
   /* Firmware doesn't like 0 */
   ps_timeout = ieee80211_tu_to_usec(
    vif->bss_conf.beacon_int) / 1000;
  }

  ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
        ps_timeout);
  if (ret) {
   ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n",
        arvif->vdev_id, ret);
   return ret;
  }
 } else {
  psmode = WMI_STA_PS_MODE_DISABLED;
 }

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
     arvif->vdev_id, psmode ? "enable" : "disable");

 ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
 if (ret) {
  ath10k_warn(ar, "failed to set PS Mode %d for vdev %d: %d\n",
       psmode, arvif->vdev_id, ret);
  return ret;
 }

 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;
}

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;

 lockdep_assert_held(&arvif->ar->conf_mutex);

 if (WARN_ON(!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)))
  return;

 if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
  return;

 if (!vif->bss_conf.csa_active)
  return;

 if (!arvif->is_up)
  return;

 if (!ieee80211_beacon_cntdwn_is_complete(vif, 0)) {
  ieee80211_beacon_update_cntdwn(vif, 0);

  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, 0);
 }
}

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);
}

static void ath10k_mac_handle_beacon_iter(void *data, u8 *mac,
       struct ieee80211_vif *vif)
{
 struct sk_buff *skb = data;
 struct ieee80211_mgmt *mgmt = (void *)skb->data;
 struct ath10k_vif *arvif = (void *)vif->drv_priv;

 if (vif->type != NL80211_IFTYPE_STATION)
  return;

 if (!ether_addr_equal(mgmt->bssid, vif->bss_conf.bssid))
  return;

 cancel_delayed_work(&arvif->connection_loss_work);
}

void ath10k_mac_handle_beacon(struct ath10k *ar, struct sk_buff *skb)
{
 ieee80211_iterate_active_interfaces_atomic(ar->hw,
         ATH10K_ITER_NORMAL_FLAGS,
         ath10k_mac_handle_beacon_iter,
         skb);
}

static void ath10k_mac_handle_beacon_miss_iter(void *data, u8 *mac,
            struct ieee80211_vif *vif)
{
 u32 *vdev_id = data;
 struct ath10k_vif *arvif = (void *)vif->drv_priv;
 struct ath10k *ar = arvif->ar;
 struct ieee80211_hw *hw = ar->hw;

 if (arvif->vdev_id != *vdev_id)
  return;

 if (!arvif->is_up)
  return;

 ieee80211_beacon_loss(vif);

 /* Firmware doesn't report beacon loss events repeatedly. If AP probe
 * (done by mac80211) succeeds but beacons do not resume then it
 * doesn't make sense to continue operation. Queue connection loss work
 * which can be cancelled when beacon is received.
 */

 ieee80211_queue_delayed_work(hw, &arvif->connection_loss_work,
         ATH10K_CONNECTION_LOSS_HZ);
}

void ath10k_mac_handle_beacon_miss(struct ath10k *ar, u32 vdev_id)
{
 ieee80211_iterate_active_interfaces_atomic(ar->hw,
         ATH10K_ITER_NORMAL_FLAGS,
         ath10k_mac_handle_beacon_miss_iter,
         &vdev_id);
}

static void ath10k_mac_vif_sta_connection_loss_work(struct work_struct *work)
{
 struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
      connection_loss_work.work);
 struct ieee80211_vif *vif = arvif->vif;

 if (!arvif->is_up)
  return;

 ieee80211_connection_loss(vif);
}

/**********************/
/* Station management */
/**********************/

static u32 ath10k_peer_assoc_h_listen_intval(struct ath10k *ar,
          struct ieee80211_vif *vif)
{
 /* Some firmware revisions have unstable STA powersave when listen
 * interval is set too high (e.g. 5). The symptoms are firmware doesn't
 * generate NullFunc frames properly even if buffered frames have been
 * indicated in Beacon TIM. Firmware would seldom wake up to pull
 * buffered frames. Often pinging the device from AP would simply fail.
 *
 * As a workaround set it to 1.
 */

 if (vif->type == NL80211_IFTYPE_STATION)
  return 1;

 return ar->hw->conf.listen_interval;
}

static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
          struct ieee80211_vif *vif,
          struct ieee80211_sta *sta,
          struct wmi_peer_assoc_complete_arg *arg)
{
 struct ath10k_vif *arvif = (void *)vif->drv_priv;
 u32 aid;

 lockdep_assert_held(&ar->conf_mutex);

 if (vif->type == NL80211_IFTYPE_STATION)
  aid = vif->cfg.aid;
 else
  aid = sta->aid;

 ether_addr_copy(arg->addr, sta->addr);
 arg->vdev_id = arvif->vdev_id;
 arg->peer_aid = aid;
 arg->peer_flags |= arvif->ar->wmi.peer_flags->auth;
 arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif);
 arg->peer_num_spatial_streams = 1;
 arg->peer_caps = vif->bss_conf.assoc_capability;
}

static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
           struct ieee80211_vif *vif,
           struct ieee80211_sta *sta,
           struct wmi_peer_assoc_complete_arg *arg)
{
 struct ieee80211_bss_conf *info = &vif->bss_conf;
 struct cfg80211_chan_def def;
 struct cfg80211_bss *bss;
 const u8 *rsnie = NULL;
 const u8 *wpaie = NULL;

 lockdep_assert_held(&ar->conf_mutex);

 if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
  return;

 bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid,
          vif->cfg.ssid_len ? vif->cfg.ssid : NULL,
          vif->cfg.ssid_len,
          IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
 if (bss) {
  const struct cfg80211_bss_ies *ies;

  rcu_read_lock();
  rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);

  ies = rcu_dereference(bss->ies);

  wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
      WLAN_OUI_TYPE_MICROSOFT_WPA,
      ies->data,
      ies->len);
  rcu_read_unlock();
  cfg80211_put_bss(ar->hw->wiphy, bss);
 }

 /* FIXME: base on RSN IE/WPA IE is a correct idea? */
 if (rsnie || wpaie) {
  ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
  arg->peer_flags |= ar->wmi.peer_flags->need_ptk_4_way;
 }

 if (wpaie) {
  ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
  arg->peer_flags |= ar->wmi.peer_flags->need_gtk_2_way;
 }

 if (sta->mfp &&
     test_bit(ATH10K_FW_FEATURE_MFP_SUPPORT,
       ar->running_fw->fw_file.fw_features)) {
  arg->peer_flags |= ar->wmi.peer_flags->pmf;
 }
}

static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
          struct ieee80211_vif *vif,
          struct ieee80211_sta *sta,
          struct wmi_peer_assoc_complete_arg *arg)
{
 struct ath10k_vif *arvif = (void *)vif->drv_priv;
 struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates;
 struct cfg80211_chan_def def;
 const struct ieee80211_supported_band *sband;
 const struct ieee80211_rate *rates;
 enum nl80211_band band;
 u32 ratemask;
 u8 rate;
 int i;

 lockdep_assert_held(&ar->conf_mutex);

 if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
  return;

 band = def.chan->band;
 sband = ar->hw->wiphy->bands[band];
 ratemask = sta->deflink.supp_rates[band];
 ratemask &= arvif->bitrate_mask.control[band].legacy;
 rates = sband->bitrates;

 rateset->num_rates = 0;

 for (i = 0; i < 32; i++, ratemask >>= 1, rates++) {
  if (!(ratemask & 1))
   continue;

  rate = ath10k_mac_bitrate_to_rate(rates->bitrate);
  rateset->rates[rateset->num_rates] = rate;
  rateset->num_rates++;
 }
}

static bool
ath10k_peer_assoc_h_ht_masked(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
{
 int nss;

 for (nss = 0; nss < IEEE80211_HT_MCS_MASK_LEN; nss++)
  if (ht_mcs_mask[nss])
   return false;

 return true;
}

static bool
ath10k_peer_assoc_h_vht_masked(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
{
 int nss;

 for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++)
  if (vht_mcs_mask[nss])
   return false;

 return true;
}

static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
       struct ieee80211_vif *vif,
       struct ieee80211_sta *sta,
       struct wmi_peer_assoc_complete_arg *arg)
{
 const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
 struct ath10k_vif *arvif = (void *)vif->drv_priv;
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 const u8 *ht_mcs_mask;
 const u16 *vht_mcs_mask;
 int i, n;
 u8 max_nss;
 u32 stbc;

 lockdep_assert_held(&ar->conf_mutex);

 if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
  return;

 if (!ht_cap->ht_supported)
  return;

 band = def.chan->band;
 ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
 vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;

 if (ath10k_peer_assoc_h_ht_masked(ht_mcs_mask) &&
     ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
  return;

 arg->peer_flags |= ar->wmi.peer_flags->ht;
 arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
        ht_cap->ampdu_factor)) - 1;

 arg->peer_mpdu_density =
  ath10k_parse_mpdudensity(ht_cap->ampdu_density);

 arg->peer_ht_caps = ht_cap->cap;
 arg->peer_rate_caps |= WMI_RC_HT_FLAG;

 if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
  arg->peer_flags |= ar->wmi.peer_flags->ldbc;

 if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) {
  arg->peer_flags |= ar->wmi.peer_flags->bw40;
  arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
 }

 if (arvif->bitrate_mask.control[band].gi != NL80211_TXRATE_FORCE_LGI) {
  if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
   arg->peer_rate_caps |= WMI_RC_SGI_FLAG;

  if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
   arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
 }

 if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
  arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
  arg->peer_flags |= ar->wmi.peer_flags->stbc;
 }

 if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
  stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC;
  stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
  stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
  arg->peer_rate_caps |= stbc;
  arg->peer_flags |= ar->wmi.peer_flags->stbc;
 }

 if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
  arg->peer_rate_caps |= WMI_RC_TS_FLAG;
 else if (ht_cap->mcs.rx_mask[1])
  arg->peer_rate_caps |= WMI_RC_DS_FLAG;

 for (i = 0, n = 0, max_nss = 0; i < IEEE80211_HT_MCS_MASK_LEN * 8; i++)
  if ((ht_cap->mcs.rx_mask[i / 8] & BIT(i % 8)) &&
      (ht_mcs_mask[i / 8] & BIT(i % 8))) {
   max_nss = (i / 8) + 1;
   arg->peer_ht_rates.rates[n++] = i;
  }

 /*
 * This is a workaround for HT-enabled STAs which break the spec
 * and have no HT capabilities RX mask (no HT RX MCS map).
 *
 * As per spec, in section 20.3.5 Modulation and coding scheme (MCS),
 * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs.
 *
 * Firmware asserts if such situation occurs.
 */

 if (n == 0) {
  arg->peer_ht_rates.num_rates = 8;
  for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
   arg->peer_ht_rates.rates[i] = i;
 } else {
  arg->peer_ht_rates.num_rates = n;
  arg->peer_num_spatial_streams = min(sta->deflink.rx_nss,
          max_nss);
 }

 ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
     arg->addr,
     arg->peer_ht_rates.num_rates,
     arg->peer_num_spatial_streams);
}

static int ath10k_peer_assoc_qos_ap(struct ath10k *ar,
        struct ath10k_vif *arvif,
        struct ieee80211_sta *sta)
{
 u32 uapsd = 0;
 u32 max_sp = 0;
 int ret = 0;

 lockdep_assert_held(&ar->conf_mutex);

 if (sta->wme && sta->uapsd_queues) {
  ath10k_dbg(ar, ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
      sta->uapsd_queues, sta->max_sp);

  if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
   uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
     WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
  if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
   uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN |
     WMI_AP_PS_UAPSD_AC2_TRIGGER_EN;
  if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
   uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN |
     WMI_AP_PS_UAPSD_AC1_TRIGGER_EN;
  if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
   uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
     WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;

  if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
   max_sp = sta->max_sp;

  ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
       sta->addr,
       WMI_AP_PS_PEER_PARAM_UAPSD,
       uapsd);
  if (ret) {
   ath10k_warn(ar, "failed to set ap ps peer param uapsd for vdev %i: %d\n",
        arvif->vdev_id, ret);
   return ret;
  }

  ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
       sta->addr,
       WMI_AP_PS_PEER_PARAM_MAX_SP,
       max_sp);
  if (ret) {
   ath10k_warn(ar, "failed to set ap ps peer param max sp for vdev %i: %d\n",
        arvif->vdev_id, ret);
   return ret;
  }

  /* TODO setup this based on STA listen interval and
 * beacon interval. Currently we don't know
 * sta->listen_interval - mac80211 patch required.
 * Currently use 10 seconds
 */

  ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr,
       WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
       10);
  if (ret) {
   ath10k_warn(ar, "failed to set ap ps peer param ageout time for vdev %i: %d\n",
        arvif->vdev_id, ret);
   return ret;
  }
 }

 return 0;
}

static u16
ath10k_peer_assoc_h_vht_limit(u16 tx_mcs_set,
         const u16 vht_mcs_limit[NL80211_VHT_NSS_MAX])
{
 int idx_limit;
 int nss;
 u16 mcs_map;
 u16 mcs;

 for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
  mcs_map = ath10k_mac_get_max_vht_mcs_map(tx_mcs_set, nss) &
     vht_mcs_limit[nss];

  if (mcs_map)
   idx_limit = fls(mcs_map) - 1;
  else
   idx_limit = -1;

  switch (idx_limit) {
  case 0:
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
  case 6:
  default:
   /* see ath10k_mac_can_set_bitrate_mask() */
   WARN_ON(1);
   fallthrough;
  case -1:
   mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED;
   break;
  case 7:
   mcs = IEEE80211_VHT_MCS_SUPPORT_0_7;
   break;
  case 8:
   mcs = IEEE80211_VHT_MCS_SUPPORT_0_8;
   break;
  case 9:
   mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
   break;
  }

  tx_mcs_set &= ~(0x3 << (nss * 2));
  tx_mcs_set |= mcs << (nss * 2);
 }

 return tx_mcs_set;
}

static u32 get_160mhz_nss_from_maxrate(int rate)
{
 u32 nss;

 switch (rate) {
 case 780:
  nss = 1;
  break;
 case 1560:
  nss = 2;
  break;
 case 2106:
  nss = 3; /* not support MCS9 from spec*/
  break;
 case 3120:
  nss = 4;
  break;
 default:
   nss = 1;
 }

 return nss;
}

static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
        struct ieee80211_vif *vif,
        struct ieee80211_sta *sta,
        struct wmi_peer_assoc_complete_arg *arg)
{
 const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
 struct ath10k_vif *arvif = (void *)vif->drv_priv;
 struct ath10k_hw_params *hw = &ar->hw_params;
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 const u16 *vht_mcs_mask;
 u8 ampdu_factor;
 u8 max_nss, vht_mcs;
 int i;

 if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
  return;

 if (!vht_cap->vht_supported)
  return;

 band = def.chan->band;
 vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;

 if (ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
  return;

 arg->peer_flags |= ar->wmi.peer_flags->vht;

 if (def.chan->band == NL80211_BAND_2GHZ)
  arg->peer_flags |= ar->wmi.peer_flags->vht_2g;

 arg->peer_vht_caps = vht_cap->cap;

 ampdu_factor = (vht_cap->cap &
   IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
         IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;

 /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to
 * zero in VHT IE. Using it would result in degraded throughput.
 * arg->peer_max_mpdu at this point contains HT max_mpdu so keep
 * it if VHT max_mpdu is smaller.
 */

 arg->peer_max_mpdu = max(arg->peer_max_mpdu,
     (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
     ampdu_factor)) - 1);

 if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80)
  arg->peer_flags |= ar->wmi.peer_flags->bw80;

 if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
  arg->peer_flags |= ar->wmi.peer_flags->bw160;

 /* Calculate peer NSS capability from VHT capabilities if STA
 * supports VHT.
 */

 for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) {
  vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >>
     (2 * i) & 3;

  if ((vht_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) &&
      vht_mcs_mask[i])
   max_nss = i + 1;
 }
 arg->peer_num_spatial_streams = min(sta->deflink.rx_nss, max_nss);
 arg->peer_vht_rates.rx_max_rate =
  __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
 arg->peer_vht_rates.rx_mcs_set =
  __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
 arg->peer_vht_rates.tx_max_rate =
  __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
 arg->peer_vht_rates.tx_mcs_set = ath10k_peer_assoc_h_vht_limit(
  __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask);

 /* Configure bandwidth-NSS mapping to FW
 * for the chip's tx chains setting on 160Mhz bw
 */

 if (arg->peer_phymode == MODE_11AC_VHT160 ||
     arg->peer_phymode == MODE_11AC_VHT80_80) {
  u32 rx_nss;
  u32 max_rate;

  max_rate = arg->peer_vht_rates.rx_max_rate;
  rx_nss = get_160mhz_nss_from_maxrate(max_rate);

  if (rx_nss == 0)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=94 G=95

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.