Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  mac.c   Sprache: C

 
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */


#include <net/mac80211.h>
#include <net/cfg80211.h>
#include <linux/etherdevice.h>
#include <linux/bitfield.h>
#include <linux/inetdevice.h>
#include <net/if_inet6.h>
#include <net/ipv6.h>

#include "mac.h"
#include "core.h"
#include "debug.h"
#include "wmi.h"
#include "hw.h"
#include "dp_tx.h"
#include "dp_rx.h"
#include "testmode.h"
#include "peer.h"
#include "debugfs_sta.h"
#include "hif.h"
#include "wow.h"

#define CHAN2G(_channel, _freq, _flags) { \
 .band                   = NL80211_BAND_2GHZ, \
 .hw_value               = (_channel), \
 .center_freq            = (_freq), \
 .flags                  = (_flags), \
 .max_antenna_gain       = 0, \
 .max_power              = 30, \
}

#define CHAN5G(_channel, _freq, _flags) { \
 .band                   = NL80211_BAND_5GHZ, \
 .hw_value               = (_channel), \
 .center_freq            = (_freq), \
 .flags                  = (_flags), \
 .max_antenna_gain       = 0, \
 .max_power              = 30, \
}

#define CHAN6G(_channel, _freq, _flags) { \
 .band                   = NL80211_BAND_6GHZ, \
 .hw_value               = (_channel), \
 .center_freq            = (_freq), \
 .flags                  = (_flags), \
 .max_antenna_gain       = 0, \
 .max_power              = 30, \
}

static const struct ieee80211_channel ath11k_2ghz_channels[] = {
 CHAN2G(1, 2412, 0),
 CHAN2G(2, 2417, 0),
 CHAN2G(3, 2422, 0),
 CHAN2G(4, 2427, 0),
 CHAN2G(5, 2432, 0),
 CHAN2G(6, 2437, 0),
 CHAN2G(7, 2442, 0),
 CHAN2G(8, 2447, 0),
 CHAN2G(9, 2452, 0),
 CHAN2G(10, 2457, 0),
 CHAN2G(11, 2462, 0),
 CHAN2G(12, 2467, 0),
 CHAN2G(13, 2472, 0),
 CHAN2G(14, 2484, 0),
};

static const struct ieee80211_channel ath11k_5ghz_channels[] = {
 CHAN5G(36, 5180, 0),
 CHAN5G(40, 5200, 0),
 CHAN5G(44, 5220, 0),
 CHAN5G(48, 5240, 0),
 CHAN5G(52, 5260, 0),
 CHAN5G(56, 5280, 0),
 CHAN5G(60, 5300, 0),
 CHAN5G(64, 5320, 0),
 CHAN5G(100, 5500, 0),
 CHAN5G(104, 5520, 0),
 CHAN5G(108, 5540, 0),
 CHAN5G(112, 5560, 0),
 CHAN5G(116, 5580, 0),
 CHAN5G(120, 5600, 0),
 CHAN5G(124, 5620, 0),
 CHAN5G(128, 5640, 0),
 CHAN5G(132, 5660, 0),
 CHAN5G(136, 5680, 0),
 CHAN5G(140, 5700, 0),
 CHAN5G(144, 5720, 0),
 CHAN5G(149, 5745, 0),
 CHAN5G(153, 5765, 0),
 CHAN5G(157, 5785, 0),
 CHAN5G(161, 5805, 0),
 CHAN5G(165, 5825, 0),
 CHAN5G(169, 5845, 0),
 CHAN5G(173, 5865, 0),
 CHAN5G(177, 5885, 0),
};

static const struct ieee80211_channel ath11k_6ghz_channels[] = {
 CHAN6G(1, 5955, 0),
 CHAN6G(5, 5975, 0),
 CHAN6G(9, 5995, 0),
 CHAN6G(13, 6015, 0),
 CHAN6G(17, 6035, 0),
 CHAN6G(21, 6055, 0),
 CHAN6G(25, 6075, 0),
 CHAN6G(29, 6095, 0),
 CHAN6G(33, 6115, 0),
 CHAN6G(37, 6135, 0),
 CHAN6G(41, 6155, 0),
 CHAN6G(45, 6175, 0),
 CHAN6G(49, 6195, 0),
 CHAN6G(53, 6215, 0),
 CHAN6G(57, 6235, 0),
 CHAN6G(61, 6255, 0),
 CHAN6G(65, 6275, 0),
 CHAN6G(69, 6295, 0),
 CHAN6G(73, 6315, 0),
 CHAN6G(77, 6335, 0),
 CHAN6G(81, 6355, 0),
 CHAN6G(85, 6375, 0),
 CHAN6G(89, 6395, 0),
 CHAN6G(93, 6415, 0),
 CHAN6G(97, 6435, 0),
 CHAN6G(101, 6455, 0),
 CHAN6G(105, 6475, 0),
 CHAN6G(109, 6495, 0),
 CHAN6G(113, 6515, 0),
 CHAN6G(117, 6535, 0),
 CHAN6G(121, 6555, 0),
 CHAN6G(125, 6575, 0),
 CHAN6G(129, 6595, 0),
 CHAN6G(133, 6615, 0),
 CHAN6G(137, 6635, 0),
 CHAN6G(141, 6655, 0),
 CHAN6G(145, 6675, 0),
 CHAN6G(149, 6695, 0),
 CHAN6G(153, 6715, 0),
 CHAN6G(157, 6735, 0),
 CHAN6G(161, 6755, 0),
 CHAN6G(165, 6775, 0),
 CHAN6G(169, 6795, 0),
 CHAN6G(173, 6815, 0),
 CHAN6G(177, 6835, 0),
 CHAN6G(181, 6855, 0),
 CHAN6G(185, 6875, 0),
 CHAN6G(189, 6895, 0),
 CHAN6G(193, 6915, 0),
 CHAN6G(197, 6935, 0),
 CHAN6G(201, 6955, 0),
 CHAN6G(205, 6975, 0),
 CHAN6G(209, 6995, 0),
 CHAN6G(213, 7015, 0),
 CHAN6G(217, 7035, 0),
 CHAN6G(221, 7055, 0),
 CHAN6G(225, 7075, 0),
 CHAN6G(229, 7095, 0),
 CHAN6G(233, 7115, 0),

 /* new addition in IEEE Std 802.11ax-2021 */
 CHAN6G(2, 5935, 0),
};

static struct ieee80211_rate ath11k_legacy_rates[] = {
 { .bitrate = 10,
   .hw_value = ATH11K_HW_RATE_CCK_LP_1M },
 { .bitrate = 20,
   .hw_value = ATH11K_HW_RATE_CCK_LP_2M,
   .hw_value_short = ATH11K_HW_RATE_CCK_SP_2M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 55,
   .hw_value = ATH11K_HW_RATE_CCK_LP_5_5M,
   .hw_value_short = ATH11K_HW_RATE_CCK_SP_5_5M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 110,
   .hw_value = ATH11K_HW_RATE_CCK_LP_11M,
   .hw_value_short = ATH11K_HW_RATE_CCK_SP_11M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },

 { .bitrate = 60, .hw_value = ATH11K_HW_RATE_OFDM_6M },
 { .bitrate = 90, .hw_value = ATH11K_HW_RATE_OFDM_9M },
 { .bitrate = 120, .hw_value = ATH11K_HW_RATE_OFDM_12M },
 { .bitrate = 180, .hw_value = ATH11K_HW_RATE_OFDM_18M },
 { .bitrate = 240, .hw_value = ATH11K_HW_RATE_OFDM_24M },
 { .bitrate = 360, .hw_value = ATH11K_HW_RATE_OFDM_36M },
 { .bitrate = 480, .hw_value = ATH11K_HW_RATE_OFDM_48M },
 { .bitrate = 540, .hw_value = ATH11K_HW_RATE_OFDM_54M },
};

static const int
ath11k_phymodes[NUM_NL80211_BANDS][ATH11K_CHAN_WIDTH_NUM] = {
 [NL80211_BAND_2GHZ] = {
   [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20_2G,
   [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20_2G,
   [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40_2G,
   [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80_2G,
   [NL80211_CHAN_WIDTH_80P80] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_160] = MODE_UNKNOWN,
 },
 [NL80211_BAND_5GHZ] = {
   [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20,
   [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20,
   [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40,
   [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80,
   [NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160,
   [NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80,
 },
 [NL80211_BAND_6GHZ] = {
   [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20,
   [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20,
   [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40,
   [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80,
   [NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160,
   [NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80,
 },

};

const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default = {
 .rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START |
       HTT_RX_FILTER_TLV_FLAGS_PPDU_END |
       HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE,
 .pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0,
 .pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1,
 .pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2,
 .pkt_filter_flags3 = HTT_RX_FP_DATA_FILTER_FLASG3 |
        HTT_RX_FP_CTRL_FILTER_FLASG3
};

#define ATH11K_MAC_FIRST_OFDM_RATE_IDX 4
#define ath11k_g_rates ath11k_legacy_rates
#define ath11k_g_rates_size (ARRAY_SIZE(ath11k_legacy_rates))
#define ath11k_a_rates (ath11k_legacy_rates + 4)
#define ath11k_a_rates_size (ARRAY_SIZE(ath11k_legacy_rates) - 4)

#define ATH11K_MAC_SCAN_CMD_EVT_OVERHEAD  200 /* in msecs */

/* Overhead due to the processing of channel switch events from FW */
#define ATH11K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD 10 /* in msecs */

static const u32 ath11k_smps_map[] = {
 [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC,
 [WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC,
 [WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE,
 [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
};

enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy)
{
 enum nl80211_he_ru_alloc ret;

 switch (ru_phy) {
 case RU_26:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_26;
  break;
 case RU_52:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_52;
  break;
 case RU_106:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_106;
  break;
 case RU_242:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_242;
  break;
 case RU_484:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_484;
  break;
 case RU_996:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_996;
  break;
 default:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_26;
  break;
 }

 return ret;
}

enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones)
{
 enum nl80211_he_ru_alloc ret;

 switch (ru_tones) {
 case 26:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_26;
  break;
 case 52:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_52;
  break;
 case 106:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_106;
  break;
 case 242:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_242;
  break;
 case 484:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_484;
  break;
 case 996:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_996;
  break;
 case (996 * 2):
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996;
  break;
 default:
  ret = NL80211_RATE_INFO_HE_RU_ALLOC_26;
  break;
 }

 return ret;
}

enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi)
{
 enum nl80211_he_gi ret;

 switch (sgi) {
 case RX_MSDU_START_SGI_0_8_US:
  ret = NL80211_RATE_INFO_HE_GI_0_8;
  break;
 case RX_MSDU_START_SGI_1_6_US:
  ret = NL80211_RATE_INFO_HE_GI_1_6;
  break;
 case RX_MSDU_START_SGI_3_2_US:
  ret = NL80211_RATE_INFO_HE_GI_3_2;
  break;
 default:
  ret = NL80211_RATE_INFO_HE_GI_0_8;
  break;
 }

 return ret;
}

u8 ath11k_mac_bw_to_mac80211_bw(u8 bw)
{
 u8 ret = 0;

 switch (bw) {
 case ATH11K_BW_20:
  ret = RATE_INFO_BW_20;
  break;
 case ATH11K_BW_40:
  ret = RATE_INFO_BW_40;
  break;
 case ATH11K_BW_80:
  ret = RATE_INFO_BW_80;
  break;
 case ATH11K_BW_160:
  ret = RATE_INFO_BW_160;
  break;
 }

 return ret;
}

enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw)
{
 switch (bw) {
 case RATE_INFO_BW_20:
  return ATH11K_BW_20;
 case RATE_INFO_BW_40:
  return ATH11K_BW_40;
 case RATE_INFO_BW_80:
  return ATH11K_BW_80;
 case RATE_INFO_BW_160:
  return ATH11K_BW_160;
 default:
  return ATH11K_BW_20;
 }
}

int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx,
       u16 *rate)
{
 /* As default, it is OFDM rates */
 int i = ATH11K_MAC_FIRST_OFDM_RATE_IDX;
 int max_rates_idx = ath11k_g_rates_size;

 if (preamble == WMI_RATE_PREAMBLE_CCK) {
  hw_rc &= ~ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK;
  i = 0;
  max_rates_idx = ATH11K_MAC_FIRST_OFDM_RATE_IDX;
 }

 while (i < max_rates_idx) {
  if (hw_rc == ath11k_legacy_rates[i].hw_value) {
   *rateidx = i;
   *rate = ath11k_legacy_rates[i].bitrate;
   return 0;
  }
  i++;
 }

 return -EINVAL;
}

static int get_num_chains(u32 mask)
{
 int num_chains = 0;

 while (mask) {
  if (mask & BIT(0))
   num_chains++;
  mask >>= 1;
 }

 return num_chains;
}

u8 ath11k_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 u32
ath11k_mac_max_ht_nss(const u8 *ht_mcs_mask)
{
 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
ath11k_mac_max_vht_nss(const u16 *vht_mcs_mask)
{
 int nss;

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

 return 1;
}

static u32
ath11k_mac_max_he_nss(const u16 *he_mcs_mask)
{
 int nss;

 for (nss = NL80211_HE_NSS_MAX - 1; nss >= 0; nss--)
  if (he_mcs_mask[nss])
   return nss + 1;

 return 1;
}

static u8 ath11k_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;
 }
}

static int ath11k_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 bool ath11k_mac_bitrate_is_cck(int bitrate)
{
 switch (bitrate) {
 case 10:
 case 20:
 case 55:
 case 110:
  return true;
 }

 return false;
}

u8 ath11k_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 (ath11k_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;
}

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

static void ath11k_get_arvif_iter(void *data, u8 *mac,
      struct ieee80211_vif *vif)
{
 struct ath11k_vif_iter *arvif_iter = data;
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);

 if (arvif->vdev_id == arvif_iter->vdev_id)
  arvif_iter->arvif = arvif;
}

struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id)
{
 struct ath11k_vif_iter arvif_iter;
 u32 flags;

 memset(&arvif_iter, 0, sizeof(struct ath11k_vif_iter));
 arvif_iter.vdev_id = vdev_id;

 flags = IEEE80211_IFACE_ITER_RESUME_ALL;
 ieee80211_iterate_active_interfaces_atomic(ar->hw,
         flags,
         ath11k_get_arvif_iter,
         &arvif_iter);
 if (!arvif_iter.arvif) {
  ath11k_warn(ar->ab, "No VIF found for vdev %d\n", vdev_id);
  return NULL;
 }

 return arvif_iter.arvif;
}

struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab,
         u32 vdev_id)
{
 int i;
 struct ath11k_pdev *pdev;
 struct ath11k_vif *arvif;

 for (i = 0; i < ab->num_radios; i++) {
  pdev = rcu_dereference(ab->pdevs_active[i]);
  if (pdev && pdev->ar &&
      (pdev->ar->allocated_vdev_map & (1LL << vdev_id))) {
   arvif = ath11k_mac_get_arvif(pdev->ar, vdev_id);
   if (arvif)
    return arvif;
  }
 }

 return NULL;
}

struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id)
{
 int i;
 struct ath11k_pdev *pdev;

 for (i = 0; i < ab->num_radios; i++) {
  pdev = rcu_dereference(ab->pdevs_active[i]);
  if (pdev && pdev->ar) {
   if (pdev->ar->allocated_vdev_map & (1LL << vdev_id))
    return pdev->ar;
  }
 }

 return NULL;
}

struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id)
{
 int i;
 struct ath11k_pdev *pdev;

 if (ab->hw_params.single_pdev_only) {
  pdev = rcu_dereference(ab->pdevs_active[0]);
  return pdev ? pdev->ar : NULL;
 }

 if (WARN_ON(pdev_id > ab->num_radios))
  return NULL;

 for (i = 0; i < ab->num_radios; i++) {
  if (ab->fw_mode == ATH11K_FIRMWARE_MODE_FTM)
   pdev = &ab->pdevs[i];
  else
   pdev = rcu_dereference(ab->pdevs_active[i]);

  if (pdev && pdev->pdev_id == pdev_id)
   return (pdev->ar ? pdev->ar : NULL);
 }

 return NULL;
}

struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab)
{
 struct ath11k *ar;
 struct ath11k_pdev *pdev;
 struct ath11k_vif *arvif;
 int i;

 for (i = 0; i < ab->num_radios; i++) {
  pdev = &ab->pdevs[i];
  ar = pdev->ar;
  list_for_each_entry(arvif, &ar->arvifs, list) {
   if (arvif->is_up)
    return arvif;
  }
 }

 return NULL;
}

static bool ath11k_mac_band_match(enum nl80211_band band1, enum WMI_HOST_WLAN_BAND band2)
{
 return (((band1 == NL80211_BAND_2GHZ) && (band2 & WMI_HOST_WLAN_2G_CAP)) ||
  (((band1 == NL80211_BAND_5GHZ) || (band1 == NL80211_BAND_6GHZ)) &&
     (band2 & WMI_HOST_WLAN_5G_CAP)));
}

u8 ath11k_mac_get_target_pdev_id_from_vif(struct ath11k_vif *arvif)
{
 struct ath11k *ar = arvif->ar;
 struct ath11k_base *ab = ar->ab;
 struct ieee80211_vif *vif = arvif->vif;
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 u8 pdev_id = ab->target_pdev_ids[0].pdev_id;
 int i;

 if (WARN_ON(ath11k_mac_vif_chan(vif, &def)))
  return pdev_id;

 band = def.chan->band;

 for (i = 0; i < ab->target_pdev_count; i++) {
  if (ath11k_mac_band_match(band, ab->target_pdev_ids[i].supported_bands))
   return ab->target_pdev_ids[i].pdev_id;
 }

 return pdev_id;
}

u8 ath11k_mac_get_target_pdev_id(struct ath11k *ar)
{
 struct ath11k_vif *arvif;

 arvif = ath11k_mac_get_vif_up(ar->ab);

 if (arvif)
  return ath11k_mac_get_target_pdev_id_from_vif(arvif);
 else
  return ar->ab->target_pdev_ids[0].pdev_id;
}

static void ath11k_pdev_caps_update(struct ath11k *ar)
{
 struct ath11k_base *ab = ar->ab;

 ar->max_tx_power = ab->target_caps.hw_max_tx_power;

 /* FIXME Set min_tx_power to ab->target_caps.hw_min_tx_power.
 * But since the received value in svcrdy is same as hw_max_tx_power,
 * we can set ar->min_tx_power to 0 currently until
 * this is fixed in firmware
 */

 ar->min_tx_power = 0;

 ar->txpower_limit_2g = ar->max_tx_power;
 ar->txpower_limit_5g = ar->max_tx_power;
 ar->txpower_scale = WMI_HOST_TP_SCALE_MAX;
}

static int ath11k_mac_txpower_recalc(struct ath11k *ar)
{
 struct ath11k_pdev *pdev = ar->pdev;
 struct ath11k_vif *arvif;
 int ret, txpower = -1;
 u32 param;

 lockdep_assert_held(&ar->conf_mutex);

 list_for_each_entry(arvif, &ar->arvifs, list) {
  if (arvif->txpower <= 0)
   continue;

  if (txpower == -1)
   txpower = arvif->txpower;
  else
   txpower = min(txpower, arvif->txpower);
 }

 if (txpower == -1)
  return 0;

 /* txpwr is set as 2 units per dBm in FW*/
 txpower = min_t(u32, max_t(u32, ar->min_tx_power, txpower),
   ar->max_tx_power) * 2;

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower to set in hw %d\n",
     txpower / 2);

 if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) &&
     ar->txpower_limit_2g != txpower) {
  param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G;
  ret = ath11k_wmi_pdev_set_param(ar, param,
      txpower, ar->pdev->pdev_id);
  if (ret)
   goto fail;
  ar->txpower_limit_2g = txpower;
 }

 if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) &&
     ar->txpower_limit_5g != txpower) {
  param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G;
  ret = ath11k_wmi_pdev_set_param(ar, param,
      txpower, ar->pdev->pdev_id);
  if (ret)
   goto fail;
  ar->txpower_limit_5g = txpower;
 }

 return 0;

fail:
 ath11k_warn(ar->ab, "failed to recalc txpower limit %d using pdev param %d: %d\n",
      txpower / 2, param, ret);
 return ret;
}

static int ath11k_recalc_rtscts_prot(struct ath11k_vif *arvif)
{
 struct ath11k *ar = arvif->ar;
 u32 vdev_param, rts_cts = 0;
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 vdev_param = WMI_VDEV_PARAM_ENABLE_RTSCTS;

 /* Enable RTS/CTS protection for sw retries (when legacy stations
 * are in BSS) or by default only for second rate series.
 * TODO: Check if we need to enable CTS 2 Self in any case
 */

 rts_cts = WMI_USE_RTS_CTS;

 if (arvif->num_legacy_stations > 0)
  rts_cts |= WMI_RTSCTS_ACROSS_SW_RETRIES << 4;
 else
  rts_cts |= WMI_RTSCTS_FOR_SECOND_RATESERIES << 4;

 /* Need not send duplicate param value to firmware */
 if (arvif->rtscts_prot_mode == rts_cts)
  return 0;

 arvif->rtscts_prot_mode = rts_cts;

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %d recalc rts/cts prot %d\n",
     arvif->vdev_id, rts_cts);

 ret =  ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
          vdev_param, rts_cts);
 if (ret)
  ath11k_warn(ar->ab, "failed to recalculate rts/cts prot for vdev %d: %d\n",
       arvif->vdev_id, ret);

 return ret;
}

static int ath11k_mac_set_kickout(struct ath11k_vif *arvif)
{
 struct ath11k *ar = arvif->ar;
 u32 param;
 int ret;

 ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_STA_KICKOUT_TH,
     ATH11K_KICKOUT_THRESHOLD,
     ar->pdev->pdev_id);
 if (ret) {
  ath11k_warn(ar->ab, "failed to set kickout threshold on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 param = WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS;
 ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
         ATH11K_KEEPALIVE_MIN_IDLE);
 if (ret) {
  ath11k_warn(ar->ab, "failed to set keepalive minimum idle time on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS;
 ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
         ATH11K_KEEPALIVE_MAX_IDLE);
 if (ret) {
  ath11k_warn(ar->ab, "failed to set keepalive maximum idle time on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS;
 ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
         ATH11K_KEEPALIVE_MAX_UNRESPONSIVE);
 if (ret) {
  ath11k_warn(ar->ab, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

void ath11k_mac_peer_cleanup_all(struct ath11k *ar)
{
 struct ath11k_peer *peer, *tmp;
 struct ath11k_base *ab = ar->ab;

 lockdep_assert_held(&ar->conf_mutex);

 mutex_lock(&ab->tbl_mtx_lock);
 spin_lock_bh(&ab->base_lock);
 list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
  ath11k_peer_rx_tid_cleanup(ar, peer);
  ath11k_peer_rhash_delete(ab, peer);
  list_del(&peer->list);
  kfree(peer);
 }
 spin_unlock_bh(&ab->base_lock);
 mutex_unlock(&ab->tbl_mtx_lock);

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

static inline int ath11k_mac_vdev_setup_sync(struct ath11k *ar)
{
 lockdep_assert_held(&ar->conf_mutex);

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

 if (!wait_for_completion_timeout(&ar->vdev_setup_done,
      ATH11K_VDEV_SETUP_TIMEOUT_HZ))
  return -ETIMEDOUT;

 return ar->last_wmi_vdev_start_status ? -EINVAL : 0;
}

static void
ath11k_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 int ath11k_mac_monitor_vdev_start(struct ath11k *ar, int vdev_id,
      struct cfg80211_chan_def *chandef)
{
 struct ieee80211_channel *channel;
 struct wmi_vdev_start_req_arg arg = {};
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 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;

 arg.channel.mode = ath11k_phymodes[chandef->chan->band][chandef->width];
 arg.channel.chan_radar = !!(channel->flags & IEEE80211_CHAN_RADAR);

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

 arg.pref_tx_streams = ar->num_tx_chains;
 arg.pref_rx_streams = ar->num_rx_chains;

 arg.channel.passive = !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR);

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

 ret = ath11k_wmi_vdev_start(ar, &arg, false);
 if (ret) {
  ath11k_warn(ar->ab, "failed to request monitor vdev %i start: %d\n",
       vdev_id, ret);
  return ret;
 }

 ret = ath11k_mac_vdev_setup_sync(ar);
 if (ret) {
  ath11k_warn(ar->ab, "failed to synchronize setup for monitor vdev %i start: %d\n",
       vdev_id, ret);
  return ret;
 }

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

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "monitor vdev %i started\n",
     vdev_id);

 return 0;

vdev_stop:
 reinit_completion(&ar->vdev_setup_done);

 ret = ath11k_wmi_vdev_stop(ar, vdev_id);
 if (ret) {
  ath11k_warn(ar->ab, "failed to stop monitor vdev %i after start failure: %d\n",
       vdev_id, ret);
  return ret;
 }

 ret = ath11k_mac_vdev_setup_sync(ar);
 if (ret) {
  ath11k_warn(ar->ab, "failed to synchronize setup for vdev %i stop: %d\n",
       vdev_id, ret);
  return ret;
 }

 return -EIO;
}

static int ath11k_mac_monitor_vdev_stop(struct ath11k *ar)
{
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 reinit_completion(&ar->vdev_setup_done);

 ret = ath11k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
 if (ret) {
  ath11k_warn(ar->ab, "failed to request monitor vdev %i stop: %d\n",
       ar->monitor_vdev_id, ret);
  return ret;
 }

 ret = ath11k_mac_vdev_setup_sync(ar);
 if (ret) {
  ath11k_warn(ar->ab, "failed to synchronize monitor vdev %i stop: %d\n",
       ar->monitor_vdev_id, ret);
  return ret;
 }

 ret = ath11k_wmi_vdev_down(ar, ar->monitor_vdev_id);
 if (ret) {
  ath11k_warn(ar->ab, "failed to put down monitor vdev %i: %d\n",
       ar->monitor_vdev_id, ret);
  return ret;
 }

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "monitor vdev %i stopped\n",
     ar->monitor_vdev_id);

 return 0;
}

static int ath11k_mac_monitor_vdev_create(struct ath11k *ar)
{
 struct ath11k_pdev *pdev = ar->pdev;
 struct vdev_create_params param = {};
 int bit, ret;
 u8 tmp_addr[6] = {};
 u16 nss;

 lockdep_assert_held(&ar->conf_mutex);

 if (test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags))
  return 0;

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

 bit = __ffs64(ar->ab->free_vdev_map);

 ar->monitor_vdev_id = bit;

 param.if_id = ar->monitor_vdev_id;
 param.type = WMI_VDEV_TYPE_MONITOR;
 param.subtype = WMI_VDEV_SUBTYPE_NONE;
 param.pdev_id = pdev->pdev_id;

 if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) {
  param.chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains;
  param.chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains;
 }
 if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) {
  param.chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains;
  param.chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains;
 }

 ret = ath11k_wmi_vdev_create(ar, tmp_addr, ¶m);
 if (ret) {
  ath11k_warn(ar->ab, "failed to request monitor vdev %i creation: %d\n",
       ar->monitor_vdev_id, ret);
  ar->monitor_vdev_id = -1;
  return ret;
 }

 nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1;
 ret = ath11k_wmi_vdev_set_param_cmd(ar, ar->monitor_vdev_id,
         WMI_VDEV_PARAM_NSS, nss);
 if (ret) {
  ath11k_warn(ar->ab, "failed to set vdev %d chainmask 0x%x, nss %d :%d\n",
       ar->monitor_vdev_id, ar->cfg_tx_chainmask, nss, ret);
  goto err_vdev_del;
 }

 ret = ath11k_mac_txpower_recalc(ar);
 if (ret) {
  ath11k_warn(ar->ab, "failed to recalc txpower for monitor vdev %d: %d\n",
       ar->monitor_vdev_id, ret);
  goto err_vdev_del;
 }

 ar->allocated_vdev_map |= 1LL << ar->monitor_vdev_id;
 ar->ab->free_vdev_map &= ~(1LL << ar->monitor_vdev_id);
 ar->num_created_vdevs++;
 set_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags);

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "monitor vdev %d created\n",
     ar->monitor_vdev_id);

 return 0;

err_vdev_del:
 ath11k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
 ar->monitor_vdev_id = -1;
 return ret;
}

static int ath11k_mac_monitor_vdev_delete(struct ath11k *ar)
{
 int ret;
 unsigned long time_left;

 lockdep_assert_held(&ar->conf_mutex);

 if (!test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags))
  return 0;

 reinit_completion(&ar->vdev_delete_done);

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

 time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
      ATH11K_VDEV_DELETE_TIMEOUT_HZ);
 if (time_left == 0) {
  ath11k_warn(ar->ab, "Timeout in receiving vdev delete response\n");
 } else {
  ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "monitor vdev %d deleted\n",
      ar->monitor_vdev_id);

  ar->allocated_vdev_map &= ~(1LL << ar->monitor_vdev_id);
  ar->ab->free_vdev_map |= 1LL << (ar->monitor_vdev_id);
  ar->num_created_vdevs--;
  ar->monitor_vdev_id = -1;
  clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags);
 }

 return ret;
}

static int ath11k_mac_monitor_start(struct ath11k *ar)
{
 struct cfg80211_chan_def *chandef = NULL;
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags))
  return 0;

 ieee80211_iter_chan_contexts_atomic(ar->hw,
         ath11k_mac_get_any_chandef_iter,
         &chandef);
 if (!chandef)
  return 0;

 ret = ath11k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id, chandef);
 if (ret) {
  ath11k_warn(ar->ab, "failed to start monitor vdev: %d\n", ret);
  ath11k_mac_monitor_vdev_delete(ar);
  return ret;
 }

 set_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags);

 ar->num_started_vdevs++;
 ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, false);
 if (ret) {
  ath11k_warn(ar->ab, "failed to configure htt monitor mode ring during start: %d",
       ret);
  return ret;
 }

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "monitor started\n");

 return 0;
}

static int ath11k_mac_monitor_stop(struct ath11k *ar)
{
 int ret;

 lockdep_assert_held(&ar->conf_mutex);

 if (!test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags))
  return 0;

 ret = ath11k_mac_monitor_vdev_stop(ar);
 if (ret) {
  ath11k_warn(ar->ab, "failed to stop monitor vdev: %d\n", ret);
  return ret;
 }

 clear_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags);
 ar->num_started_vdevs--;

 ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, true);
 if (ret) {
  ath11k_warn(ar->ab, "failed to configure htt monitor mode ring during stop: %d",
       ret);
  return ret;
 }

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "monitor stopped ret %d\n", ret);

 return 0;
}

static int ath11k_mac_vif_setup_ps(struct ath11k_vif *arvif)
{
 struct ath11k *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 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) {
  psmode = WMI_STA_PS_MODE_ENABLED;
  param = WMI_STA_PS_PARAM_INACTIVITY_TIME;

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

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

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %d psmode %s\n",
     arvif->vdev_id, psmode ? "enable" : "disable");

 ret = ath11k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, psmode);
 if (ret) {
  ath11k_warn(ar->ab, "failed to set sta power save mode %d for vdev %d: %d\n",
       psmode, arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

static int ath11k_mac_config_ps(struct ath11k *ar)
{
 struct ath11k_vif *arvif;
 int ret = 0;

 lockdep_assert_held(&ar->conf_mutex);

 list_for_each_entry(arvif, &ar->arvifs, list) {
  ret = ath11k_mac_vif_setup_ps(arvif);
  if (ret) {
   ath11k_warn(ar->ab, "failed to setup powersave: %d\n", ret);
   break;
  }
 }

 return ret;
}

static int ath11k_mac_op_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
{
 struct ath11k *ar = hw->priv;
 struct ieee80211_conf *conf = &hw->conf;
 int ret = 0;

 mutex_lock(&ar->conf_mutex);

 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
  if (conf->flags & IEEE80211_CONF_MONITOR) {
   set_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags);

   if (test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED,
         &ar->monitor_flags))
    goto out;

   ret = ath11k_mac_monitor_vdev_create(ar);
   if (ret) {
    ath11k_warn(ar->ab, "failed to create monitor vdev: %d",
         ret);
    goto out;
   }

   ret = ath11k_mac_monitor_start(ar);
   if (ret) {
    ath11k_warn(ar->ab, "failed to start monitor: %d",
         ret);
    goto err_mon_del;
   }
  } else {
   clear_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags);

   if (!test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED,
          &ar->monitor_flags))
    goto out;

   ret = ath11k_mac_monitor_stop(ar);
   if (ret) {
    ath11k_warn(ar->ab, "failed to stop monitor: %d",
         ret);
    goto out;
   }

   ret = ath11k_mac_monitor_vdev_delete(ar);
   if (ret) {
    ath11k_warn(ar->ab, "failed to delete monitor vdev: %d",
         ret);
    goto out;
   }
  }
 }

out:
 mutex_unlock(&ar->conf_mutex);
 return ret;

err_mon_del:
 ath11k_mac_monitor_vdev_delete(ar);
 mutex_unlock(&ar->conf_mutex);
 return ret;
}

static void ath11k_mac_setup_nontx_vif_rsnie(struct ath11k_vif *arvif,
          bool tx_arvif_rsnie_present,
          const u8 *profile, u8 profile_len)
{
 if (cfg80211_find_ie(WLAN_EID_RSN, profile, profile_len)) {
  arvif->rsnie_present = true;
 } else if (tx_arvif_rsnie_present) {
  int i;
  u8 nie_len;
  const u8 *nie = cfg80211_find_ext_ie(WLAN_EID_EXT_NON_INHERITANCE,
           profile, profile_len);
  if (!nie)
   return;

  nie_len = nie[1];
  nie += 2;
  for (i = 0; i < nie_len; i++) {
   if (nie[i] == WLAN_EID_RSN) {
    arvif->rsnie_present = false;
    break;
   }
  }
 }
}

static bool ath11k_mac_set_nontx_vif_params(struct ath11k_vif *tx_arvif,
         struct ath11k_vif *arvif,
         struct sk_buff *bcn)
{
 struct ieee80211_mgmt *mgmt;
 const u8 *ies, *profile, *next_profile;
 int ies_len;

 ies = bcn->data + ieee80211_get_hdrlen_from_skb(bcn);
 mgmt = (struct ieee80211_mgmt *)bcn->data;
 ies += sizeof(mgmt->u.beacon);
 ies_len = skb_tail_pointer(bcn) - ies;

 ies = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ies, ies_len);
 arvif->rsnie_present = tx_arvif->rsnie_present;

 while (ies) {
  u8 mbssid_len;

  ies_len -= (2 + ies[1]);
  mbssid_len = ies[1] - 1;
  profile = &ies[3];

  while (mbssid_len) {
   u8 profile_len;

   profile_len = profile[1];
   next_profile = profile + (2 + profile_len);
   mbssid_len -= (2 + profile_len);

   profile += 2;
   profile_len -= (2 + profile[1]);
   profile += (2 + profile[1]); /* nontx capabilities */
   profile_len -= (2 + profile[1]);
   profile += (2 + profile[1]); /* SSID */
   if (profile[2] == arvif->vif->bss_conf.bssid_index) {
    profile_len -= 5;
    profile = profile + 5;
    ath11k_mac_setup_nontx_vif_rsnie(arvif,
         tx_arvif->rsnie_present,
         profile,
         profile_len);
    return true;
   }
   profile = next_profile;
  }
  ies = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, profile,
           ies_len);
 }

 return false;
}

static int ath11k_mac_setup_bcn_p2p_ie(struct ath11k_vif *arvif,
           struct sk_buff *bcn)
{
 struct ath11k *ar = arvif->ar;
 struct ieee80211_mgmt *mgmt;
 const u8 *p2p_ie;
 int ret;

 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 = ath11k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie);
 if (ret) {
  ath11k_warn(ar->ab, "failed to submit P2P GO bcn ie for vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 return ret;
}

static int ath11k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui,
           u8 oui_type, size_t ie_offset)
{
 size_t len;
 const u8 *next, *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 ath11k_mac_set_vif_params(struct ath11k_vif *arvif,
         struct sk_buff *bcn)
{
 struct ath11k_base *ab = arvif->ar->ab;
 struct ieee80211_mgmt *mgmt;
 int ret = 0;
 u8 *ies;

 ies = bcn->data + ieee80211_get_hdrlen_from_skb(bcn);
 mgmt = (struct ieee80211_mgmt *)bcn->data;
 ies += sizeof(mgmt->u.beacon);

 if (cfg80211_find_ie(WLAN_EID_RSN, ies, (skb_tail_pointer(bcn) - ies)))
  arvif->rsnie_present = true;
 else
  arvif->rsnie_present = false;

 if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
        WLAN_OUI_TYPE_MICROSOFT_WPA,
        ies, (skb_tail_pointer(bcn) - ies)))
  arvif->wpaie_present = true;
 else
  arvif->wpaie_present = false;

 if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
  return ret;

 ret = ath11k_mac_setup_bcn_p2p_ie(arvif, bcn);
 if (ret) {
  ath11k_warn(ab, "failed to setup P2P GO bcn ie: %d\n",
       ret);
  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.
 */

 ret = ath11k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA,
       WLAN_OUI_TYPE_WFA_P2P,
       offsetof(struct ieee80211_mgmt,
         u.beacon.variable));
 if (ret) {
  ath11k_warn(ab, "failed to remove P2P vendor ie: %d\n",
       ret);
  return ret;
 }

 return ret;
}

static struct ath11k_vif *ath11k_mac_get_tx_arvif(struct ath11k_vif *arvif)
{
 struct ieee80211_bss_conf *link_conf, *tx_bss_conf;

 lockdep_assert_wiphy(arvif->ar->hw->wiphy);

 link_conf = &arvif->vif->bss_conf;
 tx_bss_conf = wiphy_dereference(arvif->ar->hw->wiphy, link_conf->tx_bss_conf);
 if (tx_bss_conf)
  return ath11k_vif_to_arvif(tx_bss_conf->vif);

 return NULL;
}

static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
      struct ath11k_vif *tx_arvif)
{
 struct ieee80211_ema_beacons *beacons;
 int ret = 0;
 bool nontx_vif_params_set = false;
 u32 params = 0;
 u8 i = 0;

 beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw,
        tx_arvif->vif, 0);
 if (!beacons || !beacons->cnt) {
  ath11k_warn(arvif->ar->ab,
       "failed to get ema beacon templates from mac80211\n");
  return -EPERM;
 }

 if (tx_arvif == arvif) {
  if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb))
   return -EINVAL;
 } else {
  arvif->wpaie_present = tx_arvif->wpaie_present;
 }

 for (i = 0; i < beacons->cnt; i++) {
  if (tx_arvif != arvif && !nontx_vif_params_set)
   nontx_vif_params_set =
    ath11k_mac_set_nontx_vif_params(tx_arvif, arvif,
        beacons->bcn[i].skb);

  params = beacons->cnt;
  params |= (i << WMI_EMA_TMPL_IDX_SHIFT);
  params |= ((!i ? 1 : 0) << WMI_EMA_FIRST_TMPL_SHIFT);
  params |= ((i + 1 == beacons->cnt ? 1 : 0) << WMI_EMA_LAST_TMPL_SHIFT);

  ret = ath11k_wmi_bcn_tmpl(tx_arvif->ar, tx_arvif->vdev_id,
       &beacons->bcn[i].offs,
       beacons->bcn[i].skb, params);
  if (ret) {
   ath11k_warn(tx_arvif->ar->ab,
        "failed to set ema beacon template id %i error %d\n",
        i, ret);
   break;
  }
 }

 ieee80211_beacon_free_ema_list(beacons);

 if (tx_arvif != arvif && !nontx_vif_params_set)
  return -EINVAL; /* Profile not found in the beacons */

 return ret;
}

static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
         struct ath11k_vif *tx_arvif)
{
 struct ath11k *ar = arvif->ar;
 struct ath11k_base *ab = ar->ab;
 struct ieee80211_hw *hw = ar->hw;
 struct ieee80211_vif *vif = arvif->vif;
 struct ieee80211_mutable_offsets offs = {};
 struct sk_buff *bcn;
 int ret;

 if (tx_arvif != arvif) {
  ar = tx_arvif->ar;
  ab = ar->ab;
  hw = ar->hw;
  vif = tx_arvif->vif;
 }

 bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0);
 if (!bcn) {
  ath11k_warn(ab, "failed to get beacon template from mac80211\n");
  return -EPERM;
 }

 if (tx_arvif == arvif) {
  if (ath11k_mac_set_vif_params(tx_arvif, bcn))
   return -EINVAL;
 } else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
  return -EINVAL;
 }

 ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
 kfree_skb(bcn);

 if (ret)
  ath11k_warn(ab, "failed to submit beacon template command: %d\n",
       ret);

 return ret;
}

static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
{
 struct ieee80211_vif *vif = arvif->vif;
 struct ath11k_vif *tx_arvif;

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

 /* Target does not expect beacon templates for the already up
 * non-transmitting interfaces, and results in a crash if sent.
 */

 tx_arvif = ath11k_mac_get_tx_arvif(arvif);
 if (tx_arvif) {
  if (arvif != tx_arvif && arvif->is_up)
   return 0;

  if (vif->bss_conf.ema_ap)
   return ath11k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif);
 } else {
  tx_arvif = arvif;
 }

 return ath11k_mac_setup_bcn_tmpl_mbssid(arvif, tx_arvif);
}

void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif)
{
 struct ieee80211_vif *vif = arvif->vif;

 if (!vif->bss_conf.color_change_active && !arvif->bcca_zero_sent)
  return;

 if (vif->bss_conf.color_change_active &&
     ieee80211_beacon_cntdwn_is_complete(vif, 0)) {
  arvif->bcca_zero_sent = true;
  ieee80211_color_change_finish(vif, 0);
  return;
 }

 arvif->bcca_zero_sent = false;

 if (vif->bss_conf.color_change_active)
  ieee80211_beacon_update_cntdwn(vif, 0);
 ath11k_mac_setup_bcn_tmpl(arvif);
}

static void ath11k_control_beaconing(struct ath11k_vif *arvif,
         struct ieee80211_bss_conf *info)
{
 struct ath11k *ar = arvif->ar;
 struct ath11k_vif *tx_arvif;
 int ret = 0;

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

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

  arvif->is_up = false;
  return;
 }

 /* Install the beacon template to the FW */
 ret = ath11k_mac_setup_bcn_tmpl(arvif);
 if (ret) {
  ath11k_warn(ar->ab, "failed to update bcn tmpl during vdev up: %d\n",
       ret);
  return;
 }

 arvif->aid = 0;

 ether_addr_copy(arvif->bssid, info->bssid);

 tx_arvif = ath11k_mac_get_tx_arvif(arvif);
 ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
     arvif->bssid,
     tx_arvif ? tx_arvif->bssid : NULL,
     info->bssid_index,
     1 << info->bssid_indicator);
 if (ret) {
  ath11k_warn(ar->ab, "failed to bring up vdev %d: %i\n",
       arvif->vdev_id, ret);
  return;
 }

 arvif->is_up = true;

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %d up\n", arvif->vdev_id);
}

static void ath11k_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 ath11k_vif *arvif = ath11k_vif_to_arvif(vif);

 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 ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb)
{
 ieee80211_iterate_active_interfaces_atomic(ar->hw,
         IEEE80211_IFACE_ITER_NORMAL,
         ath11k_mac_handle_beacon_iter,
         skb);
}

static void ath11k_mac_handle_beacon_miss_iter(void *data, u8 *mac,
            struct ieee80211_vif *vif)
{
 u32 *vdev_id = data;
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 struct ath11k *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,
         ATH11K_CONNECTION_LOSS_HZ);
}

void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id)
{
 ieee80211_iterate_active_interfaces_atomic(ar->hw,
         IEEE80211_IFACE_ITER_NORMAL,
         ath11k_mac_handle_beacon_miss_iter,
         &vdev_id);
}

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

 if (!arvif->is_up)
  return;

 ieee80211_connection_loss(vif);
}

static void ath11k_peer_assoc_h_basic(struct ath11k *ar,
          struct ieee80211_vif *vif,
          struct ieee80211_sta *sta,
          struct peer_assoc_params *arg)
{
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 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->peer_mac, sta->addr);
 arg->vdev_id = arvif->vdev_id;
 arg->peer_associd = aid;
 arg->auth_flag = true;
 /* TODO: STA WAR in ath10k for listen interval required? */
 arg->peer_listen_intval = ar->hw->conf.listen_interval;
 arg->peer_nss = 1;
 arg->peer_caps = vif->bss_conf.assoc_capability;
}

static void ath11k_peer_assoc_h_crypto(struct ath11k *ar,
           struct ieee80211_vif *vif,
           struct ieee80211_sta *sta,
           struct peer_assoc_params *arg)
{
 struct ieee80211_bss_conf *info = &vif->bss_conf;
 struct cfg80211_chan_def def;
 struct cfg80211_bss *bss;
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 const u8 *rsnie = NULL;
 const u8 *wpaie = NULL;

 lockdep_assert_held(&ar->conf_mutex);

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

 bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid, NULL, 0,
          IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);

 if (arvif->rsnie_present || arvif->wpaie_present) {
  arg->need_ptk_4_way = true;
  if (arvif->wpaie_present)
   arg->need_gtk_2_way = true;
 } else 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) {
  ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
      "%s: rsn ie found\n", __func__);
  arg->need_ptk_4_way = true;
 }

 if (wpaie) {
  ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
      "%s: wpa ie found\n", __func__);
  arg->need_gtk_2_way = true;
 }

 if (sta->mfp) {
  /* TODO: Need to check if FW supports PMF? */
  arg->is_pmf_enabled = true;
 }

 /* TODO: safe_mode_enabled (bypass 4-way handshake) flag req? */
}

static void ath11k_peer_assoc_h_rates(struct ath11k *ar,
          struct ieee80211_vif *vif,
          struct ieee80211_sta *sta,
          struct peer_assoc_params *arg)
{
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 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(ath11k_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 = ath11k_mac_bitrate_to_rate(rates->bitrate);
  rateset->rates[rateset->num_rates] = rate;
  rateset->num_rates++;
 }
}

static bool
ath11k_peer_assoc_h_ht_masked(const u8 *ht_mcs_mask)
{
 int nss;

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

 return true;
}

static bool
ath11k_peer_assoc_h_vht_masked(const u16 *vht_mcs_mask)
{
 int nss;

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

 return true;
}

static void ath11k_peer_assoc_h_ht(struct ath11k *ar,
       struct ieee80211_vif *vif,
       struct ieee80211_sta *sta,
       struct peer_assoc_params *arg)
{
 const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 const u8 *ht_mcs_mask;
 int i, n;
 u8 max_nss;
 u32 stbc;

 lockdep_assert_held(&ar->conf_mutex);

 if (WARN_ON(ath11k_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;

 if (ath11k_peer_assoc_h_ht_masked(ht_mcs_mask))
  return;

 arg->ht_flag = true;

 arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
        ht_cap->ampdu_factor)) - 1;

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

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

 if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
  arg->ldpc_flag = true;

 if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) {
  arg->bw_40 = true;
  arg->peer_rate_caps |= WMI_HOST_RC_CW40_FLAG;
 }

 /* As firmware handles this two flags (IEEE80211_HT_CAP_SGI_20
 * and IEEE80211_HT_CAP_SGI_40) for enabling SGI, we reset
 * both flags if guard interval is Default GI
 */

 if (arvif->bitrate_mask.control[band].gi == NL80211_TXRATE_DEFAULT_GI)
  arg->peer_ht_caps &= ~(IEEE80211_HT_CAP_SGI_20 |
    IEEE80211_HT_CAP_SGI_40);

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

 if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
  arg->peer_rate_caps |= WMI_HOST_RC_TX_STBC_FLAG;
  arg->stbc_flag = true;
 }

 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_HOST_RC_RX_STBC_FLAG_S;
  arg->peer_rate_caps |= stbc;
  arg->stbc_flag = true;
 }

 if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
  arg->peer_rate_caps |= WMI_HOST_RC_TS_FLAG;
 else if (ht_cap->mcs.rx_mask[1])
  arg->peer_rate_caps |= WMI_HOST_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_nss = min(sta->deflink.rx_nss, max_nss);
 }

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "ht peer %pM mcs cnt %d nss %d\n",
     arg->peer_mac,
     arg->peer_ht_rates.num_rates,
     arg->peer_nss);
}

static int ath11k_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 u16
ath11k_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 = ath11k_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:
  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;
  default:
   WARN_ON(1);
   fallthrough;
  case -1:
   mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED;
   break;
  }

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

 return tx_mcs_set;
}

static u8 ath11k_get_nss_160mhz(struct ath11k *ar,
    u8 max_nss)
{
 u8 nss_ratio_info = ar->pdev->cap.nss_ratio_info;
 u8 max_sup_nss = 0;

 switch (nss_ratio_info) {
 case WMI_NSS_RATIO_1BY2_NSS:
  max_sup_nss = max_nss >> 1;
  break;
 case WMI_NSS_RATIO_3BY4_NSS:
  ath11k_warn(ar->ab, "WMI_NSS_RATIO_3BY4_NSS not supported\n");
  break;
 case WMI_NSS_RATIO_1_NSS:
  max_sup_nss = max_nss;
  break;
 case WMI_NSS_RATIO_2_NSS:
  ath11k_warn(ar->ab, "WMI_NSS_RATIO_2_NSS not supported\n");
  break;
 default:
  ath11k_warn(ar->ab, "invalid nss ratio received from firmware: %d\n",
       nss_ratio_info);
  break;
 }

 return max_sup_nss;
}

static void ath11k_peer_assoc_h_vht(struct ath11k *ar,
        struct ieee80211_vif *vif,
        struct ieee80211_sta *sta,
        struct peer_assoc_params *arg)
{
 const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 u16 *vht_mcs_mask;
 u8 ampdu_factor;
 u8 max_nss, vht_mcs;
 int i, vht_nss, nss_idx;
 bool user_rate_valid = true;
 u32 rx_nss, tx_nss, nss_160;

 if (WARN_ON(ath11k_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 (ath11k_peer_assoc_h_vht_masked(vht_mcs_mask))
  return;

 arg->vht_flag = true;

 /* TODO: similar flags required? */
 arg->vht_capable = true;

 if (def.chan->band == NL80211_BAND_2GHZ)
  arg->vht_ng_flag = true;

 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->bw_80 = true;

 if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
  arg->bw_160 = true;

 vht_nss =  ath11k_mac_max_vht_nss(vht_mcs_mask);

 if (vht_nss > sta->deflink.rx_nss) {
  user_rate_valid = false;
  for (nss_idx = sta->deflink.rx_nss - 1; nss_idx >= 0; nss_idx--) {
   if (vht_mcs_mask[nss_idx]) {
    user_rate_valid = true;
    break;
   }
  }
 }

 if (!user_rate_valid) {
  ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "setting vht range mcs value to peer supported nss %d for peer %pM\n",
      sta->deflink.rx_nss, sta->addr);
  vht_mcs_mask[sta->deflink.rx_nss - 1] = vht_mcs_mask[vht_nss - 1];
 }

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

 for (i = 0, max_nss = 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_nss = min(sta->deflink.rx_nss, max_nss);
 arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
 arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
 arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
 arg->tx_mcs_set = ath11k_peer_assoc_h_vht_limit(
  __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask);

 /* In IPQ8074 platform, VHT mcs rate 10 and 11 is enabled by default.
 * VHT mcs rate 10 and 11 is not supported in 11ac standard.
 * so explicitly disable the VHT MCS rate 10 and 11 in 11ac mode.
 */

 arg->tx_mcs_set &= ~IEEE80211_VHT_MCS_SUPPORT_0_11_MASK;
 arg->tx_mcs_set |= IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11;

 if ((arg->tx_mcs_set & IEEE80211_VHT_MCS_NOT_SUPPORTED) ==
   IEEE80211_VHT_MCS_NOT_SUPPORTED)
  arg->peer_vht_caps &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;

 /* TODO:  Check */
 arg->tx_max_mcs_nss = 0xFF;

 if (arg->peer_phymode == MODE_11AC_VHT160 ||
     arg->peer_phymode == MODE_11AC_VHT80_80) {
  tx_nss = ath11k_get_nss_160mhz(ar, max_nss);
  rx_nss = min(arg->peer_nss, tx_nss);
  arg->peer_bw_rxnss_override = ATH11K_BW_NSS_MAP_ENABLE;

  if (!rx_nss) {
   ath11k_warn(ar->ab, "invalid max_nss\n");
   return;
  }

  if (arg->peer_phymode == MODE_11AC_VHT160)
   nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_160MHZ, rx_nss - 1);
  else
   nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_80_80MHZ, rx_nss - 1);

  arg->peer_bw_rxnss_override |= nss_160;
 }

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
     "vht peer %pM max_mpdu %d flags 0x%x nss_override 0x%x\n",
     sta->addr, arg->peer_max_mpdu, arg->peer_flags,
     arg->peer_bw_rxnss_override);
}

static int ath11k_mac_get_max_he_mcs_map(u16 mcs_map, int nss)
{
 switch ((mcs_map >> (2 * nss)) & 0x3) {
 case IEEE80211_HE_MCS_SUPPORT_0_7: return BIT(8) - 1;
 case IEEE80211_HE_MCS_SUPPORT_0_9: return BIT(10) - 1;
 case IEEE80211_HE_MCS_SUPPORT_0_11: return BIT(12) - 1;
 }
 return 0;
}

static u16 ath11k_peer_assoc_h_he_limit(u16 tx_mcs_set,
     const u16 he_mcs_limit[NL80211_HE_NSS_MAX])
{
 int idx_limit;
 int nss;
 u16 mcs_map;
 u16 mcs;

 for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) {
  mcs_map = ath11k_mac_get_max_he_mcs_map(tx_mcs_set, nss) &
   he_mcs_limit[nss];

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

  switch (idx_limit) {
  case 0 ... 7:
   mcs = IEEE80211_HE_MCS_SUPPORT_0_7;
   break;
  case 8:
  case 9:
   mcs = IEEE80211_HE_MCS_SUPPORT_0_9;
   break;
  case 10:
  case 11:
   mcs = IEEE80211_HE_MCS_SUPPORT_0_11;
   break;
  default:
   WARN_ON(1);
   fallthrough;
  case -1:
   mcs = IEEE80211_HE_MCS_NOT_SUPPORTED;
   break;
  }

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

 return tx_mcs_set;
}

static bool
ath11k_peer_assoc_h_he_masked(const u16 *he_mcs_mask)
{
 int nss;

 for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++)
  if (he_mcs_mask[nss])
   return false;

 return true;
}

static void ath11k_peer_assoc_h_he(struct ath11k *ar,
       struct ieee80211_vif *vif,
       struct ieee80211_sta *sta,
       struct peer_assoc_params *arg)
{
 struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
 struct cfg80211_chan_def def;
 const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
 enum nl80211_band band;
 u16 he_mcs_mask[NL80211_HE_NSS_MAX];
 u8 max_nss, he_mcs;
 u16 he_tx_mcs = 0, v = 0;
 int i, he_nss, nss_idx;
 bool user_rate_valid = true;
 u32 rx_nss, tx_nss, nss_160;
 u8 ampdu_factor, rx_mcs_80, rx_mcs_160;
 u16 mcs_160_map, mcs_80_map;
 bool support_160;

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

 if (!he_cap->has_he)
  return;

 band = def.chan->band;
 memcpy(he_mcs_mask, arvif->bitrate_mask.control[band].he_mcs,
        sizeof(he_mcs_mask));

 if (ath11k_peer_assoc_h_he_masked(he_mcs_mask))
  return;

 arg->he_flag = true;
 support_160 = !!(he_cap->he_cap_elem.phy_cap_info[0] &
    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G);

 /* Supported HE-MCS and NSS Set of peer he_cap is intersection with self he_cp */
 mcs_160_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);

 /* Initialize rx_mcs_160 to 9 which is an invalid value */
 rx_mcs_160 = 9;
 if (support_160) {
  for (i = 7; i >= 0; i--) {
   u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;

   if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
    rx_mcs_160 = i + 1;
    break;
   }
  }
 }

 /* Initialize rx_mcs_80 to 9 which is an invalid value */
 rx_mcs_80 = 9;
 for (i = 7; i >= 0; i--) {
  u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;

  if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
   rx_mcs_80 = i + 1;
   break;
  }
 }

 if (support_160)
  max_nss = min(rx_mcs_80, rx_mcs_160);
 else
  max_nss = rx_mcs_80;

 arg->peer_nss = min(sta->deflink.rx_nss, max_nss);

 memcpy_and_pad(&arg->peer_he_cap_macinfo,
         sizeof(arg->peer_he_cap_macinfo),
         he_cap->he_cap_elem.mac_cap_info,
         sizeof(he_cap->he_cap_elem.mac_cap_info),
         0);
 memcpy_and_pad(&arg->peer_he_cap_phyinfo,
         sizeof(arg->peer_he_cap_phyinfo),
         he_cap->he_cap_elem.phy_cap_info,
         sizeof(he_cap->he_cap_elem.phy_cap_info),
         0);
 arg->peer_he_ops = vif->bss_conf.he_oper.params;

 /* the top most byte is used to indicate BSS color info */
 arg->peer_he_ops &= 0xffffff;

 /* As per section 26.6.1 11ax Draft5.0, if the Max AMPDU Exponent Extension
 * in HE cap is zero, use the arg->peer_max_mpdu as calculated while parsing
 * VHT caps(if VHT caps is present) or HT caps (if VHT caps is not present).
 *
 * For non-zero value of Max AMPDU Extponent Extension in HE MAC caps,
 * if a HE STA sends VHT cap and HE cap IE in assoc request then, use
 * MAX_AMPDU_LEN_FACTOR as 20 to calculate max_ampdu length.
 * If a HE STA that does not send VHT cap, but HE and HT cap in assoc
 * request, then use MAX_AMPDU_LEN_FACTOR as 16 to calculate max_ampdu
 * length.
 */

 ampdu_factor = u8_get_bits(he_cap->he_cap_elem.mac_cap_info[3],
       IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK);

 if (ampdu_factor) {
  if (sta->deflink.vht_cap.vht_supported)
   arg->peer_max_mpdu = (1 << (IEEE80211_HE_VHT_MAX_AMPDU_FACTOR +
          ampdu_factor)) - 1;
  else if (sta->deflink.ht_cap.ht_supported)
   arg->peer_max_mpdu = (1 << (IEEE80211_HE_HT_MAX_AMPDU_FACTOR +
          ampdu_factor)) - 1;
 }

 if (he_cap->he_cap_elem.phy_cap_info[6] &
     IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
  int bit = 7;
  int nss, ru;

  arg->peer_ppet.numss_m1 = he_cap->ppe_thres[0] &
       IEEE80211_PPE_THRES_NSS_MASK;
  arg->peer_ppet.ru_bit_mask =
   (he_cap->ppe_thres[0] &
    IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK) >>
   IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS;

  for (nss = 0; nss <= arg->peer_ppet.numss_m1; nss++) {
   for (ru = 0; ru < 4; ru++) {
    u32 val = 0;
    int i;

    if ((arg->peer_ppet.ru_bit_mask & BIT(ru)) == 0)
     continue;
    for (i = 0; i < 6; i++) {
     val >>= 1;
     val |= ((he_cap->ppe_thres[bit / 8] >>
       (bit % 8)) & 0x1) << 5;
     bit++;
    }
    arg->peer_ppet.ppet16_ppet8_ru3_ru0[nss] |=
        val << (ru * 6);
   }
  }
 }

 if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_RES)
  arg->twt_responder = true;
 if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_REQ)
  arg->twt_requester = true;

 he_nss =  ath11k_mac_max_he_nss(he_mcs_mask);

 if (he_nss > sta->deflink.rx_nss) {
  user_rate_valid = false;
  for (nss_idx = sta->deflink.rx_nss - 1; nss_idx >= 0; nss_idx--) {
   if (he_mcs_mask[nss_idx]) {
    user_rate_valid = true;
    break;
   }
  }
 }

 if (!user_rate_valid) {
  ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "setting he range mcs value to peer supported nss %d for peer %pM\n",
      sta->deflink.rx_nss, sta->addr);
  he_mcs_mask[sta->deflink.rx_nss - 1] = he_mcs_mask[he_nss - 1];
 }

 switch (sta->deflink.bandwidth) {
 case IEEE80211_STA_RX_BW_160:
  if (he_cap->he_cap_elem.phy_cap_info[0] &
      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) {
   v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80p80);
   v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
   arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v;

   v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80p80);
   arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v;

   arg->peer_he_mcs_count++;
   he_tx_mcs = v;
  }
  v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
  arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;

  v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160);
  v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
  arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;

  arg->peer_he_mcs_count++;
  if (!he_tx_mcs)
   he_tx_mcs = v;
  fallthrough;

 default:
  v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
  arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;

  v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80);
  v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
  arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;

  arg->peer_he_mcs_count++;
  if (!he_tx_mcs)
   he_tx_mcs = v;
  break;
 }

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

 for (i = 0, max_nss = 0; i < NL80211_HE_NSS_MAX; i++) {
  he_mcs = he_tx_mcs >> (2 * i) & 3;

  /* In case of fixed rates, MCS Range in he_tx_mcs might have
 * unsupported range, with he_mcs_mask set, so check either of them
 * to find nss.
 */

  if (he_mcs != IEEE80211_HE_MCS_NOT_SUPPORTED ||
      he_mcs_mask[i])
   max_nss = i + 1;
 }
 arg->peer_nss = min(sta->deflink.rx_nss, max_nss);

 if (arg->peer_phymode == MODE_11AX_HE160 ||
     arg->peer_phymode == MODE_11AX_HE80_80) {
  tx_nss = ath11k_get_nss_160mhz(ar, max_nss);
  rx_nss = min(arg->peer_nss, tx_nss);
  arg->peer_bw_rxnss_override = ATH11K_BW_NSS_MAP_ENABLE;

  if (!rx_nss) {
   ath11k_warn(ar->ab, "invalid max_nss\n");
   return;
  }

  if (arg->peer_phymode == MODE_11AX_HE160)
   nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_160MHZ, rx_nss - 1);
  else
   nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_80_80MHZ, rx_nss - 1);

  arg->peer_bw_rxnss_override |= nss_160;
 }

 ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
     "he peer %pM nss %d mcs cnt %d nss_override 0x%x\n",
     sta->addr, arg->peer_nss,
     arg->peer_he_mcs_count,
     arg->peer_bw_rxnss_override);
}

static void ath11k_peer_assoc_h_he_6ghz(struct ath11k *ar,
     struct ieee80211_vif *vif,
     struct ieee80211_sta *sta,
     struct peer_assoc_params *arg)
{
--> --------------------

--> maximum size reached

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

Messung V0.5
C=99 H=92 G=95

¤ Dauer der Verarbeitung: 0.31 Sekunden  ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge