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

Quelle  mac.c   Sprache: C

 
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
 * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
 */


#include <net/mac80211.h>
#include <net/cfg80211.h>
#include <linux/etherdevice.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.h"
#include "hif.h"
#include "wow.h"
#include "debugfs_sta.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 ath12k_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 ath12k_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),
};

static const struct ieee80211_channel ath12k_6ghz_channels[] = {
 /* Operating Class 136 */
 CHAN6G(2, 5935, 0),

 /* Operating Classes 131-135 */
 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),
};

static struct ieee80211_rate ath12k_legacy_rates[] = {
 { .bitrate = 10,
   .hw_value = ATH12K_HW_RATE_CCK_LP_1M },
 { .bitrate = 20,
   .hw_value = ATH12K_HW_RATE_CCK_LP_2M,
   .hw_value_short = ATH12K_HW_RATE_CCK_SP_2M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 55,
   .hw_value = ATH12K_HW_RATE_CCK_LP_5_5M,
   .hw_value_short = ATH12K_HW_RATE_CCK_SP_5_5M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 { .bitrate = 110,
   .hw_value = ATH12K_HW_RATE_CCK_LP_11M,
   .hw_value_short = ATH12K_HW_RATE_CCK_SP_11M,
   .flags = IEEE80211_RATE_SHORT_PREAMBLE },

 { .bitrate = 60, .hw_value = ATH12K_HW_RATE_OFDM_6M },
 { .bitrate = 90, .hw_value = ATH12K_HW_RATE_OFDM_9M },
 { .bitrate = 120, .hw_value = ATH12K_HW_RATE_OFDM_12M },
 { .bitrate = 180, .hw_value = ATH12K_HW_RATE_OFDM_18M },
 { .bitrate = 240, .hw_value = ATH12K_HW_RATE_OFDM_24M },
 { .bitrate = 360, .hw_value = ATH12K_HW_RATE_OFDM_36M },
 { .bitrate = 480, .hw_value = ATH12K_HW_RATE_OFDM_48M },
 { .bitrate = 540, .hw_value = ATH12K_HW_RATE_OFDM_54M },
};

static const int
ath12k_phymodes[NUM_NL80211_BANDS][ATH12K_CHAN_WIDTH_NUM] = {
 [NL80211_BAND_2GHZ] = {
   [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11BE_EHT20_2G,
   [NL80211_CHAN_WIDTH_20] = MODE_11BE_EHT20_2G,
   [NL80211_CHAN_WIDTH_40] = MODE_11BE_EHT40_2G,
   [NL80211_CHAN_WIDTH_80] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_80P80] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_160] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_320] = MODE_UNKNOWN,
 },
 [NL80211_BAND_5GHZ] = {
   [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11BE_EHT20,
   [NL80211_CHAN_WIDTH_20] = MODE_11BE_EHT20,
   [NL80211_CHAN_WIDTH_40] = MODE_11BE_EHT40,
   [NL80211_CHAN_WIDTH_80] = MODE_11BE_EHT80,
   [NL80211_CHAN_WIDTH_160] = MODE_11BE_EHT160,
   [NL80211_CHAN_WIDTH_80P80] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_320] = MODE_11BE_EHT320,
 },
 [NL80211_BAND_6GHZ] = {
   [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11BE_EHT20,
   [NL80211_CHAN_WIDTH_20] = MODE_11BE_EHT20,
   [NL80211_CHAN_WIDTH_40] = MODE_11BE_EHT40,
   [NL80211_CHAN_WIDTH_80] = MODE_11BE_EHT80,
   [NL80211_CHAN_WIDTH_160] = MODE_11BE_EHT160,
   [NL80211_CHAN_WIDTH_80P80] = MODE_UNKNOWN,
   [NL80211_CHAN_WIDTH_320] = MODE_11BE_EHT320,
 },

};

const struct htt_rx_ring_tlv_filter ath12k_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 |
       HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO,
 .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 ATH12K_MAC_FIRST_OFDM_RATE_IDX 4
#define ath12k_g_rates ath12k_legacy_rates
#define ath12k_g_rates_size (ARRAY_SIZE(ath12k_legacy_rates))
#define ath12k_a_rates (ath12k_legacy_rates + 4)
#define ath12k_a_rates_size (ARRAY_SIZE(ath12k_legacy_rates) - 4)

#define ATH12K_MAC_SCAN_TIMEOUT_MSECS 200 /* in msecs */

static const u32 ath12k_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,
};

static int ath12k_start_vdev_delay(struct ath12k *ar,
       struct ath12k_link_vif *arvif);
static void ath12k_mac_stop(struct ath12k *ar);
static int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif);
static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ath12k_link_vif *arvif);

static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode)
{
 switch (mode) {
 case MODE_11A:
  return "11a";
 case MODE_11G:
  return "11g";
 case MODE_11B:
  return "11b";
 case MODE_11GONLY:
  return "11gonly";
 case MODE_11NA_HT20:
  return "11na-ht20";
 case MODE_11NG_HT20:
  return "11ng-ht20";
 case MODE_11NA_HT40:
  return "11na-ht40";
 case MODE_11NG_HT40:
  return "11ng-ht40";
 case MODE_11AC_VHT20:
  return "11ac-vht20";
 case MODE_11AC_VHT40:
  return "11ac-vht40";
 case MODE_11AC_VHT80:
  return "11ac-vht80";
 case MODE_11AC_VHT160:
  return "11ac-vht160";
 case MODE_11AC_VHT80_80:
  return "11ac-vht80+80";
 case MODE_11AC_VHT20_2G:
  return "11ac-vht20-2g";
 case MODE_11AC_VHT40_2G:
  return "11ac-vht40-2g";
 case MODE_11AC_VHT80_2G:
  return "11ac-vht80-2g";
 case MODE_11AX_HE20:
  return "11ax-he20";
 case MODE_11AX_HE40:
  return "11ax-he40";
 case MODE_11AX_HE80:
  return "11ax-he80";
 case MODE_11AX_HE80_80:
  return "11ax-he80+80";
 case MODE_11AX_HE160:
  return "11ax-he160";
 case MODE_11AX_HE20_2G:
  return "11ax-he20-2g";
 case MODE_11AX_HE40_2G:
  return "11ax-he40-2g";
 case MODE_11AX_HE80_2G:
  return "11ax-he80-2g";
 case MODE_11BE_EHT20:
  return "11be-eht20";
 case MODE_11BE_EHT40:
  return "11be-eht40";
 case MODE_11BE_EHT80:
  return "11be-eht80";
 case MODE_11BE_EHT80_80:
  return "11be-eht80+80";
 case MODE_11BE_EHT160:
  return "11be-eht160";
 case MODE_11BE_EHT160_160:
  return "11be-eht160+160";
 case MODE_11BE_EHT320:
  return "11be-eht320";
 case MODE_11BE_EHT20_2G:
  return "11be-eht20-2g";
 case MODE_11BE_EHT40_2G:
  return "11be-eht40-2g";
 case MODE_UNKNOWN:
  /* skip */
  break;

  /* no default handler to allow compiler to check that the
 * enum is fully handled
 */

 }

 return "";
}

u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones)
{
 switch (tones) {
 case 26:
  return RU_26;
 case 52:
  return RU_52;
 case 106:
  return RU_106;
 case 242:
  return RU_242;
 case 484:
  return RU_484;
 case 996:
  return RU_996;
 case (996 * 2):
  return RU_2X996;
 default:
  return RU_26;
 }
}

enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi)
{
 switch (sgi) {
 case RX_MSDU_START_SGI_0_8_US:
  return NL80211_RATE_INFO_EHT_GI_0_8;
 case RX_MSDU_START_SGI_1_6_US:
  return NL80211_RATE_INFO_EHT_GI_1_6;
 case RX_MSDU_START_SGI_3_2_US:
  return NL80211_RATE_INFO_EHT_GI_3_2;
 default:
  return NL80211_RATE_INFO_EHT_GI_0_8;
 }
}

enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones)
{
 switch (ru_tones) {
 case 26:
  return NL80211_RATE_INFO_EHT_RU_ALLOC_26;
 case 52:
  return NL80211_RATE_INFO_EHT_RU_ALLOC_52;
 case (52 + 26):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_52P26;
 case 106:
  return NL80211_RATE_INFO_EHT_RU_ALLOC_106;
 case (106 + 26):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_106P26;
 case 242:
  return NL80211_RATE_INFO_EHT_RU_ALLOC_242;
 case 484:
  return NL80211_RATE_INFO_EHT_RU_ALLOC_484;
 case (484 + 242):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_484P242;
 case 996:
  return NL80211_RATE_INFO_EHT_RU_ALLOC_996;
 case (996 + 484):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484;
 case (996 + 484 + 242):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242;
 case (2 * 996):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996;
 case (2 * 996 + 484):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484;
 case (3 * 996):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996;
 case (3 * 996 + 484):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484;
 case (4 * 996):
  return NL80211_RATE_INFO_EHT_RU_ALLOC_4x996;
 default:
  return NL80211_RATE_INFO_EHT_RU_ALLOC_26;
 }
}

enum rate_info_bw
ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw)
{
 u8 ret = RATE_INFO_BW_20;

 switch (bw) {
 case ATH12K_BW_20:
  ret = RATE_INFO_BW_20;
  break;
 case ATH12K_BW_40:
  ret = RATE_INFO_BW_40;
  break;
 case ATH12K_BW_80:
  ret = RATE_INFO_BW_80;
  break;
 case ATH12K_BW_160:
  ret = RATE_INFO_BW_160;
  break;
 case ATH12K_BW_320:
  ret = RATE_INFO_BW_320;
  break;
 }

 return ret;
}

enum ath12k_supported_bw ath12k_mac_mac80211_bw_to_ath12k_bw(enum rate_info_bw bw)
{
 switch (bw) {
 case RATE_INFO_BW_20:
  return ATH12K_BW_20;
 case RATE_INFO_BW_40:
  return ATH12K_BW_40;
 case RATE_INFO_BW_80:
  return ATH12K_BW_80;
 case RATE_INFO_BW_160:
  return ATH12K_BW_160;
 case RATE_INFO_BW_320:
  return ATH12K_BW_320;
 default:
  return ATH12K_BW_20;
 }
}

int ath12k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx,
       u16 *rate)
{
 /* As default, it is OFDM rates */
 int i = ATH12K_MAC_FIRST_OFDM_RATE_IDX;
 int max_rates_idx = ath12k_g_rates_size;

 if (preamble == WMI_RATE_PREAMBLE_CCK) {
  hw_rc &= ~ATH12K_HW_RATECODE_CCK_SHORT_PREAM_MASK;
  i = 0;
  max_rates_idx = ATH12K_MAC_FIRST_OFDM_RATE_IDX;
 }

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

 return -EINVAL;
}

u8 ath12k_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
ath12k_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
ath12k_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
ath12k_mac_max_he_nss(const u16 he_mcs_mask[NL80211_HE_NSS_MAX])
{
 int nss;

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

 return 1;
}

static u8 ath12k_parse_mpdudensity(u8 mpdudensity)
{
/*  From IEEE Std 802.11-2020 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 ath12k_mac_vif_link_chan(struct ieee80211_vif *vif, u8 link_id,
        struct cfg80211_chan_def *def)
{
 struct ieee80211_bss_conf *link_conf;
 struct ieee80211_chanctx_conf *conf;

 rcu_read_lock();
 link_conf = rcu_dereference(vif->link_conf[link_id]);

 if (!link_conf) {
  rcu_read_unlock();
  return -ENOLINK;
 }

 conf = rcu_dereference(link_conf->chanctx_conf);
 if (!conf) {
  rcu_read_unlock();
  return -ENOENT;
 }
 *def = conf->def;
 rcu_read_unlock();

 return 0;
}

static struct ath12k_link_vif *
ath12k_mac_get_tx_arvif(struct ath12k_link_vif *arvif,
   struct ieee80211_bss_conf *link_conf)
{
 struct ieee80211_bss_conf *tx_bss_conf;
 struct ath12k *ar = arvif->ar;
 struct ath12k_vif *tx_ahvif;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 tx_bss_conf = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
     link_conf->tx_bss_conf);
 if (tx_bss_conf) {
  tx_ahvif = ath12k_vif_to_ahvif(tx_bss_conf->vif);
  return wiphy_dereference(tx_ahvif->ah->hw->wiphy,
      tx_ahvif->link[tx_bss_conf->link_id]);
 }

 return NULL;
}

static const u8 *ath12k_mac_get_tx_bssid(struct ath12k_link_vif *arvif)
{
 struct ieee80211_bss_conf *link_conf;
 struct ath12k_link_vif *tx_arvif;
 struct ath12k *ar = arvif->ar;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 link_conf = ath12k_mac_get_link_bss_conf(arvif);
 if (!link_conf) {
  ath12k_warn(ar->ab,
       "unable to access bss link conf for link %u required to retrieve transmitting link conf\n",
       arvif->link_id);
  return NULL;
 }
 if (link_conf->vif->type == NL80211_IFTYPE_STATION) {
  if (link_conf->nontransmitted)
   return link_conf->transmitter_bssid;
 } else {
  tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf);
  if (tx_arvif)
   return tx_arvif->bssid;
 }

 return NULL;
}

struct ieee80211_bss_conf *
ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif)
{
 struct ieee80211_vif *vif = arvif->ahvif->vif;
 struct ieee80211_bss_conf *link_conf;
 struct ath12k *ar = arvif->ar;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 if (arvif->link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
  return NULL;

 link_conf = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
          vif->link_conf[arvif->link_id]);

 return link_conf;
}

static struct ieee80211_link_sta *ath12k_mac_get_link_sta(struct ath12k_link_sta *arsta)
{
 struct ath12k_sta *ahsta = arsta->ahsta;
 struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta);
 struct ieee80211_link_sta *link_sta;

 lockdep_assert_wiphy(ahsta->ahvif->ah->hw->wiphy);

 if (arsta->link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
  return NULL;

 link_sta = wiphy_dereference(ahsta->ahvif->ah->hw->wiphy,
         sta->link[arsta->link_id]);

 return link_sta;
}

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

 return false;
}

u8 ath12k_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 (ath12k_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 ath12k_mac_bitrate_to_rate(int bitrate)
{
 return DIV_ROUND_UP(bitrate, 5) |
        (ath12k_mac_bitrate_is_cck(bitrate) ? BIT(7) : 0);
}

static void ath12k_get_arvif_iter(void *data, u8 *mac,
      struct ieee80211_vif *vif)
{
 struct ath12k_vif_iter *arvif_iter = data;
 struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
 unsigned long links_map = ahvif->links_map;
 struct ath12k_link_vif *arvif;
 u8 link_id;

 for_each_set_bit(link_id, &links_map, IEEE80211_MLD_MAX_NUM_LINKS) {
  arvif = rcu_dereference(ahvif->link[link_id]);

  if (WARN_ON(!arvif))
   continue;

  if (!arvif->is_created)
   continue;

  if (arvif->vdev_id == arvif_iter->vdev_id &&
      arvif->ar == arvif_iter->ar) {
   arvif_iter->arvif = arvif;
   break;
  }
 }
}

struct ath12k_link_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id)
{
 struct ath12k_vif_iter arvif_iter = {};
 u32 flags;

 /* To use the arvif returned, caller must have held rcu read lock.
 */

 WARN_ON(!rcu_read_lock_any_held());
 arvif_iter.vdev_id = vdev_id;
 arvif_iter.ar = ar;

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

 return arvif_iter.arvif;
}

struct ath12k_link_vif *ath12k_mac_get_arvif_by_vdev_id(struct ath12k_base *ab,
       u32 vdev_id)
{
 int i;
 struct ath12k_pdev *pdev;
 struct ath12k_link_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 = ath12k_mac_get_arvif(pdev->ar, vdev_id);
   if (arvif)
    return arvif;
  }
 }

 return NULL;
}

struct ath12k *ath12k_mac_get_ar_by_vdev_id(struct ath12k_base *ab, u32 vdev_id)
{
 int i;
 struct ath12k_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 ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id)
{
 int i;
 struct ath12k_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 == ATH12K_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;
}

static bool ath12k_mac_is_ml_arvif(struct ath12k_link_vif *arvif)
{
 struct ath12k_vif *ahvif = arvif->ahvif;

 lockdep_assert_wiphy(ahvif->ah->hw->wiphy);

 if (ahvif->vif->valid_links & BIT(arvif->link_id))
  return true;

 return false;
}

static struct ath12k *ath12k_mac_get_ar_by_chan(struct ieee80211_hw *hw,
      struct ieee80211_channel *channel)
{
 struct ath12k_hw *ah = hw->priv;
 struct ath12k *ar;
 int i;

 ar = ah->radio;

 if (ah->num_radio == 1)
  return ar;

 for_each_ar(ah, ar, i) {
  if (channel->center_freq >= KHZ_TO_MHZ(ar->freq_range.start_freq) &&
      channel->center_freq <= KHZ_TO_MHZ(ar->freq_range.end_freq))
   return ar;
 }
 return NULL;
}

static struct ath12k *ath12k_get_ar_by_ctx(struct ieee80211_hw *hw,
        struct ieee80211_chanctx_conf *ctx)
{
 if (!ctx)
  return NULL;

 return ath12k_mac_get_ar_by_chan(hw, ctx->def.chan);
}

struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw,
        struct ieee80211_vif *vif,
        u8 link_id)
{
 struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
 struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
 struct ath12k_link_vif *arvif;

 lockdep_assert_wiphy(hw->wiphy);

 /* If there is one pdev within ah, then we return
 * ar directly.
 */

 if (ah->num_radio == 1)
  return ah->radio;

 if (!(ahvif->links_map & BIT(link_id)))
  return NULL;

 arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
 if (arvif && arvif->is_created)
  return arvif->ar;

 return NULL;
}

void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw,
       struct ieee80211_chanctx_conf *conf,
       void *data)
{
 struct ath12k_mac_get_any_chanctx_conf_arg *arg = data;
 struct ath12k *ctx_ar = ath12k_get_ar_by_ctx(hw, conf);

 if (ctx_ar == arg->ar)
  arg->chanctx_conf = conf;
}

static struct ath12k_link_vif *ath12k_mac_get_vif_up(struct ath12k *ar)
{
 struct ath12k_link_vif *arvif;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 list_for_each_entry(arvif, &ar->arvifs, list) {
  if (arvif->is_up)
   return arvif;
 }

 return NULL;
}

static bool ath12k_mac_band_match(enum nl80211_band band1, enum WMI_HOST_WLAN_BAND band2)
{
 switch (band1) {
 case NL80211_BAND_2GHZ:
  if (band2 & WMI_HOST_WLAN_2GHZ_CAP)
   return true;
  break;
 case NL80211_BAND_5GHZ:
 case NL80211_BAND_6GHZ:
  if (band2 & WMI_HOST_WLAN_5GHZ_CAP)
   return true;
  break;
 default:
  return false;
 }

 return false;
}

static u8 ath12k_mac_get_target_pdev_id_from_vif(struct ath12k_link_vif *arvif)
{
 struct ath12k *ar = arvif->ar;
 struct ath12k_base *ab = ar->ab;
 struct ieee80211_vif *vif = arvif->ahvif->vif;
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 u8 pdev_id = ab->fw_pdev[0].pdev_id;
 int i;

 if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)))
  return pdev_id;

 band = def.chan->band;

 for (i = 0; i < ab->fw_pdev_count; i++) {
  if (ath12k_mac_band_match(band, ab->fw_pdev[i].supported_bands))
   return ab->fw_pdev[i].pdev_id;
 }

 return pdev_id;
}

u8 ath12k_mac_get_target_pdev_id(struct ath12k *ar)
{
 struct ath12k_link_vif *arvif;
 struct ath12k_base *ab = ar->ab;

 if (!ab->hw_params->single_pdev_only)
  return ar->pdev->pdev_id;

 arvif = ath12k_mac_get_vif_up(ar);

 /* fw_pdev array has pdev ids derived from phy capability
 * service ready event (pdev_and_hw_link_ids).
 * If no vif is active, return default first index.
 */

 if (!arvif)
  return ar->ab->fw_pdev[0].pdev_id;

 /* If active vif is found, return the pdev id matching chandef band */
 return ath12k_mac_get_target_pdev_id_from_vif(arvif);
}

static void ath12k_pdev_caps_update(struct ath12k *ar)
{
 struct ath12k_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 ath12k_mac_txpower_recalc(struct ath12k *ar)
{
 struct ath12k_pdev *pdev = ar->pdev;
 struct ath12k_link_vif *arvif;
 int ret, txpower = -1;
 u32 param;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 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;

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

 if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) &&
     ar->txpower_limit_2g != txpower) {
  param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G;
  ret = ath12k_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_5GHZ_CAP) &&
     ar->txpower_limit_5g != txpower) {
  param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G;
  ret = ath12k_wmi_pdev_set_param(ar, param,
      txpower, ar->pdev->pdev_id);
  if (ret)
   goto fail;
  ar->txpower_limit_5g = txpower;
 }

 return 0;

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

static int ath12k_recalc_rtscts_prot(struct ath12k_link_vif *arvif)
{
 struct ath12k *ar = arvif->ar;
 u32 vdev_param, rts_cts;
 int ret;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 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;

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

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

 return ret;
}

static int ath12k_mac_set_kickout(struct ath12k_link_vif *arvif)
{
 struct ath12k *ar = arvif->ar;
 u32 param;
 int ret;

 ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_STA_KICKOUT_TH,
     ATH12K_KICKOUT_THRESHOLD,
     ar->pdev->pdev_id);
 if (ret) {
  ath12k_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 = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
         ATH12K_KEEPALIVE_MIN_IDLE);
 if (ret) {
  ath12k_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 = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
         ATH12K_KEEPALIVE_MAX_IDLE);
 if (ret) {
  ath12k_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 = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
         ATH12K_KEEPALIVE_MAX_UNRESPONSIVE);
 if (ret) {
  ath12k_warn(ar->ab, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

void ath12k_mac_peer_cleanup_all(struct ath12k *ar)
{
 struct ath12k_peer *peer, *tmp;
 struct ath12k_base *ab = ar->ab;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 spin_lock_bh(&ab->base_lock);
 list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
  /* Skip Rx TID cleanup for self peer */
  if (peer->sta)
   ath12k_dp_rx_peer_tid_cleanup(ar, peer);

  list_del(&peer->list);
  kfree(peer);
 }
 spin_unlock_bh(&ab->base_lock);

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

static int ath12k_mac_vdev_setup_sync(struct ath12k *ar)
{
 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

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

 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "vdev setup timeout %d\n",
     ATH12K_VDEV_SETUP_TIMEOUT_HZ);

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

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

static int ath12k_monitor_vdev_up(struct ath12k *ar, int vdev_id)
{
 struct ath12k_wmi_vdev_up_params params = {};
 int ret;

 params.vdev_id = vdev_id;
 params.bssid = ar->mac_addr;
 ret = ath12k_wmi_vdev_up(ar, ¶ms);
 if (ret) {
  ath12k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n",
       vdev_id, ret);
  return ret;
 }

 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor vdev %i started\n",
     vdev_id);
 return 0;
}

static int ath12k_mac_monitor_vdev_start(struct ath12k *ar, int vdev_id,
      struct cfg80211_chan_def *chandef)
{
 struct ieee80211_channel *channel;
 struct wmi_vdev_start_req_arg arg = {};
 struct ath12k_wmi_vdev_up_params params = {};
 int ret;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 channel = chandef->chan;
 arg.vdev_id = vdev_id;
 arg.freq = channel->center_freq;
 arg.band_center_freq1 = chandef->center_freq1;
 arg.band_center_freq2 = chandef->center_freq2;
 arg.mode = ath12k_phymodes[chandef->chan->band][chandef->width];
 arg.chan_radar = !!(channel->flags & IEEE80211_CHAN_RADAR);

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

 arg.pref_tx_streams = ar->num_tx_chains;
 arg.pref_rx_streams = ar->num_rx_chains;
 arg.punct_bitmap = 0xFFFFFFFF;

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

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

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

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

 params.vdev_id = vdev_id;
 params.bssid = ar->mac_addr;
 ret = ath12k_wmi_vdev_up(ar, ¶ms);
 if (ret) {
  ath12k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n",
       vdev_id, ret);
  goto vdev_stop;
 }

 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor vdev %i started\n",
     vdev_id);
 return 0;

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

static int ath12k_mac_monitor_vdev_stop(struct ath12k *ar)
{
 int ret;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 reinit_completion(&ar->vdev_setup_done);

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

 ret = ath12k_mac_vdev_setup_sync(ar);
 if (ret)
  ath12k_warn(ar->ab, "failed to synchronize monitor vdev %i stop: %d\n",
       ar->monitor_vdev_id, ret);

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

 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor vdev %i stopped\n",
     ar->monitor_vdev_id);
 return ret;
}

static int ath12k_mac_monitor_vdev_delete(struct ath12k *ar)
{
 int ret;
 unsigned long time_left;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 if (!ar->monitor_vdev_created)
  return 0;

 reinit_completion(&ar->vdev_delete_done);

 ret = ath12k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
 if (ret) {
  ath12k_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,
      ATH12K_VDEV_DELETE_TIMEOUT_HZ);
 if (time_left == 0) {
  ath12k_warn(ar->ab, "Timeout in receiving vdev delete response\n");
 } else {
  ar->allocated_vdev_map &= ~(1LL << ar->monitor_vdev_id);
  ar->ab->free_vdev_map |= 1LL << (ar->monitor_vdev_id);
  ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor vdev %d deleted\n",
      ar->monitor_vdev_id);
  ar->num_created_vdevs--;
  ar->monitor_vdev_id = -1;
  ar->monitor_vdev_created = false;
 }

 return ret;
}

static int ath12k_mac_monitor_start(struct ath12k *ar)
{
 struct ath12k_mac_get_any_chanctx_conf_arg arg;
 int ret;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 if (ar->monitor_started)
  return 0;

 arg.ar = ar;
 arg.chanctx_conf = NULL;
 ieee80211_iter_chan_contexts_atomic(ath12k_ar_to_hw(ar),
         ath12k_mac_get_any_chanctx_conf_iter,
         &arg);
 if (!arg.chanctx_conf)
  return 0;

 ret = ath12k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id,
         &arg.chanctx_conf->def);
 if (ret) {
  ath12k_warn(ar->ab, "failed to start monitor vdev: %d\n", ret);
  return ret;
 }

 ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, false);
 if (ret) {
  ath12k_warn(ar->ab, "fail to set monitor filter: %d\n", ret);
  return ret;
 }

 ar->monitor_started = true;
 ar->num_started_vdevs++;

 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor started\n");

 return 0;
}

static int ath12k_mac_monitor_stop(struct ath12k *ar)
{
 int ret;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 if (!ar->monitor_started)
  return 0;

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

 ar->monitor_started = false;
 ar->num_started_vdevs--;
 ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, true);
 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor stopped ret %d\n", ret);
 return ret;
}

int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif)
{
 struct ath12k_vif *ahvif = arvif->ahvif;
 struct ath12k *ar = arvif->ar;
 int ret;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 reinit_completion(&ar->vdev_setup_done);

 ret = ath12k_wmi_vdev_stop(ar, arvif->vdev_id);
 if (ret) {
  ath12k_warn(ar->ab, "failed to stop WMI vdev %i: %d\n",
       arvif->vdev_id, ret);
  goto err;
 }

 ret = ath12k_mac_vdev_setup_sync(ar);
 if (ret) {
  ath12k_warn(ar->ab, "failed to synchronize setup for vdev %i: %d\n",
       arvif->vdev_id, ret);
  goto err;
 }

 WARN_ON(ar->num_started_vdevs == 0);

 ar->num_started_vdevs--;
 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "vdev %pM stopped, vdev_id %d\n",
     ahvif->vif->addr, arvif->vdev_id);

 if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) {
  clear_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags);
  ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "CAC Stopped for vdev %d\n",
      arvif->vdev_id);
 }

 return 0;
err:
 return ret;
}

static int ath12k_mac_op_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
{
 return 0;
}

static int ath12k_mac_setup_bcn_p2p_ie(struct ath12k_link_vif *arvif,
           struct sk_buff *bcn)
{
 struct ath12k *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) {
  ath12k_warn(ar->ab, "no P2P ie found in beacon\n");
  return -ENOENT;
 }

 ret = ath12k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie);
 if (ret) {
  ath12k_warn(ar->ab, "failed to submit P2P GO bcn ie for vdev %i: %d\n",
       arvif->vdev_id, ret);
  return ret;
 }

 return 0;
}

static int ath12k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui,
           u8 oui_type, size_t ie_offset)
{
 const u8 *next, *end;
 size_t len;
 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 void ath12k_mac_set_arvif_ies(struct ath12k_link_vif *arvif,
         struct ath12k_link_vif *tx_arvif,
         struct sk_buff *bcn,
         u8 bssid_index, bool *nontx_profile_found)
{
 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)bcn->data;
 const struct element *elem, *nontx, *index, *nie, *ext_cap_ie;
 const u8 *start, *tail;
 u16 rem_len;
 u8 i;

 start = bcn->data + ieee80211_get_hdrlen_from_skb(bcn) + sizeof(mgmt->u.beacon);
 tail = skb_tail_pointer(bcn);
 rem_len = tail - start;

 arvif->rsnie_present = false;
 arvif->wpaie_present = false;

 if (cfg80211_find_ie(WLAN_EID_RSN, start, rem_len))
  arvif->rsnie_present = true;
 if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA,
        start, rem_len))
  arvif->wpaie_present = true;

 ext_cap_ie = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, start, rem_len);
 if (ext_cap_ie && ext_cap_ie->datalen >= 11 &&
     (ext_cap_ie->data[10] & WLAN_EXT_CAPA11_BCN_PROTECT))
  tx_arvif->beacon_prot = true;

 /* Return from here for the transmitted profile */
 if (!bssid_index)
  return;

 /* Initial rsnie_present for the nontransmitted profile is set to be same as that
 * of the transmitted profile. It will be changed if security configurations are
 * different.
 */

 *nontx_profile_found = false;
 for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, rem_len) {
  /* Fixed minimum MBSSID element length with at least one
 * nontransmitted BSSID profile is 12 bytes as given below;
 * 1 (max BSSID indicator) +
 * 2 (Nontransmitted BSSID profile: Subelement ID + length) +
 * 4 (Nontransmitted BSSID Capabilities: tag + length + info)
 * 2 (Nontransmitted BSSID SSID: tag + length)
 * 3 (Nontransmitted BSSID Index: tag + length + BSSID index
 */

  if (elem->datalen < 12 || elem->data[0] < 1)
   continue/* Max BSSID indicator must be >=1 */

  for_each_element(nontx, elem->data + 1, elem->datalen - 1) {
   start = nontx->data;

   if (nontx->id != 0 || nontx->datalen < 4)
    continue/* Invalid nontransmitted profile */

   if (nontx->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
       nontx->data[1] != 2) {
    continue/* Missing nontransmitted BSS capabilities */
   }

   if (nontx->data[4] != WLAN_EID_SSID)
    continue/* Missing SSID for nontransmitted BSS */

   index = cfg80211_find_elem(WLAN_EID_MULTI_BSSID_IDX,
         start, nontx->datalen);
   if (!index || index->datalen < 1 || index->data[0] == 0)
    continue/* Invalid MBSSID Index element */

   if (index->data[0] == bssid_index) {
    *nontx_profile_found = true;

    /* Check if nontx BSS has beacon protection enabled */
    if (!tx_arvif->beacon_prot) {
     ext_cap_ie =
         cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY,
              nontx->data,
              nontx->datalen);
     if (ext_cap_ie && ext_cap_ie->datalen >= 11 &&
         (ext_cap_ie->data[10] &
          WLAN_EXT_CAPA11_BCN_PROTECT))
      tx_arvif->beacon_prot = true;
    }

    if (cfg80211_find_ie(WLAN_EID_RSN,
           nontx->data,
           nontx->datalen)) {
     arvif->rsnie_present = true;
     return;
    } else if (!arvif->rsnie_present) {
     return/* Both tx and nontx BSS are open */
    }

    nie = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
            nontx->data,
            nontx->datalen);
    if (!nie || nie->datalen < 2)
     return/* Invalid non-inheritance element */

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

    return;
   }
  }
 }
}

static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif,
      struct ath12k_link_vif *tx_arvif,
      u8 bssid_index)
{
 struct ath12k_wmi_bcn_tmpl_ema_arg ema_args;
 struct ieee80211_ema_beacons *beacons;
 bool nontx_profile_found = false;
 int ret = 0;
 u8 i;

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

 if (tx_arvif == arvif)
  ath12k_mac_set_arvif_ies(arvif, tx_arvif, beacons->bcn[0].skb, 0, NULL);

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

  ema_args.bcn_cnt = beacons->cnt;
  ema_args.bcn_index = i;
  ret = ath12k_wmi_bcn_tmpl(tx_arvif, &beacons->bcn[i].offs,
       beacons->bcn[i].skb, &ema_args);
  if (ret) {
   ath12k_warn(tx_arvif->ar->ab,
        "failed to set ema beacon template id %i error %d\n",
        i, ret);
   break;
  }
 }

 if (tx_arvif != arvif && !nontx_profile_found)
  ath12k_warn(arvif->ar->ab,
       "nontransmitted bssid index %u not found in beacon template\n",
       bssid_index);

 ieee80211_beacon_free_ema_list(beacons);
 return ret;
}

static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif)
{
 struct ath12k_vif *ahvif = arvif->ahvif;
 struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
 struct ieee80211_bss_conf *link_conf;
 struct ath12k_link_vif *tx_arvif;
 struct ath12k *ar = arvif->ar;
 struct ath12k_base *ab = ar->ab;
 struct ieee80211_mutable_offsets offs = {};
 bool nontx_profile_found = false;
 struct sk_buff *bcn;
 int ret;

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

 link_conf = ath12k_mac_get_link_bss_conf(arvif);
 if (!link_conf) {
  ath12k_warn(ar->ab, "unable to access bss link conf to set bcn tmpl for vif %pM link %u\n",
       vif->addr, arvif->link_id);
  return -ENOLINK;
 }

 tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf);
 if (tx_arvif) {
  if (tx_arvif != arvif && arvif->is_up)
   return 0;

  if (link_conf->ema_ap)
   return ath12k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif,
            link_conf->bssid_index);
 } else {
  tx_arvif = arvif;
 }

 bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar),
         tx_arvif->ahvif->vif,
         &offs, tx_arvif->link_id);
 if (!bcn) {
  ath12k_warn(ab, "failed to get beacon template from mac80211\n");
  return -EPERM;
 }

 if (tx_arvif == arvif) {
  ath12k_mac_set_arvif_ies(arvif, tx_arvif, bcn, 0, NULL);
 } else {
  ath12k_mac_set_arvif_ies(arvif, tx_arvif, bcn,
      link_conf->bssid_index,
      &nontx_profile_found);
  if (!nontx_profile_found)
   ath12k_warn(ab,
        "nontransmitted profile not found in beacon template\n");
 }

 if (ahvif->vif->type == NL80211_IFTYPE_AP && ahvif->vif->p2p) {
  ret = ath12k_mac_setup_bcn_p2p_ie(arvif, bcn);
  if (ret) {
   ath12k_warn(ab, "failed to setup P2P GO bcn ie: %d\n",
        ret);
   goto free_bcn_skb;
  }

  /* 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 = ath12k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA,
        WLAN_OUI_TYPE_WFA_P2P,
        offsetof(struct ieee80211_mgmt,
          u.beacon.variable));
  if (ret) {
   ath12k_warn(ab, "failed to remove P2P vendor ie: %d\n",
        ret);
   goto free_bcn_skb;
  }
 }

 ret = ath12k_wmi_bcn_tmpl(arvif, &offs, bcn, NULL);

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

free_bcn_skb:
 kfree_skb(bcn);
 return ret;
}

static void ath12k_control_beaconing(struct ath12k_link_vif *arvif,
         struct ieee80211_bss_conf *info)
{
 struct ath12k_wmi_vdev_up_params params = {};
 struct ath12k_vif *ahvif = arvif->ahvif;
 struct ath12k *ar = arvif->ar;
 int ret;

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

 if (!info->enable_beacon) {
  ret = ath12k_wmi_vdev_down(ar, arvif->vdev_id);
  if (ret)
   ath12k_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 = ath12k_mac_setup_bcn_tmpl(arvif);
 if (ret) {
  ath12k_warn(ar->ab, "failed to update bcn tmpl during vdev up: %d\n",
       ret);
  return;
 }

 ahvif->aid = 0;

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

 params.vdev_id = arvif->vdev_id;
 params.aid = ahvif->aid;
 params.bssid = arvif->bssid;
 params.tx_bssid = ath12k_mac_get_tx_bssid(arvif);
 if (params.tx_bssid) {
  params.nontx_profile_idx = info->bssid_index;
  params.nontx_profile_cnt = 1 << info->bssid_indicator;
 }
 ret = ath12k_wmi_vdev_up(arvif->ar, ¶ms);
 if (ret) {
  ath12k_warn(ar->ab, "failed to bring up vdev %d: %i\n",
       arvif->vdev_id, ret);
  return;
 }

 arvif->is_up = true;

 ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
}

static void ath12k_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 ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
 struct ath12k_link_vif *arvif = &ahvif->deflink;

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

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

 cancel_delayed_work(&arvif->connection_loss_work);
}

void ath12k_mac_handle_beacon(struct ath12k *ar, struct sk_buff *skb)
{
 ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar),
         IEEE80211_IFACE_ITER_NORMAL,
         ath12k_mac_handle_beacon_iter,
         skb);
}

static void ath12k_mac_handle_beacon_miss_iter(void *data, u8 *mac,
            struct ieee80211_vif *vif)
{
 u32 *vdev_id = data;
 struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
 struct ath12k_link_vif *arvif = &ahvif->deflink;
 struct ieee80211_hw *hw;

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

 if (!arvif->is_up)
  return;

 ieee80211_beacon_loss(vif);
 hw = ath12k_ar_to_hw(arvif->ar);

 /* 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,
         ATH12K_CONNECTION_LOSS_HZ);
}

void ath12k_mac_handle_beacon_miss(struct ath12k *ar, u32 vdev_id)
{
 ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar),
         IEEE80211_IFACE_ITER_NORMAL,
         ath12k_mac_handle_beacon_miss_iter,
         &vdev_id);
}

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

 if (!arvif->is_up)
  return;

 ieee80211_connection_loss(vif);
}

static void ath12k_peer_assoc_h_basic(struct ath12k *ar,
          struct ath12k_link_vif *arvif,
          struct ath12k_link_sta *arsta,
          struct ath12k_wmi_peer_assoc_arg *arg)
{
 struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
 struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
 struct ieee80211_hw *hw = ath12k_ar_to_hw(ar);
 struct ieee80211_bss_conf *bss_conf;
 u32 aid;

 lockdep_assert_wiphy(hw->wiphy);

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

 ether_addr_copy(arg->peer_mac, arsta->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 = hw->conf.listen_interval;
 arg->peer_nss = 1;

 bss_conf = ath12k_mac_get_link_bss_conf(arvif);
 if (!bss_conf) {
  ath12k_warn(ar->ab, "unable to access bss link conf in peer assoc for vif %pM link %u\n",
       vif->addr, arvif->link_id);
  return;
 }

 arg->peer_caps = bss_conf->assoc_capability;
}

static void ath12k_peer_assoc_h_crypto(struct ath12k *ar,
           struct ath12k_link_vif *arvif,
           struct ath12k_link_sta *arsta,
           struct ath12k_wmi_peer_assoc_arg *arg)
{
 struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
 struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
 struct ieee80211_bss_conf *info;
 struct cfg80211_chan_def def;
 struct cfg80211_bss *bss;
 struct ieee80211_hw *hw = ath12k_ar_to_hw(ar);
 const u8 *rsnie = NULL;
 const u8 *wpaie = NULL;

 lockdep_assert_wiphy(hw->wiphy);

 info = ath12k_mac_get_link_bss_conf(arvif);
 if (!info) {
  ath12k_warn(ar->ab, "unable to access bss link conf for peer assoc crypto for vif %pM link %u\n",
       vif->addr, arvif->link_id);
  return;
 }

 if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)))
  return;

 bss = cfg80211_get_bss(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(hw->wiphy, bss);
 }

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

 if (wpaie) {
  ath12k_dbg(ar->ab, ATH12K_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 ath12k_peer_assoc_h_rates(struct ath12k *ar,
          struct ath12k_link_vif *arvif,
          struct ath12k_link_sta *arsta,
          struct ath12k_wmi_peer_assoc_arg *arg)
{
 struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
 struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
 struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates;
 struct ieee80211_link_sta *link_sta;
 struct cfg80211_chan_def def;
 const struct ieee80211_supported_band *sband;
 const struct ieee80211_rate *rates;
 struct ieee80211_hw *hw = ath12k_ar_to_hw(ar);
 enum nl80211_band band;
 u32 ratemask;
 u8 rate;
 int i;

 lockdep_assert_wiphy(hw->wiphy);

 if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)))
  return;

 link_sta = ath12k_mac_get_link_sta(arsta);
 if (!link_sta) {
  ath12k_warn(ar->ab, "unable to access link sta in peer assoc rates for sta %pM link %u\n",
       sta->addr, arsta->link_id);
  return;
 }

 band = def.chan->band;
 sband = hw->wiphy->bands[band];
 ratemask = link_sta->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 = ath12k_mac_bitrate_to_rate(rates->bitrate);
  rateset->rates[rateset->num_rates] = rate;
  rateset->num_rates++;
 }
}

static bool
ath12k_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
ath12k_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 ath12k_peer_assoc_h_ht(struct ath12k *ar,
       struct ath12k_link_vif *arvif,
       struct ath12k_link_sta *arsta,
       struct ath12k_wmi_peer_assoc_arg *arg)
{
 struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
 struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
 const struct ieee80211_sta_ht_cap *ht_cap;
 struct ieee80211_link_sta *link_sta;
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 const u8 *ht_mcs_mask;
 int i, n;
 u8 max_nss;
 u32 stbc;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)))
  return;

 link_sta = ath12k_mac_get_link_sta(arsta);
 if (!link_sta) {
  ath12k_warn(ar->ab, "unable to access link sta in peer assoc ht for sta %pM link %u\n",
       sta->addr, arsta->link_id);
  return;
 }

 ht_cap = &link_sta->ht_cap;
 if (!ht_cap->ht_supported)
  return;

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

 if (ath12k_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 =
  ath12k_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 (link_sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
  arg->bw_40 = true;
  arg->peer_rate_caps |= WMI_HOST_RC_CW40_FLAG;
 }

 /* As firmware handles these two flags (IEEE80211_HT_CAP_SGI_20
 * and IEEE80211_HT_CAP_SGI_40) for enabling SGI, reset both
 * flags if guard interval is to force Long GI
 */

 if (arvif->bitrate_mask.control[band].gi == NL80211_TXRATE_FORCE_LGI) {
  arg->peer_ht_caps &= ~(IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40);
 } else {
  /* Enable SGI flag if either SGI_20 or SGI_40 is supported */
  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(link_sta->rx_nss, max_nss);
 }

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

static int ath12k_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
ath12k_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 = ath12k_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 ath12k_get_nss_160mhz(struct ath12k *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:
  ath12k_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:
  ath12k_warn(ar->ab, "WMI_NSS_RATIO_2_NSS not supported\n");
  break;
 default:
  ath12k_warn(ar->ab, "invalid nss ratio received from fw: %d\n",
       nss_ratio_info);
  break;
 }

 return max_sup_nss;
}

static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
        struct ath12k_link_vif *arvif,
        struct ath12k_link_sta *arsta,
        struct ath12k_wmi_peer_assoc_arg *arg)
{
 struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
 struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
 const struct ieee80211_sta_vht_cap *vht_cap;
 struct ieee80211_link_sta *link_sta;
 struct cfg80211_chan_def def;
 enum nl80211_band band;
 u16 *vht_mcs_mask;
 u16 tx_mcs_map;
 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;

 lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

 if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)))
  return;

 link_sta = ath12k_mac_get_link_sta(arsta);
 if (!link_sta) {
  ath12k_warn(ar->ab, "unable to access link sta in peer assoc vht for sta %pM link %u\n",
       sta->addr, arsta->link_id);
  return;
 }

 vht_cap = &link_sta->vht_cap;
 if (!vht_cap->vht_supported)
  return;

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

 if (ath12k_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 (link_sta->bandwidth == IEEE80211_STA_RX_BW_80)
  arg->bw_80 = true;

 if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
  arg->bw_160 = true;

 vht_nss =  ath12k_mac_max_vht_nss(vht_mcs_mask);

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

 if (!user_rate_valid) {
  ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
      "Setting vht range MCS value to peer supported nss:%d for peer %pM\n",
      link_sta->rx_nss, arsta->addr);
  vht_mcs_mask[link_sta->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, 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_nss = min(link_sta->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);

 tx_mcs_map = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
 arg->tx_mcs_set = ath12k_peer_assoc_h_vht_limit(tx_mcs_map, vht_mcs_mask);

 /* In QCN9274 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) {
  tx_nss = ath12k_get_nss_160mhz(ar, max_nss);
  rx_nss = min(arg->peer_nss, tx_nss);
  arg->peer_bw_rxnss_override = ATH12K_BW_NSS_MAP_ENABLE;

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

  nss_160 = u32_encode_bits(rx_nss - 1, ATH12K_PEER_RX_NSS_160MHZ);
  arg->peer_bw_rxnss_override |= nss_160;
 }

 ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
     "mac vht peer %pM max_mpdu %d flags 0x%x nss_override 0x%x\n",
     arsta->addr, arg->peer_max_mpdu, arg->peer_flags,
     arg->peer_bw_rxnss_override);
}

static int ath12k_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 ath12k_peer_assoc_h_he_limit(u16 tx_mcs_set,
     const u16 *he_mcs_limit)
{
 int idx_limit;
 int nss;
 u16 mcs_map;
 u16 mcs;

 for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) {
  mcs_map = ath12k_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
ath12k_peer_assoc_h_he_masked(const u16 he_mcs_mask[NL80211_HE_NSS_MAX])
{
 int nss;

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

 return true;
}

static void ath12k_peer_assoc_h_he(struct ath12k *ar,
       struct ath12k_link_vif *arvif,
       struct ath12k_link_sta *arsta,
       struct ath12k_wmi_peer_assoc_arg *arg)
{
 struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
 struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
 const struct ieee80211_sta_he_cap *he_cap;
 struct ieee80211_bss_conf *link_conf;
 struct ieee80211_link_sta *link_sta;
 struct cfg80211_chan_def def;
 int i;
 u8 ampdu_factor, max_nss;
 u8 rx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
 u8 rx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
 u16 mcs_160_map, mcs_80_map;
 u8 link_id = arvif->link_id;
 bool support_160;
 enum nl80211_band band;
 u16 *he_mcs_mask;
 u8 he_mcs;
 u16 he_tx_mcs = 0, v = 0;
 int he_nss, nss_idx;
 bool user_rate_valid = true;
 u32 rx_nss, tx_nss, nss_160;

 if (WARN_ON(ath12k_mac_vif_link_chan(vif, link_id, &def)))
  return;

 link_conf = ath12k_mac_get_link_bss_conf(arvif);
 if (!link_conf) {
  ath12k_warn(ar->ab, "unable to access bss link conf in peer assoc he for vif %pM link %u",
       vif->addr, link_id);
  return;
 }

 link_sta = ath12k_mac_get_link_sta(arsta);
 if (!link_sta) {
  ath12k_warn(ar->ab, "unable to access link sta in peer assoc he for sta %pM link %u\n",
       sta->addr, arsta->link_id);
  return;
 }

 he_cap = &link_sta->he_cap;
 if (!he_cap->has_he)
  return;

 band = def.chan->band;
 he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;

 if (ath12k_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);

 if (support_160) {
  for (i = 7; i >= 0; i--) {
   u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;

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

 for (i = 7; i >= 0; i--) {
  u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;

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

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

--> maximum size reached

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

Messung V0.5
C=98 H=93 G=95

¤ Dauer der Verarbeitung: 0.25 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.