All of lore.kernel.org
 help / color / mirror / Atom feed
From: viktor.barna@celeno.com
To: linux-wireless@vger.kernel.org
Cc: Kalle Valo <kvalo@codeaurora.org>,
	"David S . Miller" <davem@davemloft.net>,
	Jakub Kicinski <kuba@kernel.org>,
	Aviad Brikman <aviad.brikman@celeno.com>,
	Eliav Farber <eliav.farber@gmail.com>,
	Maksym Kokhan <maksym.kokhan@celeno.com>,
	Oleksandr Savchenko <oleksandr.savchenko@celeno.com>,
	Shay Bar <shay.bar@celeno.com>,
	Viktor Barna <viktor.barna@celeno.com>
Subject: [RFC v2 38/96] cl8k: add mac80211.c
Date: Tue, 24 May 2022 14:34:04 +0300	[thread overview]
Message-ID: <20220524113502.1094459-39-viktor.barna@celeno.com> (raw)
In-Reply-To: <20220524113502.1094459-1-viktor.barna@celeno.com>

From: Viktor Barna <viktor.barna@celeno.com>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/mac80211.c | 2392 +++++++++++++++++++
 1 file changed, 2392 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/mac80211.c

diff --git a/drivers/net/wireless/celeno/cl8k/mac80211.c b/drivers/net/wireless/celeno/cl8k/mac80211.c
new file mode 100644
index 000000000000..13989327ccdb
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/mac80211.c
@@ -0,0 +1,2392 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Copyright(c) 2019-2022, Celeno Communications Ltd. */
+
+#include <linux/log2.h>
+#include <net/mac80211.h>
+
+#include "debug.h"
+#include "utils.h"
+#include "sta.h"
+#include "dfs.h"
+#include "regdom.h"
+#include "hw.h"
+#include "mac80211.h"
+#include "ampdu.h"
+#include "tx.h"
+#include "radio.h"
+#include "recovery.h"
+#include "rates.h"
+#include "temperature.h"
+#include "vns.h"
+#include "key.h"
+#include "version.h"
+#include "power.h"
+#include "stats.h"
+#include "scan.h"
+#include "mac_addr.h"
+#include "chip.h"
+
+#define RATE_1_MBPS   10
+#define RATE_2_MBPS   20
+#define RATE_5_5_MBPS 55
+#define RATE_11_MBPS  110
+#define RATE_6_MBPS   60
+#define RATE_9_MBPS   90
+#define RATE_12_MBPS  120
+#define RATE_18_MBPS  180
+#define RATE_24_MBPS  240
+#define RATE_36_MBPS  360
+#define RATE_48_MBPS  480
+#define RATE_54_MBPS  540
+
+#define CL_HT_CAPABILITIES                                              \
+{                                                                       \
+	.ht_supported = true,                                           \
+	.cap = IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_MAX_AMSDU, \
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,                     \
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_1,                   \
+	.mcs = {                                                        \
+		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0 },         \
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,               \
+	},                                                              \
+}
+
+#define CL_VHT_CAPABILITIES                                             \
+{                                                                       \
+	.vht_supported = false,                                         \
+	.cap = 0,                                                       \
+	.vht_mcs = {                                                    \
+		.rx_mcs_map = cpu_to_le16(                              \
+				IEEE80211_VHT_MCS_SUPPORT_0_7   << 0  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 2  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 4  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 6  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 8  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
+		.tx_mcs_map = cpu_to_le16(                              \
+				IEEE80211_VHT_MCS_SUPPORT_0_7   << 0  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 2  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 4  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 6  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 8  | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
+	}                                                               \
+}
+
+#define CL_HE_CAP_ELEM_STATION                                                       \
+{                                                                                    \
+	.mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE,                             \
+	.mac_cap_info[1] = 0,                                                        \
+	.mac_cap_info[2] = 0,                                                        \
+	.mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2,            \
+	.mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_BQR,                                \
+	.mac_cap_info[5] = IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX,               \
+	.phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G, \
+	.phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A,                     \
+	.phy_cap_info[2] = 0,                                                        \
+	.phy_cap_info[3] = 0,                                                        \
+	.phy_cap_info[4] = 0,                                                        \
+	.phy_cap_info[5] = 0,                                                        \
+	.phy_cap_info[6] = 0,                                                        \
+	.phy_cap_info[7] = 0,                                                        \
+	.phy_cap_info[8] = IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G,       \
+	.phy_cap_info[9] = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,           \
+	.phy_cap_info[10] = 0,                                                       \
+}
+
+#define CL_HE_CAP_ELEM_AP                                                       \
+{                                                                               \
+	.mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE,                        \
+	.mac_cap_info[1] = 0,                                                   \
+	.mac_cap_info[2] = 0,                                                   \
+	.mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2,       \
+	.mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_BQR,                           \
+	.mac_cap_info[5] = 0,                                                   \
+	.phy_cap_info[0] = 0,                                                   \
+	.phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A,                \
+	.phy_cap_info[2] = 0,                                                   \
+	.phy_cap_info[3] = 0,                                                   \
+	.phy_cap_info[4] = 0,                                                   \
+	.phy_cap_info[5] = 0,                                                   \
+	.phy_cap_info[6] = 0,                                                   \
+	.phy_cap_info[7] = 0,                                                   \
+	.phy_cap_info[8] = 0,                                                   \
+	.phy_cap_info[9] = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,      \
+	.phy_cap_info[10] = 0,                                                  \
+}
+
+#define CL_HE_CAP_ELEM_MESH_POINT                                               \
+{                                                                               \
+	.mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE,                        \
+	.mac_cap_info[1] = 0,                                                   \
+	.mac_cap_info[2] = 0,                                                   \
+	.mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2,       \
+	.mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_BQR,                           \
+	.mac_cap_info[5] = 0,                                                   \
+	.phy_cap_info[0] = 0,                                                   \
+	.phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A,                \
+	.phy_cap_info[2] = 0,                                                   \
+	.phy_cap_info[3] = 0,                                                   \
+	.phy_cap_info[4] = 0,                                                   \
+	.phy_cap_info[5] = 0,                                                   \
+	.phy_cap_info[6] = 0,                                                   \
+	.phy_cap_info[7] = 0,                                                   \
+	.phy_cap_info[8] = 0,                                                   \
+	.phy_cap_info[9] = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,      \
+	.phy_cap_info[10] = 0,                                                  \
+}
+
+#define CL_HE_MCS_NSS_SUPP                   \
+{                                            \
+	.rx_mcs_80 = cpu_to_le16(0xff00),    \
+	.tx_mcs_80 = cpu_to_le16(0xff00),    \
+	.rx_mcs_160 = cpu_to_le16(0xff00),   \
+	.tx_mcs_160 = cpu_to_le16(0xff00),   \
+	.rx_mcs_80p80 = cpu_to_le16(0xffff), \
+	.tx_mcs_80p80 = cpu_to_le16(0xffff), \
+}
+
+#define RATE(_bitrate, _hw_rate, _flags) { \
+	.bitrate = (_bitrate),             \
+	.flags = (_flags),                 \
+	.hw_value = (_hw_rate),            \
+}
+
+#define CHAN(_freq, _idx) {     \
+	.center_freq = (_freq), \
+	.hw_value = (_idx),     \
+	.max_power = 18,        \
+}
+
+static struct ieee80211_sband_iftype_data cl_he_data[] = {
+	{
+		.types_mask = BIT(NL80211_IFTYPE_STATION),
+		.he_cap = {
+			.has_he = true,
+			.he_cap_elem = CL_HE_CAP_ELEM_STATION,
+			.he_mcs_nss_supp = CL_HE_MCS_NSS_SUPP,
+		},
+	},
+	{
+		.types_mask = BIT(NL80211_IFTYPE_AP),
+		.he_cap = {
+			.has_he = true,
+			.he_cap_elem = CL_HE_CAP_ELEM_AP,
+			.he_mcs_nss_supp = CL_HE_MCS_NSS_SUPP,
+		},
+	},
+	{
+		.types_mask = BIT(NL80211_IFTYPE_MESH_POINT),
+		.he_cap = {
+			.has_he = true,
+			.he_cap_elem = CL_HE_CAP_ELEM_MESH_POINT,
+			.he_mcs_nss_supp = CL_HE_MCS_NSS_SUPP,
+		},
+	},
+};
+
+static struct ieee80211_rate cl_ratetable[] = {
+	RATE(10,  0x00, 0),
+	RATE(20,  0x01, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(55,  0x02, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(60,  0x04, 0),
+	RATE(90,  0x05, 0),
+	RATE(120, 0x06, 0),
+	RATE(180, 0x07, 0),
+	RATE(240, 0x08, 0),
+	RATE(360, 0x09, 0),
+	RATE(480, 0x0A, 0),
+	RATE(540, 0x0B, 0),
+};
+
+/* The channels indexes here are not used anymore */
+static struct ieee80211_channel cl_2ghz_channels[] = {
+	CHAN(2412, 0),
+	CHAN(2417, 1),
+	CHAN(2422, 2),
+	CHAN(2427, 3),
+	CHAN(2432, 4),
+	CHAN(2437, 5),
+	CHAN(2442, 6),
+	CHAN(2447, 7),
+	CHAN(2452, 8),
+	CHAN(2457, 9),
+	CHAN(2462, 10),
+	CHAN(2467, 11),
+	CHAN(2472, 12),
+	CHAN(2484, 13),
+};
+
+static struct ieee80211_channel cl_5ghz_channels[] = {
+	CHAN(5180, 0),  /* 36 -  20MHz */
+	CHAN(5200, 1),  /* 40 -  20MHz */
+	CHAN(5220, 2),  /* 44 -  20MHz */
+	CHAN(5240, 3),  /* 48 -  20MHz */
+	CHAN(5260, 4),  /* 52 -  20MHz */
+	CHAN(5280, 5),  /* 56 -  20MHz */
+	CHAN(5300, 6),  /* 60 -  20MHz */
+	CHAN(5320, 7),  /* 64 -  20MHz */
+	CHAN(5500, 8),  /* 100 - 20MHz */
+	CHAN(5520, 9),  /* 104 - 20MHz */
+	CHAN(5540, 10), /* 108 - 20MHz */
+	CHAN(5560, 11), /* 112 - 20MHz */
+	CHAN(5580, 12), /* 116 - 20MHz */
+	CHAN(5600, 13), /* 120 - 20MHz */
+	CHAN(5620, 14), /* 124 - 20MHz */
+	CHAN(5640, 15), /* 128 - 20MHz */
+	CHAN(5660, 16), /* 132 - 20MHz */
+	CHAN(5680, 17), /* 136 - 20MHz */
+	CHAN(5700, 18), /* 140 - 20MHz */
+	CHAN(5720, 19), /* 144 - 20MHz */
+	CHAN(5745, 20), /* 149 - 20MHz */
+	CHAN(5765, 21), /* 153 - 20MHz */
+	CHAN(5785, 22), /* 157 - 20MHz */
+	CHAN(5805, 23), /* 161 - 20MHz */
+	CHAN(5825, 24), /* 165 - 20MHz */
+};
+
+static struct ieee80211_channel cl_6ghz_channels[] = {
+	CHAN(5955, 1),  /* 1 - 20MHz */
+	CHAN(5935, 2),  /* 2 - 20MHz */
+	CHAN(5975, 5),  /* 5 - 20MHz */
+	CHAN(5995, 9),  /* 9 - 20MHz */
+	CHAN(6015, 13),  /* 13 - 20MHz */
+	CHAN(6035, 17),  /* 17 - 20MHz */
+	CHAN(6055, 21),  /* 21 - 20MHz */
+	CHAN(6075, 25),  /* 25 - 20MHz */
+	CHAN(6095, 29),  /* 29 - 20MHz */
+	CHAN(6115, 33),  /* 33 - 20MHz */
+	CHAN(6135, 37),  /* 37 - 20MHz */
+	CHAN(6155, 41),  /* 41 - 20MHz */
+	CHAN(6175, 45),  /* 45 - 20MHz */
+	CHAN(6195, 49),  /* 49 - 20MHz */
+	CHAN(6215, 53),  /* 53 - 20MHz */
+	CHAN(6235, 57),  /* 57 - 20MHz */
+	CHAN(6255, 61),  /* 61 - 20MHz */
+	CHAN(6275, 65),  /* 65 - 20MHz */
+	CHAN(6295, 69),  /* 69 - 20MHz */
+	CHAN(6315, 73),  /* 73 - 20MHz */
+	CHAN(6335, 77),  /* 77 - 20MHz */
+	CHAN(6355, 81),  /* 81 - 20MHz */
+	CHAN(6375, 85),  /* 85 - 20MHz */
+	CHAN(6395, 89),  /* 89 - 20MHz */
+	CHAN(6415, 93),  /* 93 - 20MHz */
+	CHAN(6435, 97),  /* 97 - 20MHz */
+	CHAN(6455, 101),  /* 101 - 20MHz */
+	CHAN(6475, 105),  /* 105 - 20MHz */
+	CHAN(6495, 109),  /* 109 - 20MHz */
+	CHAN(6515, 113),  /* 113 - 20MHz */
+	CHAN(6535, 117),  /* 117 - 20MHz */
+	CHAN(6555, 121),  /* 121 - 20MHz */
+	CHAN(6575, 125),  /* 125 - 20MHz */
+	CHAN(6595, 129),  /* 129 - 20MHz */
+	CHAN(6615, 133),  /* 133 - 20MHz */
+	CHAN(6635, 137),  /* 137 - 20MHz */
+	CHAN(6655, 141),  /* 141 - 20MHz */
+	CHAN(6675, 145),  /* 145 - 20MHz */
+	CHAN(6695, 149),  /* 149 - 20MHz */
+	CHAN(6715, 153),  /* 153 - 20MHz */
+	CHAN(6735, 157),  /* 157 - 20MHz */
+	CHAN(6755, 161),  /* 161 - 20MHz */
+	CHAN(6775, 165),  /* 165 - 20MHz */
+	CHAN(6795, 169),  /* 169 - 20MHz */
+	CHAN(6815, 173),  /* 173 - 20MHz */
+	CHAN(6835, 177),  /* 177 - 20MHz */
+	CHAN(6855, 181),  /* 181 - 20MHz */
+	CHAN(6875, 188),  /* 185 - 20MHz */
+	CHAN(6895, 189),  /* 189 - 20MHz */
+	CHAN(6915, 193),  /* 193 - 20MHz */
+	CHAN(6935, 197),  /* 197 - 20MHz */
+	CHAN(6955, 201),  /* 201 - 20MHz */
+	CHAN(6975, 205),  /* 205 - 20MHz */
+	CHAN(6995, 209),  /* 209 - 20MHz */
+	CHAN(7015, 213),  /* 213 - 20MHz */
+	CHAN(7035, 217),  /* 217 - 20MHz */
+	CHAN(7055, 221),  /* 221 - 20MHz */
+	CHAN(7075, 225),  /* 225 - 20MHz */
+	CHAN(7095, 229),  /* 229 - 20MHz */
+	CHAN(7115, 233),  /* 233 - 20MHz */
+};
+
+static struct ieee80211_supported_band cl_band_2ghz = {
+	.channels   = cl_2ghz_channels,
+	.n_channels = ARRAY_SIZE(cl_2ghz_channels),
+	.bitrates   = cl_ratetable,
+	.n_bitrates = ARRAY_SIZE(cl_ratetable),
+	.ht_cap     = CL_HT_CAPABILITIES,
+	.vht_cap    = CL_VHT_CAPABILITIES,
+};
+
+static struct ieee80211_supported_band cl_band_5ghz = {
+	.channels   = cl_5ghz_channels,
+	.n_channels = ARRAY_SIZE(cl_5ghz_channels),
+	.bitrates   = &cl_ratetable[4],
+	.n_bitrates = ARRAY_SIZE(cl_ratetable) - 4,
+	.ht_cap     = CL_HT_CAPABILITIES,
+	.vht_cap    = CL_VHT_CAPABILITIES,
+};
+
+static struct ieee80211_supported_band cl_band_6ghz = {
+	.channels   = cl_6ghz_channels,
+	.n_channels = ARRAY_SIZE(cl_6ghz_channels),
+	.bitrates   = &cl_ratetable[4],
+	.n_bitrates = ARRAY_SIZE(cl_ratetable) - 4,
+};
+
+static const struct ieee80211_iface_limit cl_limits[] = {
+	{
+		.max   = ARRAY_SIZE(((struct cl_hw *)0)->addresses),
+		.types = BIT(NL80211_IFTYPE_AP) |
+			 BIT(NL80211_IFTYPE_STATION) |
+			 BIT(NL80211_IFTYPE_MESH_POINT),
+	},
+};
+
+#define WLAN_EXT_CAPA1_2040_BSS_COEX_MGMT_ENABLED BIT(0)
+
+static u8 cl_if_types_ext_capa_ap_24g[] = {
+	[0]  = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+	[7]  = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+};
+
+static const struct wiphy_iftype_ext_capab cl_iftypes_ext_capa_24g[] = {
+	{
+		.iftype = NL80211_IFTYPE_AP,
+		.extended_capabilities = cl_if_types_ext_capa_ap_24g,
+		.extended_capabilities_mask = cl_if_types_ext_capa_ap_24g,
+		.extended_capabilities_len = sizeof(cl_if_types_ext_capa_ap_24g),
+	},
+};
+
+static u8 cl_if_types_ext_capa_ap_5g[] = {
+	[0]  = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+	[7]  = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+};
+
+static const struct wiphy_iftype_ext_capab cl_iftypes_ext_capa_5g[] = {
+	{
+		.iftype = NL80211_IFTYPE_AP,
+		.extended_capabilities = cl_if_types_ext_capa_ap_5g,
+		.extended_capabilities_mask = cl_if_types_ext_capa_ap_5g,
+		.extended_capabilities_len = sizeof(cl_if_types_ext_capa_ap_5g),
+	},
+};
+
+static u8 cl_if_types_ext_capa_ap_6g[] = {
+	[0]  = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+	[7]  = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+};
+
+static const struct wiphy_iftype_ext_capab cl_iftypes_ext_capa_6g[] = {
+	{
+		.iftype = NL80211_IFTYPE_AP,
+		.extended_capabilities = cl_if_types_ext_capa_ap_6g,
+		.extended_capabilities_mask = cl_if_types_ext_capa_ap_6g,
+		.extended_capabilities_len = sizeof(cl_if_types_ext_capa_ap_6g),
+	},
+};
+
+static struct ieee80211_iface_combination cl_combinations[] = {
+	{
+		.limits = cl_limits,
+		.n_limits = ARRAY_SIZE(cl_limits),
+		.num_different_channels = 1,
+		.max_interfaces = ARRAY_SIZE(((struct cl_hw *)0)->addresses),
+		.beacon_int_min_gcd = 100,
+		.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20) |
+				       BIT(NL80211_CHAN_WIDTH_40) |
+				       BIT(NL80211_CHAN_WIDTH_80) |
+				       BIT(NL80211_CHAN_WIDTH_160),
+	}
+};
+
+static const int cl_ac2hwq[AC_MAX] = {
+	[NL80211_TXQ_Q_VO] = CL_HWQ_VO,
+	[NL80211_TXQ_Q_VI] = CL_HWQ_VI,
+	[NL80211_TXQ_Q_BE] = CL_HWQ_BE,
+	[NL80211_TXQ_Q_BK] = CL_HWQ_BK
+};
+
+static const int cl_ac2edca[AC_MAX] = {
+	[NL80211_TXQ_Q_VO] = EDCA_AC_VO,
+	[NL80211_TXQ_Q_VI] = EDCA_AC_VI,
+	[NL80211_TXQ_Q_BE] = EDCA_AC_BE,
+	[NL80211_TXQ_Q_BK] = EDCA_AC_BK
+};
+
+static u8 cl_he_mcs_supp_tx(struct cl_hw *cl_hw, u8 nss)
+{
+	u8 mcs = cl_hw->conf->ce_he_mcs_nss_supp_tx[nss];
+
+	switch (mcs) {
+	case WRS_MCS_7:
+		return IEEE80211_HE_MCS_SUPPORT_0_7;
+	case WRS_MCS_9:
+		return IEEE80211_HE_MCS_SUPPORT_0_9;
+	case WRS_MCS_11:
+		return IEEE80211_HE_MCS_SUPPORT_0_11;
+	}
+
+	cl_dbg_err(cl_hw, "Invalid mcs %u for nss %u. Must be 7, 9 or 11!\n", mcs, nss);
+	return IEEE80211_HE_MCS_NOT_SUPPORTED;
+}
+
+static u8 cl_he_mcs_supp_rx(struct cl_hw *cl_hw, u8 nss)
+{
+	u8 mcs = cl_hw->conf->ce_he_mcs_nss_supp_rx[nss];
+
+	switch (mcs) {
+	case WRS_MCS_7:
+		return IEEE80211_HE_MCS_SUPPORT_0_7;
+	case WRS_MCS_9:
+		return IEEE80211_HE_MCS_SUPPORT_0_9;
+	case WRS_MCS_11:
+		return IEEE80211_HE_MCS_SUPPORT_0_11;
+	}
+
+	cl_dbg_err(cl_hw, "Invalid mcs %u for nss %u. Must be 7, 9 or 11!\n", mcs, nss);
+	return IEEE80211_HE_MCS_NOT_SUPPORTED;
+}
+
+static u8 cl_vht_mcs_supp_tx(struct cl_hw *cl_hw, u8 nss)
+{
+	u8 mcs = cl_hw->conf->ce_vht_mcs_nss_supp_tx[nss];
+
+	switch (mcs) {
+	case WRS_MCS_7:
+		return IEEE80211_VHT_MCS_SUPPORT_0_7;
+	case WRS_MCS_8:
+		return IEEE80211_VHT_MCS_SUPPORT_0_8;
+	case WRS_MCS_9:
+		return IEEE80211_VHT_MCS_SUPPORT_0_9;
+	}
+
+	cl_dbg_err(cl_hw, "Invalid mcs %u for nss %u. Must be 7-9!\n", mcs, nss);
+	return IEEE80211_VHT_MCS_NOT_SUPPORTED;
+}
+
+static u8 cl_vht_mcs_supp_rx(struct cl_hw *cl_hw, u8 nss)
+{
+	u8 mcs = cl_hw->conf->ce_vht_mcs_nss_supp_rx[nss];
+
+	switch (mcs) {
+	case WRS_MCS_7:
+		return IEEE80211_VHT_MCS_SUPPORT_0_7;
+	case WRS_MCS_8:
+		return IEEE80211_VHT_MCS_SUPPORT_0_8;
+	case WRS_MCS_9:
+		return IEEE80211_VHT_MCS_SUPPORT_0_9;
+	}
+
+	cl_dbg_err(cl_hw, "Invalid mcs %u for nss %u. Must be 7-9!\n", mcs, nss);
+	return IEEE80211_VHT_MCS_NOT_SUPPORTED;
+}
+
+static void cl_set_he_6ghz_capab(struct cl_hw *cl_hw)
+{
+	struct ieee80211_he_6ghz_capa *he_6ghz_cap0 = &cl_hw->iftype_data[0].he_6ghz_capa;
+	struct ieee80211_he_6ghz_capa *he_6ghz_cap1 = &cl_hw->iftype_data[1].he_6ghz_capa;
+	struct ieee80211_he_6ghz_capa *he_6ghz_cap2 = &cl_hw->iftype_data[2].he_6ghz_capa;
+
+	he_6ghz_cap0->capa = cpu_to_le16(IEEE80211_HT_MPDU_DENSITY_1);
+
+	he_6ghz_cap0->capa |=
+		cpu_to_le16(cl_hw->conf->ci_max_mpdu_len << HE_6GHZ_CAP_MAX_MPDU_LEN_OFFSET);
+	he_6ghz_cap0->capa |=
+		cpu_to_le16(IEEE80211_VHT_MAX_AMPDU_1024K << HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP_OFFSET);
+
+	he_6ghz_cap0->capa |= cpu_to_le16(IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS |
+					  IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS);
+
+	he_6ghz_cap1->capa = he_6ghz_cap0->capa;
+	he_6ghz_cap2->capa = he_6ghz_cap0->capa;
+}
+
+static void _cl_set_he_capab(struct cl_hw *cl_hw, u8 idx)
+{
+	struct ieee80211_sta_he_cap *he_cap = &cl_hw->iftype_data[idx].he_cap;
+	struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp = &he_cap->he_mcs_nss_supp;
+	struct ieee80211_he_cap_elem *he_cap_elem = &he_cap->he_cap_elem;
+	u8 rx_nss = cl_hw->conf->ce_rx_nss;
+	u8 tx_nss = cl_hw->conf->ce_tx_nss;
+	int i = 0;
+
+	if (BAND_IS_5G_6G(cl_hw)) {
+		he_cap_elem->phy_cap_info[0] |=
+			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+		for (i = 0; i < rx_nss; i++)
+			he_mcs_nss_supp->rx_mcs_160 |=
+				cpu_to_le16(cl_he_mcs_supp_rx(cl_hw, i) << (i * 2));
+
+		for (i = 0; i < tx_nss; i++)
+			he_mcs_nss_supp->tx_mcs_160 |=
+				cpu_to_le16(cl_he_mcs_supp_tx(cl_hw, i) << (i * 2));
+
+		he_cap_elem->phy_cap_info[0] |=
+			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+
+		for (i = 0; i < rx_nss; i++)
+			he_mcs_nss_supp->rx_mcs_80 |=
+				cpu_to_le16(cl_he_mcs_supp_rx(cl_hw, i) << (i * 2));
+
+		for (i = 0; i < tx_nss; i++)
+			he_mcs_nss_supp->tx_mcs_80 |=
+				cpu_to_le16(cl_he_mcs_supp_tx(cl_hw, i) << (i * 2));
+	} else {
+		he_cap_elem->phy_cap_info[0] |=
+			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+
+		for (i = 0; i < rx_nss; i++)
+			he_mcs_nss_supp->rx_mcs_80 |=
+				cpu_to_le16(cl_he_mcs_supp_rx(cl_hw, i) << (i * 2));
+
+		for (i = 0; i < tx_nss; i++)
+			he_mcs_nss_supp->tx_mcs_80 |=
+				cpu_to_le16(cl_he_mcs_supp_tx(cl_hw, i) << (i * 2));
+	}
+
+	for (i = rx_nss; i < 8; i++) {
+		he_mcs_nss_supp->rx_mcs_80 |=
+			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2));
+		he_mcs_nss_supp->rx_mcs_160 |=
+			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2));
+	}
+
+	for (i = tx_nss; i < 8; i++) {
+		he_mcs_nss_supp->tx_mcs_80 |=
+			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2));
+		he_mcs_nss_supp->tx_mcs_160 |=
+			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2));
+	}
+
+	if (cl_hw->conf->ci_he_rxldpc_en)
+		he_cap_elem->phy_cap_info[1] |=
+			IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+
+	if (cl_hw->conf->ci_rx_he_mu_ppdu)
+		he_cap_elem->phy_cap_info[3] |=
+			IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+
+	if (cl_hw->conf->ci_bf_en) {
+		he_cap_elem->phy_cap_info[3] |=
+			IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
+		he_cap_elem->phy_cap_info[5] |=
+			IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_4;
+	}
+}
+
+static void cl_set_he_capab(struct cl_hw *cl_hw)
+{
+	struct ieee80211_sta_he_cap *he_cap0 = &cl_hw->iftype_data[0].he_cap;
+	struct ieee80211_he_cap_elem *he_cap_elem = &he_cap0->he_cap_elem;
+	struct cl_tcv_conf *conf = cl_hw->conf;
+	u8 tf_mac_pad_dur = conf->ci_tf_mac_pad_dur;
+
+	memcpy(&cl_hw->iftype_data, cl_he_data, sizeof(cl_hw->iftype_data));
+
+	if (tf_mac_pad_dur == 1)
+		he_cap_elem->mac_cap_info[1] |= IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_8US;
+	else if (tf_mac_pad_dur == 2)
+		he_cap_elem->mac_cap_info[1] |= IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
+
+	_cl_set_he_capab(cl_hw, 0);
+	_cl_set_he_capab(cl_hw, 1);
+	_cl_set_he_capab(cl_hw, 2);
+
+	if (cl_band_is_6g(cl_hw))
+		cl_set_he_6ghz_capab(cl_hw);
+
+	cl_hw->sband.n_iftype_data = ARRAY_SIZE(cl_he_data);
+	cl_hw->sband.iftype_data = cl_hw->iftype_data;
+}
+
+#define RATE_1_MBPS   10
+#define RATE_2_MBPS   20
+#define RATE_5_5_MBPS 55
+#define RATE_11_MBPS  110
+#define RATE_6_MBPS   60
+#define RATE_9_MBPS   90
+#define RATE_12_MBPS  120
+#define RATE_18_MBPS  180
+#define RATE_24_MBPS  240
+#define RATE_36_MBPS  360
+#define RATE_48_MBPS  480
+#define RATE_54_MBPS  540
+
+static u16 cl_cap_convert_rate_to_bitmap(u16 rate)
+{
+	switch (rate) {
+	case RATE_1_MBPS:
+		return BIT(0);
+	case RATE_2_MBPS:
+		return BIT(1);
+	case RATE_5_5_MBPS:
+		return BIT(2);
+	case RATE_11_MBPS:
+		return BIT(3);
+	case RATE_6_MBPS:
+		return BIT(4);
+	case RATE_9_MBPS:
+		return BIT(5);
+	case RATE_12_MBPS:
+		return BIT(6);
+	case RATE_18_MBPS:
+		return BIT(7);
+	case RATE_24_MBPS:
+		return BIT(8);
+	case RATE_36_MBPS:
+		return BIT(9);
+	case RATE_48_MBPS:
+		return BIT(10);
+	case RATE_54_MBPS:
+		return BIT(11);
+	default:
+		return 0;
+	}
+}
+
+u16 cl_cap_set_mesh_basic_rates(struct cl_hw *cl_hw)
+{
+	int i;
+	struct cl_tcv_conf *conf = cl_hw->conf;
+	u16 basic_rates = 0;
+
+	for (i = 0; i < MESH_BASIC_RATE_MAX; i++)
+		basic_rates |= cl_cap_convert_rate_to_bitmap(conf->ci_mesh_basic_rates[i]);
+
+	return basic_rates;
+}
+
+void cl_cap_dyn_params(struct cl_hw *cl_hw)
+{
+	struct ieee80211_hw *hw = cl_hw->hw;
+	struct wiphy *wiphy = hw->wiphy;
+	struct cl_tcv_conf *conf = cl_hw->conf;
+	u8 rx_nss = conf->ce_rx_nss;
+	u8 tx_nss = conf->ce_tx_nss;
+	u8 guard_interval = conf->ci_short_guard_interval;
+	u8 i;
+	u8 bw = cl_hw->conf->ci_cap_bandwidth;
+	struct ieee80211_supported_band *sband = &cl_hw->sband;
+	struct ieee80211_sta_ht_cap *sband_ht_cap = &sband->ht_cap;
+	struct ieee80211_sta_vht_cap *sband_vht_cap = &sband->vht_cap;
+
+	if (cl_band_is_6g(cl_hw)) {
+		memcpy(sband, &cl_band_6ghz, sizeof(struct ieee80211_supported_band));
+	} else if (cl_band_is_5g(cl_hw)) {
+		memcpy(sband, &cl_band_5ghz, sizeof(struct ieee80211_supported_band));
+	} else {
+		memcpy(sband, &cl_band_2ghz, sizeof(struct ieee80211_supported_band));
+
+		if (!conf->ci_vht_cap_24g)
+			memset(&sband->vht_cap, 0, sizeof(struct ieee80211_sta_vht_cap));
+	}
+
+	/* 6GHz doesn't support HT/VHT */
+	if (!cl_band_is_6g(cl_hw)) {
+		if (bw > CHNL_BW_20)
+			sband_ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+		/* Guard_interval */
+		if (guard_interval) {
+			sband_ht_cap->cap |= IEEE80211_HT_CAP_SGI_20;
+
+			if (bw >= CHNL_BW_40)
+				sband_ht_cap->cap |= IEEE80211_HT_CAP_SGI_40;
+
+			if (bw >= CHNL_BW_80)
+				sband_vht_cap->cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+
+			if (bw == CHNL_BW_160)
+				sband_vht_cap->cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
+		}
+	}
+
+	/* Amsdu */
+	cl_rx_amsdu_hw_en(hw, conf->ce_rxamsdu_en);
+	cl_hw->txamsdu_en = conf->ce_txamsdu_en;
+
+	/* Hw flags */
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+	ieee80211_hw_set(hw, QUEUE_CONTROL);
+	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+	ieee80211_hw_set(hw, SPECTRUM_MGMT);
+	ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES);
+	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+	ieee80211_hw_set(hw, NO_AUTO_VIF);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+	ieee80211_hw_set(hw, SUPPORTS_PER_STA_GTK);
+	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+
+	wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
+	wiphy->features |= NL80211_FEATURE_AP_SCAN;
+	wiphy->available_antennas_tx = ANT_MASK(cl_hw->max_antennas);
+	wiphy->available_antennas_rx = ANT_MASK(cl_hw->max_antennas);
+
+	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
+
+	if (conf->ci_fast_rx_en) {
+		ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
+		ieee80211_hw_set(hw, AP_LINK_PS);
+	}
+
+	if (cl_band_is_6g(cl_hw)) {
+		hw->wiphy->iftype_ext_capab = cl_iftypes_ext_capa_6g;
+		hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(cl_iftypes_ext_capa_6g);
+	} else if (cl_band_is_5g(cl_hw)) {
+		hw->wiphy->iftype_ext_capab = cl_iftypes_ext_capa_5g;
+		hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(cl_iftypes_ext_capa_5g);
+	} else if (cl_band_is_24g(cl_hw)) {
+		/* Turn on "20/40 Coex Mgmt Support" bit (24g only) */
+		if (conf->ce_acs_coex_en) {
+			u8 *ext_cap = (u8 *)cl_iftypes_ext_capa_24g[0].extended_capabilities;
+
+			ext_cap[0] |= WLAN_EXT_CAPA1_2040_BSS_COEX_MGMT_ENABLED;
+		}
+
+		hw->wiphy->iftype_ext_capab = cl_iftypes_ext_capa_24g;
+		hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(cl_iftypes_ext_capa_24g);
+	}
+
+	/*
+	 * To disable the dynamic PS we say to the stack that we support it in
+	 * HW. This will force mac80211 rely on us to handle this.
+	 */
+	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+
+	if (conf->ci_agg_tx)
+		ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+
+	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+				 BIT(NL80211_IFTYPE_AP) |
+				 BIT(NL80211_IFTYPE_MESH_POINT);
+
+	wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+			WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+			WIPHY_FLAG_IBSS_RSN;
+
+	if (conf->ci_uapsd_en)
+		wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+	/* Modify MAX BSS num according to the desired config value */
+	for (i = 0; i < ARRAY_SIZE(cl_combinations); i++)
+		cl_combinations[i].max_interfaces = conf->ci_max_bss_num;
+	wiphy->iface_combinations = cl_combinations;
+	wiphy->n_iface_combinations = ARRAY_SIZE(cl_combinations);
+
+	/*
+	 * hw_scan ops may ask driver to forge active scan request. So the
+	 * scan capabs are filled in (the are same as inside mac80211).
+	 * However, they are not representing real hw_scan logic, since it will
+	 * fallback to the sw_scan for active scan request.
+	 **/
+	wiphy->max_scan_ssids = 4;
+	wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_report_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 1;
+
+	hw->max_tx_aggregation_subframes = conf->ce_max_agg_size_tx;
+	hw->max_rx_aggregation_subframes = conf->ce_max_agg_size_rx;
+
+	hw->vif_data_size = sizeof(struct cl_vif);
+	hw->sta_data_size = sizeof(struct cl_sta);
+
+	hw->extra_tx_headroom = 0;
+	hw->queues = IEEE80211_MAX_QUEUES;
+	hw->offchannel_tx_hw_queue = CL_HWQ_VO;
+
+	if (!cl_band_is_6g(cl_hw)) {
+		if (conf->ci_ht_rxldpc_en)
+			sband_ht_cap->cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+		sband_ht_cap->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+		sband_vht_cap->cap |= cl_hw->conf->ci_max_mpdu_len;
+		if (conf->ci_bf_en) {
+			sband_vht_cap->cap |=
+				IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+				IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+				(3 << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT) |
+				(3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+		}
+	}
+
+	if (cl_band_is_5g(cl_hw) || (cl_band_is_24g(cl_hw) && conf->ci_vht_cap_24g)) {
+		if (bw == CHNL_BW_160)
+			sband_vht_cap->cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+
+		sband_vht_cap->cap |= (conf->ci_max_ampdu_len_exp <<
+				       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
+
+		if (conf->ci_vht_rxldpc_en)
+			sband_vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
+
+		sband_vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
+		sband_vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+
+		sband_vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(0);
+		sband_vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(0);
+
+		for (i = 0; i < rx_nss; i++)
+			sband_vht_cap->vht_mcs.rx_mcs_map |=
+				cpu_to_le16(cl_vht_mcs_supp_rx(cl_hw, i) << (i * 2));
+
+		for (; i < 8; i++)
+			sband_vht_cap->vht_mcs.rx_mcs_map |=
+				cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2));
+
+		for (i = 0; i < tx_nss; i++)
+			sband_vht_cap->vht_mcs.tx_mcs_map |=
+				cpu_to_le16(cl_vht_mcs_supp_tx(cl_hw, i) << (i * 2));
+
+		for (; i < 8; i++)
+			sband_vht_cap->vht_mcs.tx_mcs_map |=
+				cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2));
+
+		sband_vht_cap->vht_supported = true;
+	}
+
+	/* 6GHz band supports HE only */
+	if (!cl_band_is_6g(cl_hw))
+		for (i = 0; i < rx_nss; i++)
+			sband_ht_cap->mcs.rx_mask[i] = U8_MAX;
+
+	cl_set_he_capab(cl_hw);
+
+	/* Get channels and power limitations information from ChannelInfo file */
+	cl_chan_info_init(cl_hw);
+
+	if (cl_band_is_6g(cl_hw)) {
+		wiphy->bands[NL80211_BAND_2GHZ] = NULL;
+		wiphy->bands[NL80211_BAND_5GHZ] = NULL;
+		wiphy->bands[NL80211_BAND_6GHZ] = sband;
+	} else if (cl_band_is_5g(cl_hw)) {
+		wiphy->bands[NL80211_BAND_2GHZ] = NULL;
+		wiphy->bands[NL80211_BAND_5GHZ] = sband;
+		wiphy->bands[NL80211_BAND_6GHZ] = NULL;
+	} else {
+		wiphy->bands[NL80211_BAND_2GHZ] = sband;
+		wiphy->bands[NL80211_BAND_5GHZ] = NULL;
+		wiphy->bands[NL80211_BAND_6GHZ] = NULL;
+	}
+}
+
+enum he_pkt_ext_constellations {
+	HE_PKT_EXT_BPSK = 0,
+	HE_PKT_EXT_QPSK,
+	HE_PKT_EXT_16QAM,
+	HE_PKT_EXT_64QAM,
+	HE_PKT_EXT_256QAM,
+	HE_PKT_EXT_1024QAM,
+	HE_PKT_EXT_RESERVED,
+	HE_PKT_EXT_NONE,
+};
+
+static u8 mcs_to_constellation[WRS_MCS_MAX_HE] = {
+	HE_PKT_EXT_BPSK,
+	HE_PKT_EXT_QPSK,
+	HE_PKT_EXT_QPSK,
+	HE_PKT_EXT_16QAM,
+	HE_PKT_EXT_16QAM,
+	HE_PKT_EXT_64QAM,
+	HE_PKT_EXT_64QAM,
+	HE_PKT_EXT_64QAM,
+	HE_PKT_EXT_256QAM,
+	HE_PKT_EXT_256QAM,
+	HE_PKT_EXT_1024QAM,
+	HE_PKT_EXT_1024QAM
+};
+
+#define QAM_THR_1 0
+#define QAM_THR_2 1
+#define QAM_THR_MAX 2
+
+static u8 cl_get_ppe_val(u8 *ppe, u8 ppe_pos_bit)
+{
+	u8 byte_num = ppe_pos_bit / 8;
+	u8 bit_num = ppe_pos_bit % 8;
+	u8 residue_bits;
+	u8 res;
+
+	if (bit_num <= 5)
+		return (ppe[byte_num] >> bit_num) &
+		       (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE) - 1);
+
+	/*
+	 * If bit_num > 5, we have to combine bits with next byte.
+	 * Calculate how many bits we need to take from current byte (called
+	 * here "residue_bits"), and add them to bits from next byte.
+	 */
+	residue_bits = 8 - bit_num;
+
+	res = (ppe[byte_num + 1] &
+	       (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE - residue_bits) - 1)) <<
+	      residue_bits;
+	res += (ppe[byte_num] >> bit_num) & (BIT(residue_bits) - 1);
+
+	return res;
+}
+
+static void cl_set_fixed_ppe_val(u8 pe_dur[CHNL_BW_MAX][WRS_MCS_MAX_HE], u8 dur)
+{
+	u8 val = ((dur << 6) | (dur << 4) | (dur << 2) | dur);
+
+	memset(pe_dur, val, CHNL_BW_MAX * WRS_MCS_MAX_HE);
+}
+
+void cl_cap_ppe_duration(struct cl_hw *cl_hw, struct ieee80211_sta *sta,
+			 u8 pe_dur[CHNL_BW_MAX][WRS_MCS_MAX_HE])
+{
+	/* Force NVRAM parameter */
+	if (cl_hw->conf->ci_pe_duration <= PPE_16US) {
+		cl_set_fixed_ppe_val(pe_dur, cl_hw->conf->ci_pe_duration);
+		return;
+	}
+
+	/*
+	 * If STA sets the PPE Threshold Present subfield to 0,
+	 * the value should be set according to the Nominal Packet Padding subfield
+	 */
+	if ((sta->he_cap.he_cap_elem.phy_cap_info[6] &
+	     IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0) {
+		switch (sta->he_cap.he_cap_elem.phy_cap_info[9] &
+			IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK) {
+		case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US:
+			cl_set_fixed_ppe_val(pe_dur, PPE_0US);
+			break;
+		case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US:
+			cl_set_fixed_ppe_val(pe_dur, PPE_8US);
+			break;
+		case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US:
+		default:
+			cl_set_fixed_ppe_val(pe_dur, PPE_16US);
+			break;
+		}
+
+		return;
+	}
+
+	/*
+	 * struct iwl_he_pkt_ext - QAM thresholds
+	 * The required PPE is set via HE Capabilities IE, per Nss x BW x MCS
+	 * The IE is organized in the following way:
+	 * Support for Nss x BW (or RU) matrix:
+	 *	(0=SISO, 1=MIMO2) x (0-20MHz, 1-40MHz, 2-80MHz, 3-160MHz)
+	 * Each entry contains 2 QAM thresholds for 8us and 16us:
+	 *	0=BPSK, 1=QPSK, 2=16QAM, 3=64QAM, 4=256QAM, 5=1024QAM, 6=RES, 7=NONE
+	 * i.e. QAM_th1 < QAM_th2 such if TX uses QAM_tx:
+	 *	QAM_tx < QAM_th1            --> PPE=0us
+	 *	QAM_th1 <= QAM_tx < QAM_th2 --> PPE=8us
+	 *	QAM_th2 <= QAM_tx           --> PPE=16us
+	 * @pkt_ext_qam_th: QAM thresholds
+	 *	For each Nss/Bw define 2 QAM thrsholds (0..5)
+	 *	For rates below the low_th, no need for PPE
+	 *	For rates between low_th and high_th, need 8us PPE
+	 *	For rates equal or higher then the high_th, need 16us PPE
+	 *	Nss (0-siso, 1-mimo2) x BW (0-20MHz, 1-40MHz, 2-80MHz, 3-160MHz) x
+	 *		(0-low_th, 1-high_th)
+	 */
+	u8 pkt_ext_qam_th[WRS_SS_MAX][CHNL_BW_MAX][QAM_THR_MAX];
+
+	/* If PPE Thresholds exist, parse them into a FW-familiar format. */
+	u8 nss = (sta->he_cap.ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) + 1;
+	u8 ru_index_bitmap = u32_get_bits(sta->he_cap.ppe_thres[0],
+					  IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
+	u8 *ppe = &sta->he_cap.ppe_thres[0];
+	u8 ppe_pos_bit = 7; /* Starting after PPE header */
+	u8 bw, ss, mcs, constellation;
+
+	if (nss > WRS_SS_MAX)
+		nss = WRS_SS_MAX;
+
+	for (ss = 0; ss < nss; ss++) {
+		u8 ru_index_tmp = ru_index_bitmap << 1;
+
+		for (bw = 0; bw <= cl_hw->bw; bw++) {
+			ru_index_tmp >>= 1;
+			if (!(ru_index_tmp & 1))
+				continue;
+
+			pkt_ext_qam_th[ss][bw][QAM_THR_2] = cl_get_ppe_val(ppe, ppe_pos_bit);
+			ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
+			pkt_ext_qam_th[ss][bw][QAM_THR_1] = cl_get_ppe_val(ppe, ppe_pos_bit);
+			ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
+		}
+	}
+
+	/* Reset PE duration before filling it */
+	memset(pe_dur, 0, CHNL_BW_MAX * WRS_MCS_MAX_HE);
+
+	for (ss = 0; ss < nss; ss++) {
+		for (bw = 0; bw <= cl_hw->bw; bw++) {
+			for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+				constellation = mcs_to_constellation[mcs];
+
+				if (constellation < pkt_ext_qam_th[ss][bw][QAM_THR_1])
+					pe_dur[bw][mcs] |= (PPE_0US << (ss * 2));
+				else if (constellation < pkt_ext_qam_th[ss][bw][QAM_THR_2])
+					pe_dur[bw][mcs] |= (PPE_8US << (ss * 2));
+				else
+					pe_dur[bw][mcs] |= (PPE_16US << (ss * 2));
+			}
+		}
+	}
+}
+
+static void cl_ops_tx_agg(struct cl_hw *cl_hw,
+			  struct sk_buff *skb,
+			  struct ieee80211_tx_info *tx_info,
+			  struct cl_sta *cl_sta)
+{
+	cl_hw->tx_packet_cntr.forward.from_mac_agg++;
+
+	if (!cl_sta) {
+		struct cl_vif *cl_vif =
+			(struct cl_vif *)tx_info->control.vif->drv_priv;
+		u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+		u8 ac = tid_to_ac[tid];
+
+		kfree_skb(skb);
+		cl_dbg_err(cl_hw, "cl_sta null in agg packet\n");
+		cl_hw->tx_packet_cntr.drop.sta_null_in_agg++;
+		cl_vif->trfc_cntrs[ac].tx_errors++;
+		return;
+	}
+
+	/* AMSDU in HW can work only with header conversion. */
+	tx_info->control.flags &= ~IEEE80211_TX_CTRL_AMSDU;
+	cl_tx_agg(cl_hw, cl_sta, skb, false, true);
+}
+
+static void cl_ops_tx_single(struct cl_hw *cl_hw,
+			     struct sk_buff *skb,
+			     struct ieee80211_tx_info *tx_info,
+			     struct cl_sta *cl_sta,
+			     struct ieee80211_sta *sta)
+{
+	bool is_vns = cl_vns_is_very_near(cl_hw, cl_sta, skb);
+
+	cl_hw->tx_packet_cntr.forward.from_mac_single++;
+	if (cl_hw->tx_db.block_prob_resp) {
+		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+		if (ieee80211_is_probe_resp(hdr->frame_control)) {
+			struct cl_vif *cl_vif = NETDEV_TO_CL_VIF(skb->dev);
+			u8 ac = cl_vif->vif->hw_queue[skb_get_queue_mapping(skb)];
+
+			cl_tx_single_free_skb(cl_hw, skb);
+			cl_hw->tx_packet_cntr.drop.probe_response++;
+			cl_vif->trfc_cntrs[ac].tx_dropped++;
+			return;
+		}
+	}
+
+	if (sta) {
+		u32 sta_vht_cap = sta->vht_cap.cap;
+		struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+		if (!(sta_vht_cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK))
+			goto out_tx;
+
+		if (ieee80211_is_assoc_resp(mgmt->frame_control)) {
+			int len = skb->len - (mgmt->u.assoc_resp.variable - skb->data);
+			const u8 *vht_cap_addr = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY,
+								 mgmt->u.assoc_resp.variable,
+								 len);
+
+			if (vht_cap_addr) {
+				struct ieee80211_vht_cap *vht_cap =
+					(struct ieee80211_vht_cap *)(2 + vht_cap_addr);
+
+				vht_cap->vht_cap_info &=
+					~(cpu_to_le32(IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK |
+						      IEEE80211_VHT_CAP_SHORT_GI_160));
+			}
+		}
+	}
+
+out_tx:
+	cl_tx_single(cl_hw, cl_sta, skb, is_vns, true);
+}
+
+static u16 cl_ops_recalc_smallest_tbtt(struct cl_hw *cl_hw)
+{
+	struct wiphy *wiphy = cl_hw->hw->wiphy;
+	u8 cmb_idx = wiphy->n_iface_combinations - 1;
+	struct cl_vif *cl_vif = NULL;
+	u16 ret = 0;
+	u8 topology = cl_hw_get_iface_conf(cl_hw);
+
+	read_lock_bh(&cl_hw->vif_db.lock);
+	list_for_each_entry(cl_vif, &cl_hw->vif_db.head, list) {
+		if (cl_vif->vif->type == NL80211_IFTYPE_STATION)
+			continue;
+		else if (ret == 0)
+			ret = cl_vif->vif->bss_conf.beacon_int;
+		else if (cl_vif->vif->bss_conf.beacon_int)
+			ret = min(ret, cl_vif->vif->bss_conf.beacon_int);
+	}
+	read_unlock_bh(&cl_hw->vif_db.lock);
+
+	if (ret == 0) {
+		WARN_ONCE(topology != CL_IFCONF_STA && topology != CL_IFCONF_MESH_ONLY,
+			  "invalid smallest beacon interval");
+		return wiphy->iface_combinations[cmb_idx].beacon_int_min_gcd;
+	}
+	return ret;
+}
+
+static void cl_ops_set_mesh_tbtt(struct cl_hw *cl_hw, u16 this_beacon_int,
+				 u16 smallest_beacon_int)
+{
+	u16 div = this_beacon_int / smallest_beacon_int;
+
+	cl_hw->mesh_tbtt_div = (div > 0) ? div : 1;
+}
+
+void cl_ops_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb)
+{
+	/*
+	 * Almost all traffic passing here is singles.
+	 * Only when opening a BA session some packets with
+	 * IEEE80211_TX_CTL_AMPDU set can pass here.
+	 * All skbs passing here did header conversion.
+	 */
+	struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_sta *sta = control->sta;
+	struct cl_sta *cl_sta = NULL;
+
+	if (sta) {
+		cl_sta = IEEE80211_STA_TO_CL_STA(sta);
+
+		/*
+		 * Prior to STA connection sta can be set but we don't
+		 * want cl_sta to be used since it's not initialized yet
+		 */
+		if (cl_sta->sta_idx == STA_IDX_INVALID)
+			cl_sta = NULL;
+	}
+
+	if (cl_recovery_in_progress(cl_hw)) {
+		cl_hw->tx_packet_cntr.drop.in_recovery++;
+
+		if (cl_sta) {
+			struct cl_vif *cl_vif = cl_sta->cl_vif;
+
+			if (cl_vif) {
+				struct ieee80211_vif *vif = cl_vif->vif;
+				u8 hw_queue;
+
+				if (vif) {
+					hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
+					cl_vif->trfc_cntrs[hw_queue].tx_dropped++;
+				}
+			}
+		}
+
+		cl_tx_drop_skb(skb);
+		return;
+	}
+
+	if (cl_sta && (tx_info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
+		goto fast_tx;
+
+	if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+		cl_ops_tx_agg(cl_hw, skb, tx_info, cl_sta);
+	else
+		cl_ops_tx_single(cl_hw, skb, tx_info, cl_sta, sta);
+
+	return;
+
+fast_tx:
+	if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+		cl_tx_fast_agg(cl_hw, cl_sta, skb, true);
+	else
+		cl_tx_fast_single(cl_hw, cl_sta, skb, true);
+}
+
+int cl_ops_start(struct ieee80211_hw *hw)
+{
+	/*
+	 * Called before the first netdevice attached to the hardware
+	 * is enabled. This should turn on the hardware and must turn on
+	 * frame reception (for possibly enabled monitor interfaces.)
+	 * Returns negative error codes, these may be seen in userspace,
+	 * or zero.
+	 * When the device is started it should not have a MAC address
+	 * to avoid acknowledging frames before a non-monitor device
+	 * is added.
+	 * Must be implemented and can sleep.
+	 * It does not return until the firmware is up and running.
+	 */
+	int error = 0;
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_tcv_conf *conf = cl_hw->conf;
+	struct cl_hw *cl_hw_other = cl_hw_other_tcv(cl_hw);
+
+	if (!cl_hw->ipc_env) {
+		CL_DBG_ERROR(cl_hw, "ipc_env is NULL!\n");
+		return -ENODEV;
+	}
+
+	/* Exits if device is already started */
+	if (WARN_ON(test_bit(CL_DEV_STARTED, &cl_hw->drv_flags)))
+		return -EBUSY;
+
+	/* Device is now started.
+	 * Set CL_DEV_STARTED bit before the calls to other messages sent to
+	 * firmware, to prevent them from being blocked*
+	 */
+
+	set_bit(CL_DEV_STARTED, &cl_hw->drv_flags);
+
+	if (!cl_recovery_in_progress(cl_hw)) {
+		/* Read version */
+		error = cl_version_update(cl_hw);
+		if (error)
+			return error;
+
+		error = cl_temperature_diff_e2p_read(cl_hw);
+		if (error)
+			return error;
+	}
+
+	/* Set firmware debug module filter */
+	error = cl_msg_tx_dbg_set_ce_mod_filter(cl_hw, conf->ci_fw_dbg_module);
+	if (error)
+		return error;
+
+	/* Set firmware debug severity level */
+	error = cl_msg_tx_dbg_set_sev_filter(cl_hw, conf->ci_fw_dbg_severity);
+	if (error)
+		return error;
+
+	/* Set firmware rate fallbacks */
+	error = cl_msg_tx_set_rate_fallback(cl_hw);
+	if (error)
+		return error;
+
+	error = cl_msg_tx_ndp_tx_control(cl_hw,
+					 conf->ci_sensing_ndp_tx_chain_mask,
+					 conf->ci_sensing_ndp_tx_bw,
+					 conf->ci_sensing_ndp_tx_format,
+					 conf->ci_sensing_ndp_tx_num_ltf);
+	if (error)
+		return error;
+
+	/* Set default, multicast, broadcast rate */
+	cl_rate_ctrl_set_default(cl_hw);
+#ifdef CONFIG_CL8K_DYN_MCAST_RATE
+	cl_dyn_mcast_rate_set(cl_hw);
+#endif /* CONFIG_CL8K_DYN_MCAST_RATE */
+#ifdef CONFIG_CL8K_DYN_BCAST_RATE
+	cl_dyn_bcast_rate_set(cl_hw, 0);
+#endif /* CONFIG_CL8K_DYN_BCAST_RATE */
+
+	ieee80211_wake_queues(hw);
+
+	clear_bit(CL_DEV_INIT, &cl_hw->drv_flags);
+
+	cl_edca_hw_conf(cl_hw);
+
+	if (!cl_hw->chip->conf->ce_calib_runtime_en) {
+		cl_calib_dcoc_init_calibration(cl_hw);
+
+		if (cl_hw->chip->conf->ce_production_mode)
+			cl_calib_iq_init_production(cl_hw);
+		else if (!cl_hw_other || test_bit(CL_DEV_STARTED, &cl_hw_other->drv_flags))
+			cl_calib_iq_init_calibration(cl_hw);
+	}
+
+	return error;
+}
+
+void cl_ops_stop(struct ieee80211_hw *hw)
+{
+	/*
+	 * Called after last netdevice attached to the hardware
+	 * is disabled. This should turn off the hardware (at least
+	 * it must turn off frame reception.)
+	 * May be called right after add_interface if that rejects
+	 * an interface. If you added any work onto the mac80211 workqueue
+	 * you should ensure to cancel it on this callback.
+	 * Must be implemented and can sleep.
+	 */
+	struct cl_hw *cl_hw = hw->priv;
+
+	/* Stop mac80211 queues */
+	ieee80211_stop_queues(hw);
+
+	/* Go to idle */
+	cl_msg_tx_set_idle(cl_hw, MAC_IDLE_SYNC, true);
+
+	/*
+	 * Clear CL_DEV_STARTED to prevent message to be sent (besides reset and start).
+	 * It also blocks transmission of new packets
+	 */
+	clear_bit(CL_DEV_STARTED, &cl_hw->drv_flags);
+
+	cl_hw->num_ap_started = 0;
+	cl_hw->channel = 0;
+	cl_hw->radio_status = RADIO_STATUS_OFF;
+}
+
+static int cl_add_interface_to_firmware(struct cl_hw *cl_hw,
+					struct ieee80211_vif *vif, u8 vif_index)
+{
+	struct mm_add_if_cfm *add_if_cfm;
+	int ret = 0;
+
+	/* Forward the information to the firmware */
+	ret = cl_msg_tx_add_if(cl_hw, vif, vif_index);
+	if (ret)
+		return ret;
+
+	add_if_cfm = (struct mm_add_if_cfm *)(cl_hw->msg_cfm_params[MM_ADD_IF_CFM]);
+	if (!add_if_cfm)
+		return -ENOMSG;
+
+	if (add_if_cfm->status != 0) {
+		cl_dbg_verbose(cl_hw, "Status Error (%u)\n", add_if_cfm->status);
+		ret = -EIO;
+	}
+
+	cl_msg_tx_free_cfm_params(cl_hw, MM_ADD_IF_CFM);
+
+	return ret;
+}
+
+static enum cl_iface_conf cl_recalc_hw_iface(struct cl_hw *cl_hw)
+{
+	struct cl_vif *cl_vif = NULL;
+	u8 num_ap = 0, num_sta = 0, num_mp = 0;
+
+	read_lock_bh(&cl_hw->vif_db.lock);
+	list_for_each_entry(cl_vif, &cl_hw->vif_db.head, list) {
+		switch (cl_vif->vif->type) {
+		case NL80211_IFTYPE_AP:
+			num_ap++;
+			break;
+		case NL80211_IFTYPE_STATION:
+			num_sta++;
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			num_mp++;
+			break;
+		default:
+			read_unlock_bh(&cl_hw->vif_db.lock);
+			return CL_IFCONF_MAX;
+		}
+	}
+	read_unlock_bh(&cl_hw->vif_db.lock);
+
+	if (num_ap > 0 && num_sta == 0 && num_mp == 0)
+		return CL_IFCONF_AP;
+	if (num_ap == 0 && num_sta == 1 && num_mp == 0)
+		return CL_IFCONF_STA;
+	if (num_ap == 1 && num_sta == 1 && num_mp == 0)
+		return CL_IFCONF_REPEATER;
+	if (num_ap > 0 && num_sta == 0 && num_mp == 1)
+		return CL_IFCONF_MESH_AP;
+	if (num_ap == 0 && num_sta == 0 && num_mp == 1)
+		return CL_IFCONF_MESH_ONLY;
+
+	return CL_IFCONF_MAX;
+}
+
+int cl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	/*
+	 * Called when a netdevice attached to the hardware is
+	 * enabled. Because it is not called for monitor mode devices, start
+	 * and stop must be implemented.
+	 * The driver should perform any initialization it needs before
+	 * the device can be enabled. The initial configuration for the
+	 * interface is given in the conf parameter.
+	 * The callback may refuse to add an interface by returning a
+	 * negative error code (which will be seen in userspace.)
+	 * Must be implemented and can sleep.
+	 */
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_chip *chip = cl_hw->chip;
+	struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+	struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
+	struct net_device *dev = NULL;
+	u8 ac;
+
+	if (!wdev)
+		return -ENODEV;
+
+	dev = wdev->netdev;
+	if (!dev)
+		return -ENODEV;
+
+	/*
+	 * In recovery just send the message to firmware and exit
+	 * (also make sure cl_vif already exists).
+	 */
+	if (cl_recovery_in_progress(cl_hw) && cl_vif_get_by_dev(cl_hw, dev))
+		return cl_add_interface_to_firmware(cl_hw, vif, cl_vif->vif_index);
+
+	cl_vif->cl_hw = cl_hw;
+	cl_vif->vif = vif;
+	cl_vif->dev = dev;
+	cl_vif->vif_index = cl_mac_addr_find_idx(cl_hw, vif->addr);
+
+	/* MAC address not found - invalid address */
+	if (cl_vif->vif_index == BSS_INVALID_IDX) {
+		cl_dbg_err(cl_hw, "Error: Invalid MAC address %pM for vif %s\n",
+			   vif->addr, dev->name);
+
+		return -EINVAL;
+	}
+
+	if (chip->conf->ce_production_mode || vif->type == NL80211_IFTYPE_STATION)
+		cl_vif->tx_en = true;
+
+	cl_vif_key_init(cl_vif);
+
+	if (cl_add_interface_to_firmware(cl_hw, vif, cl_vif->vif_index))
+		return -EINVAL;
+
+	cl_vif->conn_data = kzalloc(sizeof(*cl_vif->conn_data), GFP_KERNEL);
+	if (!cl_vif->conn_data) {
+		cl_dbg_verbose(cl_hw, "Memory allocation for conn_data failed !!!\n");
+		return -ENOMEM;
+	}
+
+	if (vif->type != NL80211_IFTYPE_STATION)
+		vif->cab_queue = CL_HWQ_VO;
+
+	cl_vif_add(cl_hw, cl_vif);
+	cl_hw_set_iface_conf(cl_hw, cl_recalc_hw_iface(cl_hw));
+
+	for (ac = 0; ac < AC_MAX; ac++)
+		vif->hw_queue[ac] = cl_ac2hwq[ac];
+
+	if (cl_radio_is_on(cl_hw) && vif->type == NL80211_IFTYPE_AP)
+		cl_vif->tx_en = true;
+
+	/* Set active state in station mode after ifconfig down and up */
+	if (cl_hw->conf->ce_listener_en)
+		cl_radio_on(cl_hw);
+	else if (cl_radio_is_on(cl_hw) && vif->type == NL80211_IFTYPE_STATION)
+		cl_msg_tx_set_idle(cl_hw, MAC_ACTIVE, true);
+
+	if (vif->type == NL80211_IFTYPE_MESH_POINT) {
+		tasklet_init(&cl_hw->tx_mesh_bcn_task, cl_tx_bcn_mesh_task,
+			     (unsigned long)cl_vif);
+		cl_radio_on(cl_hw);
+		cl_vif->tx_en = true;
+		cl_vif->mesh_basic_rates = cl_cap_set_mesh_basic_rates(cl_hw);
+	}
+
+	return 0;
+}
+
+void cl_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	/*
+	 * Notifies a driver that an interface is going down.
+	 * The stop callback is called after this if it is the last interface
+	 * and no monitor interfaces are present.
+	 * When all interfaces are removed, the MAC address in the hardware
+	 * must be cleared so the device no longer acknowledges packets,
+	 * the mac_addr member of the conf structure is, however, set to the
+	 * MAC address of the device going away.
+	 * Hence, this callback must be implemented. It can sleep.
+	 */
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+
+	if (vif->type == NL80211_IFTYPE_MESH_POINT)
+		tasklet_kill(&cl_hw->tx_mesh_bcn_task);
+
+	if (!cl_recovery_in_progress(cl_hw)) {
+		kfree(cl_vif->conn_data);
+		cl_vif_remove(cl_hw, cl_vif);
+		cl_msg_tx_remove_if(cl_hw, cl_vif->vif_index);
+	} else {
+		cl_vif_remove(cl_hw, cl_vif);
+	}
+	cl_hw_set_iface_conf(cl_hw, cl_recalc_hw_iface(cl_hw));
+
+	cl_vif_key_deinit(cl_vif);
+
+	cl_vif->cl_hw = NULL;
+	cl_vif->vif = NULL;
+	cl_vif->dev = NULL;
+}
+
+static int cl_ops_conf_change_channel(struct ieee80211_hw *hw)
+{
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_chip *chip = cl_hw->chip;
+	struct cfg80211_chan_def *chandef = &hw->conf.chandef;
+	enum nl80211_chan_width width = chandef->width;
+	u32 primary = chandef->chan->center_freq;
+	u32 center = chandef->center_freq1;
+	u32 channel = ieee80211_frequency_to_channel(primary);
+	u8 bw = cl_width_to_bw(width);
+	int ret = 0;
+
+	if (!test_bit(CL_DEV_STARTED, &cl_hw->drv_flags))
+		return 0;
+
+	/* WA: for the first set-channel in production mode use the nvram values */
+	if (cl_hw_is_prod_or_listener(cl_hw) || !IS_REAL_PHY(chip)) {
+		ret = cl_chandef_get_default(cl_hw, &channel, &bw,
+					     &width, &primary, &center);
+
+		if (ret != 0)
+			return ret;
+	}
+
+	cl_dbg_trace(cl_hw,
+		     "channel(%u), primary(%u), center(%u), width(%u), bw(%u)\n",
+		     channel, primary, center, width, bw);
+
+	if (cl_hw->channel == channel &&
+	    cl_hw->bw == bw &&
+	    cl_hw->primary_freq == primary &&
+	    cl_hw->center_freq == center)
+		goto dfs_cac;
+
+	/*
+	 * Flush the pending data to ensure that we will finish the pending
+	 * transmissions before changing the channel
+	 */
+	if (IS_REAL_PHY(chip))
+		cl_ops_flush(hw, NULL, -1, false);
+
+	if (cl_hw->chip->conf->ce_calib_runtime_en)
+		ret = cl_calib_runtime_and_switch_channel(cl_hw, channel, bw, primary, center);
+	else
+		ret = cl_msg_tx_set_channel(cl_hw, channel, bw, primary, center,
+					    CL_CALIB_PARAMS_DEFAULT_STRUCT);
+	if (ret)
+		return -EIO;
+
+	/**
+	 * Set preffered channel type to HT+/- based on current hapd
+	 * configuration.
+	 */
+	if (cl_band_is_24g(cl_hw)) {
+		u8 ct = cfg80211_get_chandef_type(&hw->conf.chandef);
+
+		switch (ct) {
+		case NL80211_CHAN_HT40PLUS:
+		case NL80211_CHAN_HT40MINUS:
+			if (ct != cl_hw->ht40_preffered_ch_type) {
+				cl_dbg_info(cl_hw, "HT40 preffered channel type=%s\n",
+					    ct == NL80211_CHAN_HT40PLUS ? "HT+" : "HT-");
+				cl_hw->ht40_preffered_ch_type = ct;
+			}
+		}
+	}
+
+	cl_wrs_api_bss_set_bw(cl_hw, bw);
+
+dfs_cac:
+	/*
+	 * TODO: This callback is being spawned even in STA mode, moreover,
+	 * "start_ap" comes later - it is unclear whether we are an AP at this
+	 * stage. Likely, may be solved by moving "force_cac_*" states to beginning
+	 * of "start_ap", but the request should stay in current callback
+	 */
+	if (!cl_band_is_5g(cl_hw))
+		return 0;
+
+	/*
+	 * Radar listening may occur at DFS channels during in-service mode,
+	 * so CAC may clear the channels, but radar listening should be
+	 * still active, and should start it as soon as we can.
+	 */
+	if (hw->conf.radar_enabled) {
+		/* If channel policy demans to be in CAC - need to request it */
+		if (!cl_dfs_is_in_cac(cl_hw) &&
+		    chandef->chan->dfs_state == NL80211_DFS_USABLE)
+			cl_dfs_request_cac(cl_hw, true);
+
+		if (!cl_dfs_radar_listening(cl_hw))
+			cl_dfs_radar_listen_start(cl_hw);
+	} else {
+		/*
+		 * No sense to continue be in silent mode if the channel was
+		 * cleared
+		 */
+
+		if (cl_dfs_is_in_cac(cl_hw) &&
+		    chandef->chan->dfs_state == NL80211_DFS_AVAILABLE)
+			cl_dfs_request_cac(cl_hw, false);
+
+		if (cl_dfs_radar_listening(cl_hw))
+			cl_dfs_radar_listen_end(cl_hw);
+	}
+	/*
+	 * We have just finished channel switch.
+	 * Now, check what to do with CAC.
+	 */
+	if (cl_dfs_requested_cac(cl_hw))
+		cl_dfs_force_cac_start(cl_hw);
+	else if (cl_dfs_is_in_cac(cl_hw))
+		cl_dfs_force_cac_end(cl_hw);
+
+	return 0;
+}
+
+int cl_ops_config(struct ieee80211_hw *hw, u32 changed)
+{
+	/*
+	 * Handler for configuration requests. IEEE 802.11 code calls this
+	 * function to change hardware configuration, e.g., channel.
+	 * This function should never fail but returns a negative error code
+	 * if it does. The callback can sleep
+	 */
+	int error = 0;
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
+		error = cl_ops_conf_change_channel(hw);
+
+	return error;
+}
+
+/*
+ * @bss_info_changed: Handler for configuration requests related to BSS
+ *  parameters that may vary during BSS's lifespan, and may affect low
+ *  level driver (e.g. assoc/disassoc status, erp parameters).
+ *  This function should not be used if no BSS has been set, unless
+ *  for association indication. The @changed parameter indicates which
+ *  of the bss parameters has changed when a call is made. The callback
+ *  can sleep.
+ */
+void cl_ops_bss_info_changed(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_bss_conf *info,
+			     u32 changed)
+{
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		if (cl_msg_tx_set_associated(cl_hw, info))
+			return;
+	}
+
+	if (changed & BSS_CHANGED_BSSID) {
+		if (cl_msg_tx_set_bssid(cl_hw, info->bssid, cl_vif->vif_index))
+			return;
+	}
+
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		u16 smallest_int = cl_ops_recalc_smallest_tbtt(cl_hw);
+
+		cl_hw->smallest_beacon_int = smallest_int;
+
+		if (vif->type == NL80211_IFTYPE_AP ||
+		    cl_hw_get_iface_conf(cl_hw) == CL_IFCONF_MESH_ONLY) {
+			if (cl_msg_tx_set_beacon_int(cl_hw, info->beacon_int,
+						     cl_vif->vif_index))
+				return;
+			if (cl_msg_tx_dtim(cl_hw, info->dtim_period))
+				return;
+		}
+
+		if (vif->type == NL80211_IFTYPE_MESH_POINT &&
+		    cl_hw_get_iface_conf(cl_hw) == CL_IFCONF_MESH_AP) {
+			cl_ops_set_mesh_tbtt(cl_hw, info->beacon_int, smallest_int);
+		}
+	}
+
+	if (changed & BSS_CHANGED_BASIC_RATES) {
+		int shift = hw->wiphy->bands[hw->conf.chandef.chan->band]->bitrates[0].hw_value;
+
+		if (vif->type == NL80211_IFTYPE_MESH_POINT)
+			if (cl_vif->mesh_basic_rates)
+				info->basic_rates = cl_vif->mesh_basic_rates;
+
+		if (cl_msg_tx_set_basic_rates(cl_hw, info->basic_rates << shift))
+			return;
+		/* TODO: check if cl_msg_tx_set_mode() should be called */
+	}
+
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		/*
+		 * We must be in 11g mode here
+		 * TODO: we can add a check on the mode
+		 */
+		if (cl_msg_tx_set_slottime(cl_hw, info->use_short_slot))
+			return;
+	}
+
+	if (changed & BSS_CHANGED_BANDWIDTH)
+		cl_wrs_api_bss_set_bw(cl_hw, cl_width_to_bw(info->chandef.width));
+
+	if (changed & BSS_CHANGED_TXPOWER) {
+		if (info->txpower_type == NL80211_TX_POWER_FIXED) {
+			cl_hw->new_tx_power = info->txpower;
+			cl_power_tables_update(cl_hw, &cl_hw->phy_data_info.data->pwr_tables);
+			cl_msg_tx_refresh_power(cl_hw);
+		}
+	}
+
+	if (changed & BSS_CHANGED_BEACON) {
+		struct beacon_data *beacon = NULL;
+		struct ieee80211_sub_if_data *sdata =
+			container_of(vif, struct ieee80211_sub_if_data, vif);
+		struct ieee80211_ht_cap *ht_cap = NULL;
+		struct ieee80211_vht_cap *vht_cap = NULL;
+		struct ieee80211_he_cap_elem *he_cap = NULL;
+		bool sgi_en = false;
+		u8 hw_mode = cl_hw->hw_mode;
+		enum cl_wireless_mode wireless_mode = cl_hw->wireless_mode;
+
+		rcu_read_lock();
+
+		if (sdata->vif.type == NL80211_IFTYPE_AP)
+			beacon = rcu_dereference(sdata->u.ap.beacon);
+		else if (ieee80211_vif_is_mesh(&sdata->vif))
+			beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+		if (beacon) {
+			size_t ies_len = beacon->tail_len;
+			const u8 *ies = beacon->tail;
+			const u8 *cap =  NULL;
+			int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+			int len = beacon->head_len - var_offset;
+			const u8 *var_pos = beacon->head + var_offset;
+			const u8 *rate_ie = NULL;
+
+			cl_vif->wmm_enabled = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+								      WLAN_OUI_TYPE_MICROSOFT_WMM,
+								      ies,
+								      ies_len);
+			cl_dbg_info(cl_hw, "vif=%d wmm_enabled=%d\n",
+				    cl_vif->vif_index,
+				    cl_vif->wmm_enabled);
+
+			cap = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
+			if (cap && cap[1] >= sizeof(*ht_cap)) {
+				ht_cap = (void *)(cap + 2);
+				sgi_en |= (le16_to_cpu(ht_cap->cap_info) &
+					   IEEE80211_HT_CAP_SGI_20) ||
+					  (le16_to_cpu(ht_cap->cap_info) &
+					   IEEE80211_HT_CAP_SGI_40);
+			}
+
+			cap = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, ies, ies_len);
+			if (cap && cap[1] >= sizeof(*vht_cap)) {
+				vht_cap = (void *)(cap + 2);
+				sgi_en |= (le32_to_cpu(vht_cap->vht_cap_info) &
+					   IEEE80211_VHT_CAP_SHORT_GI_80) ||
+					  (le32_to_cpu(vht_cap->vht_cap_info) &
+					   IEEE80211_VHT_CAP_SHORT_GI_160);
+			}
+
+			cap = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ies, ies_len);
+			if (cap && cap[1] >= sizeof(*he_cap) + 1)
+				he_cap = (void *)(cap + 3);
+
+			rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+			if (rate_ie) {
+				if (cl_band_is_24g(cl_hw))
+					if (cl_is_valid_g_rates(rate_ie))
+						hw_mode = cl_hw->conf->ci_cck_in_hw_mode ?
+							     HW_MODE_BG : HW_MODE_G;
+					else
+						hw_mode = HW_MODE_B;
+				else
+					hw_mode = HW_MODE_A;
+			}
+		} else {
+			cl_dbg_warn(cl_hw, "beacon_data not set!\n");
+		}
+
+		rcu_read_unlock();
+
+		/*
+		 * FIXME: 1. WRS has no VIF-specific capabs settings.
+		 *        2. WRS has no BW-specific SGI configuration support.
+		 **/
+
+		/* If found any capabs info and state is different - update sgi */
+		if ((ht_cap || vht_cap) && (cl_wrs_api_bss_is_sgi_en(cl_hw) != sgi_en))
+			cl_wrs_api_bss_set_sgi(cl_hw, sgi_en);
+
+		if (hw_mode != cl_hw->hw_mode) {
+			cl_hw->hw_mode = hw_mode;
+			sgi_en =
+				(ht_cap || vht_cap) ? sgi_en : cl_hw->conf->ci_short_guard_interval;
+#ifdef CONFIG_CL8K_DYN_BCAST_RATE
+			cl_dyn_bcast_update(cl_hw);
+#endif /* CONFIG_CL8K_DYN_BCAST_RATE */
+#ifdef CONFIG_CL8K_DYN_MCAST_RATE
+			cl_dyn_mcast_update(cl_hw);
+#endif /* CONFIG_CL8K_DYN_MCAST_RATE */
+			cl_wrs_api_bss_capab_update(cl_hw, cl_hw->bw, sgi_en);
+		}
+
+		wireless_mode = cl_recalc_wireless_mode(cl_hw, !!ht_cap, !!vht_cap, !!he_cap);
+		if (wireless_mode != cl_hw->wireless_mode) {
+			sgi_en =
+				(ht_cap || vht_cap) ? sgi_en : cl_hw->conf->ci_short_guard_interval;
+			cl_hw->wireless_mode = wireless_mode;
+#ifdef CONFIG_CL8K_DYN_MCAST_RATE
+			cl_dyn_mcast_update(cl_hw);
+#endif /* CONFIG_CL8K_DYN_MCAST_RATE */
+			cl_wrs_api_bss_capab_update(cl_hw, cl_hw->bw, sgi_en);
+		}
+	}
+}
+
+int cl_ops_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	set_bit(CL_DEV_AP_STARTED, &cl_hw->drv_flags);
+
+	cl_hw->num_ap_started++;
+	if (cl_hw->conf->ce_radio_on) {
+		if (cl_radio_is_off(cl_hw))
+			cl_radio_on(cl_hw);
+
+		return 0;
+	}
+
+	/*
+	 * Set active state when cl_ops_start_ap() is called not during first driver start
+	 * but rather after removing all interfaces and then doing up again to one interface.
+	 */
+	if (cl_radio_is_on(cl_hw) && !cl_recovery_in_progress(cl_hw))
+		cl_msg_tx_set_idle(cl_hw, MAC_ACTIVE, true);
+
+	return 0;
+}
+
+void cl_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	/*
+	 * Unset CL_DEV_AP_STARTED in order to avoid
+	 * calling cl_ops_conf_change_channel after unloading the driver
+	 */
+	clear_bit(CL_DEV_AP_STARTED, &cl_hw->drv_flags);
+
+	cl_hw->num_ap_started--;
+
+	if (!cl_hw->num_ap_started)
+		cl_hw->channel = 0;
+}
+
+u64 cl_ops_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list)
+{
+	return netdev_hw_addr_list_count(mc_list);
+}
+
+void cl_ops_configure_filter(struct ieee80211_hw *hw, u32 changed_flags,
+			     u32 *total_flags, u64 multicast)
+{
+	/*
+	 * configure_filter: Configure the device's RX filter.
+	 * See the section "Frame filtering" for more information.
+	 * This callback must be implemented and can sleep.
+	 */
+	struct cl_hw *cl_hw = hw->priv;
+
+	cl_dbg_trace(cl_hw, "total_flags = 0x%08x\n", *total_flags);
+
+	/*
+	 * Reset our filter flags since our start/stop ops reset
+	 * the programmed settings
+	 */
+	if (!test_bit(CL_DEV_STARTED, &cl_hw->drv_flags)) {
+		*total_flags = 0;
+		return;
+	}
+
+	if (multicast)
+		*total_flags |= FIF_ALLMULTI;
+	else
+		*total_flags &= ~FIF_ALLMULTI;
+
+	/* TODO: optimize with changed_flags vs multicast */
+	cl_msg_tx_set_filter(cl_hw, *total_flags, false);
+
+	*total_flags &= ~(1 << 31);
+}
+
+int cl_ops_set_key(struct ieee80211_hw *hw,
+		   enum set_key_cmd cmd,
+		   struct ieee80211_vif *vif,
+		   struct ieee80211_sta *sta,
+		   struct ieee80211_key_conf *key)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	return cl_key_set(cl_hw, cmd, vif, sta, key);
+}
+
+void cl_ops_sw_scan_start(struct ieee80211_hw *hw,
+			  struct ieee80211_vif *vif,
+			  const u8 *mac_addr)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	cl_hw->sw_scan_in_progress = 1;
+
+	if (cl_hw->conf->ce_radio_on &&
+	    cl_radio_is_off(cl_hw) &&
+	    vif->type == NL80211_IFTYPE_STATION)
+		cl_radio_on(cl_hw);
+
+	if (cl_dfs_is_in_cac(cl_hw))
+		cl_dfs_force_cac_end(cl_hw);
+}
+
+void cl_ops_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	cl_hw->sw_scan_in_progress = 0;
+}
+
+int cl_ops_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+		     enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state)
+{
+	struct cl_hw *cl_hw = hw->priv;
+	int error = 0;
+
+	if (old_state == new_state)
+		return 0;
+
+	if (old_state == IEEE80211_STA_NOTEXIST &&
+	    new_state == IEEE80211_STA_NONE) {
+		cl_sta_init_sta(cl_hw, sta);
+	} else if (old_state == IEEE80211_STA_AUTH &&
+		   new_state == IEEE80211_STA_ASSOC) {
+		error = cl_sta_add(cl_hw, vif, sta);
+	} else if (old_state == IEEE80211_STA_ASSOC &&
+		   new_state == IEEE80211_STA_AUTH) {
+		cl_sta_remove(cl_hw, vif, sta);
+	}
+
+	return error;
+}
+
+void cl_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		       enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+{
+	struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+	struct cl_sta *cl_sta = IEEE80211_STA_TO_CL_STA(sta);
+	bool is_ps = (bool)!cmd;
+
+	cl_sta_ps_notify(cl_hw, cl_sta, is_ps);
+}
+
+int cl_ops_conf_tx(struct ieee80211_hw *hw,
+		   struct ieee80211_vif *vif,
+		   u16 ac_queue,
+		   const struct ieee80211_tx_queue_params *params)
+{
+	/*
+	 * Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
+	 * bursting) for a hardware TX queue.
+	 * Returns a negative error code on failure.
+	 * The callback can sleep.
+	 */
+
+	/* We only handle STA edca here */
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		struct cl_hw *cl_hw = hw->priv;
+		struct ieee80211_he_mu_edca_param_ac_rec mu_edca = {0};
+		struct edca_params edca_params = {
+			.aifsn = (u8)(params->aifs),
+			.cw_min = (u8)(ilog2(params->cw_min + 1)),
+			.cw_max = (u8)(ilog2(params->cw_max + 1)),
+			.txop = (u8)(params->txop)
+		};
+
+		if (cl_hw->wireless_mode > WIRELESS_MODE_HT_VHT)
+			memcpy(&mu_edca, &params->mu_edca_param_rec, sizeof(mu_edca));
+
+		cl_edca_set(cl_hw, cl_ac2edca[ac_queue], &edca_params, &mu_edca);
+	}
+	return 0;
+}
+
+void cl_ops_sta_rc_update(struct ieee80211_hw *hw,
+			  struct ieee80211_vif *vif,
+			  struct ieee80211_sta *sta,
+			  u32 changed)
+{
+	struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+	struct cl_sta *cl_sta = IEEE80211_STA_TO_CL_STA(sta);
+	struct cl_wrs_sta *wrs_sta = &cl_sta->wrs_sta;
+	u8 bw = wrs_sta->max_rate_cap.bw;
+	u8 nss = wrs_sta->max_rate_cap.nss;
+
+	if (changed & IEEE80211_RC_SMPS_CHANGED)
+		cl_wrs_api_set_smps_mode(cl_hw, sta, sta->bandwidth);
+
+	WARN_ON(sta->rx_nss == 0);
+	if (changed & IEEE80211_RC_NSS_CHANGED)
+		nss = min_t(u8, sta->rx_nss, WRS_SS_MAX) - 1;
+
+	if (changed & IEEE80211_RC_BW_CHANGED)
+		bw = sta->bandwidth;
+
+	if ((changed & IEEE80211_RC_NSS_CHANGED) || (changed & IEEE80211_RC_BW_CHANGED))
+		cl_wrs_api_nss_or_bw_changed(cl_hw, sta, nss, bw);
+}
+
+int cl_ops_ampdu_action(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif,
+			struct ieee80211_ampdu_params *params)
+{
+	struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+	struct cl_sta *cl_sta = IEEE80211_STA_TO_CL_STA(params->sta);
+	int ret = 0;
+
+	switch (params->action) {
+	case IEEE80211_AMPDU_RX_START:
+		ret = cl_ampdu_rx_start(cl_hw, cl_sta, params->tid,
+					params->ssn, params->buf_size);
+		break;
+	case IEEE80211_AMPDU_RX_STOP:
+		cl_ampdu_rx_stop(cl_hw, cl_sta, params->tid);
+		break;
+	case IEEE80211_AMPDU_TX_START:
+		ret = cl_ampdu_tx_start(cl_hw, vif, cl_sta, params->tid,
+					params->ssn);
+		break;
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		ret = cl_ampdu_tx_operational(cl_hw, cl_sta, params->tid,
+					      params->buf_size, params->amsdu);
+		break;
+	case IEEE80211_AMPDU_TX_STOP_CONT:
+	case IEEE80211_AMPDU_TX_STOP_FLUSH:
+	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+		ret = cl_ampdu_tx_stop(cl_hw, vif, params->action, cl_sta,
+				       params->tid);
+		break;
+	default:
+		pr_warn("Error: Unknown AMPDU action (%d)\n", params->action);
+	}
+
+	return ret;
+}
+
+int cl_ops_post_channel_switch(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif)
+{
+	/* TODO: Need to handle post switch */
+	return 0;
+}
+
+void cl_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop)
+{
+	struct cl_hw *cl_hw = hw->priv;
+	int flush_duration;
+
+	if (test_bit(CL_DEV_HW_RESTART, &cl_hw->drv_flags)) {
+		cl_dbg_verbose(cl_hw, ": bypassing (CL_DEV_HW_RESTART set)\n");
+		return;
+	}
+
+	/* Wait for a maximum time of 200ms until all pending frames are flushed */
+	for (flush_duration = 0; flush_duration < 200; flush_duration++) {
+		if (!cl_txq_frames_pending(cl_hw))
+			return;
+
+		/* Lets sleep and hope for the best */
+		usleep_range(1000, 2000);
+	}
+}
+
+bool cl_ops_tx_frames_pending(struct ieee80211_hw *hw)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	return cl_txq_frames_pending(cl_hw);
+}
+
+void cl_ops_reconfig_complete(struct ieee80211_hw *hw,
+			      enum ieee80211_reconfig_type reconfig_type)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	cl_recovery_reconfig_complete(cl_hw);
+}
+
+int cl_ops_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int *dbm)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	if (cl_hw->phy_data_info.data)
+		*dbm = cl_power_get_max(cl_hw);
+	else
+		*dbm = 0;
+
+	return 0;
+}
+
+int cl_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	/* TODO: Fix this call */
+	return 0;
+}
+
+static void cl_ops_mgd_assoc(struct cl_hw *cl_hw, struct ieee80211_vif *vif)
+{
+	struct ieee80211_sub_if_data *sdata = container_of(vif, struct ieee80211_sub_if_data, vif);
+	struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+	struct ieee80211_sta *sta = ieee80211_find_sta(vif, sdata->u.mgd.bssid);
+
+	if (!sta) {
+		/* Should never happen */
+		cl_dbg_verbose(cl_hw, "sta is NULL !!!\n");
+		return;
+	}
+
+	cl_sta_mgd_add(cl_hw, cl_vif, sta);
+
+	if (cl_hw_get_iface_conf(cl_hw) == CL_IFCONF_REPEATER) {
+		cl_vif_ap_tx_enable(cl_hw, true);
+		set_bit(CL_DEV_REPEATER, &cl_hw->drv_flags);
+	}
+}
+
+static void cl_ops_mgd_disassoc(struct cl_hw *cl_hw)
+{
+	if (cl_hw_get_iface_conf(cl_hw) == CL_IFCONF_REPEATER) {
+		cl_vif_ap_tx_enable(cl_hw, false);
+		clear_bit(CL_DEV_REPEATER, &cl_hw->drv_flags);
+	}
+}
+
+void cl_ops_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+			   const struct ieee80211_event *event)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	if (event->type == MLME_EVENT) {
+		if (event->u.mlme.data == ASSOC_EVENT &&
+		    event->u.mlme.status == MLME_SUCCESS)
+			cl_ops_mgd_assoc(cl_hw, vif);
+		else if (event->u.mlme.data == DEAUTH_TX_EVENT ||
+			 event->u.mlme.data == DEAUTH_RX_EVENT)
+			cl_ops_mgd_disassoc(cl_hw);
+	}
+}
+
+/* This function is required for PS flow - do not remove */
+int cl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
+{
+	return 0;
+}
+
+int cl_ops_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+	struct cl_hw *cl_hw = hw->priv;
+
+	*rx_ant = cl_hw->mask_num_antennas;
+	*tx_ant = cl_hw->mask_num_antennas;
+
+	return 0;
+}
+
+u32 cl_ops_get_expected_throughput(struct ieee80211_hw *hw,
+				   struct ieee80211_sta *sta)
+{
+	struct cl_sta *cl_sta = (struct cl_sta *)sta->drv_priv;
+
+	return cl_sta->wrs_sta.tx_su_params.data_rate;
+}
+
+void cl_ops_sta_statistics(struct ieee80211_hw *hw,
+			   struct ieee80211_vif *vif,
+			   struct ieee80211_sta *sta,
+			   struct station_info *sinfo)
+{
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_sta *cl_sta = NULL;
+	u64 total_tx_success = 0, total_tx_fail = 0;
+	struct cl_wrs_params *wrs_params = NULL;
+
+	if (!sta)
+		return;
+
+	cl_sta = IEEE80211_STA_TO_CL_STA(sta);
+
+	/*
+	 * Since cl8k implements rate control algorithm (sets IEEE80211_HW_HAS_RATE_CONTROL)
+	 * it is needed to initialize both rx/tx bitrates manually
+	 */
+	cl_wrs_lock_bh(&cl_hw->wrs_db);
+	wrs_params = &cl_sta->wrs_sta.tx_su_params;
+	cl_wrs_fill_sinfo_rates(&sinfo->txrate, wrs_params, cl_sta);
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+	cl_wrs_unlock_bh(&cl_hw->wrs_db);
+
+	/* mac80211 will fill sinfo stats if driver not set sinfo->filled flag */
+	if (!cl_hw->conf->ci_stats_en)
+		return;
+
+	cl_stats_get_tx(cl_hw, cl_sta, &total_tx_success, &total_tx_fail);
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
+	sinfo->tx_packets = total_tx_success;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+	sinfo->tx_failed = total_tx_fail;
+
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
+	sinfo->rx_packets = cl_stats_get_rx(cl_hw, cl_sta);
+
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
+	sinfo->tx_bytes = cl_sta->tx_bytes;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
+	sinfo->rx_bytes = cl_sta->rx_bytes;
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+	sinfo->tx_retries = cl_sta->retry_count;
+
+	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
+	sinfo->signal_avg = cl_stats_get_rssi(cl_hw, cl_sta);
+}
+
+int cl_ops_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
+{
+	struct ieee80211_conf *conf = &hw->conf;
+	struct cl_hw *cl_hw = hw->priv;
+	struct ieee80211_supported_band *sband = hw->wiphy->bands[conf->chandef.chan->band];
+	struct cl_chan_scanner *scanner = cl_hw->scanner;
+	struct cl_channel_stats *scanned_channel = NULL;
+	int chan_num;
+	u8 i;
+
+	if (idx >= sband->n_channels)
+		return -ENOENT;
+
+	survey->channel = &sband->channels[idx];
+	chan_num = ieee80211_frequency_to_channel(sband->channels[idx].center_freq);
+
+	for (i = 0; i < scanner->channels_num; i++) {
+		if (scanner->channels[i].channel == chan_num) {
+			scanned_channel = &scanner->channels[i];
+			break;
+		}
+	}
+
+	if (!scanned_channel) {
+		survey->filled = 0;
+		return 0;
+	}
+
+	survey->filled = SURVEY_INFO_TIME | SURVEY_INFO_TIME_SCAN |
+			 SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME_TX |
+			 SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BUSY |
+			 SURVEY_INFO_TIME_EXT_BUSY;
+
+	survey->noise = scanned_channel->ch_noise;
+
+	survey->time = scanned_channel->scan_time_ms;
+	survey->time_scan = survey->time;
+
+	survey->time_rx = div64_u64(scanned_channel->util_time_rx, USEC_PER_MSEC);
+	survey->time_tx = div64_u64(scanned_channel->util_time_tx, USEC_PER_MSEC);
+
+	survey->time_busy = div64_u64(scanned_channel->util_time_busy, USEC_PER_MSEC);
+	survey->time_ext_busy = survey->time_busy;
+
+	return 0;
+}
+
+static void cl_scan_completion_cb(struct cl_hw *cl_hw, void *arg)
+{
+	struct cl_chan_scanner *scanner = cl_hw->scanner;
+	struct cfg80211_scan_info info = {
+		.aborted = scanner->scan_aborted,
+	};
+
+	cl_dbg_trace(cl_hw, "Completed scan request, aborted: %u\n", info.aborted);
+
+	cl_scan_channel_switch(cl_hw, scanner->prescan_channel, scanner->prescan_bw, true);
+	ieee80211_scan_completed(cl_hw->hw, &info);
+}
+
+int cl_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		   struct ieee80211_scan_request *hw_req)
+{
+	struct cfg80211_scan_request *req = &hw_req->req;
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_chan_scanner *scanner = cl_hw->scanner;
+	u8 scan_channels[MAX_CHANNELS] = {0};
+	u8 i;
+	int ret = 0;
+
+	cl_dbg_trace(cl_hw, "Hardware scan request: n_channels:%u, n_ssids:%d\n",
+		     req->n_channels, req->n_ssids);
+
+	if (cl_hw->conf->ce_radio_on && cl_radio_is_off(cl_hw))
+		cl_radio_on(cl_hw);
+
+	ret = mutex_lock_interruptible(&scanner->cl_hw->set_channel_mutex);
+	if (ret != 0)
+		return ret;
+	scanner->prescan_bw = cl_hw->bw;
+	scanner->prescan_channel = cl_hw->channel;
+	mutex_unlock(&scanner->cl_hw->set_channel_mutex);
+
+	if (req->n_ssids > 0) {
+		/*
+		 * This is active scan request. We do not support it yet, so we
+		 * need to force the mac80211 to fallback to the sw_scan.
+		 */
+		cl_dbg_trace(cl_hw, "activating fall-back strategy - sw_scan\n");
+		return 1;
+	}
+
+	if (req->n_channels > ARRAY_SIZE(scan_channels)) {
+		cl_dbg_warn(cl_hw, "invalid number of channels to scan: %u\n",
+			    req->n_channels);
+		return -ERANGE;
+	}
+
+	for (i = 0; i < req->n_channels; ++i) {
+		if (req->channels[i]->band != cl_hw->nl_band) {
+			cl_dbg_warn(cl_hw, "band %u is invalid\n", req->channels[i]->band);
+			return -EINVAL;
+		}
+		scan_channels[i] = ieee80211_frequency_to_channel(req->channels[i]->center_freq);
+	}
+
+	ret = cl_trigger_off_channel_scan(scanner, req->duration, 0,
+					  scan_channels, CHNL_BW_20, req->n_channels,
+					  cl_scan_completion_cb, NULL);
+	return ret;
+}
+
+void cl_ops_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct cl_hw *cl_hw = hw->priv;
+	struct cl_chan_scanner *scanner = cl_hw->scanner;
+
+	if (!cl_is_scan_in_progress(scanner))
+		return;
+
+	cl_abort_scan(scanner);
+	wait_event_interruptible_timeout(scanner->wq,
+					 !cl_is_scan_in_progress(scanner),
+					 msecs_to_jiffies(MSEC_PER_SEC));
+}
-- 
2.36.1


  parent reply	other threads:[~2022-05-24 11:39 UTC|newest]

Thread overview: 125+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-24 11:33 [RFC v2 00/96] wireless: cl8k driver for Celeno IEEE 802.11ax devices viktor.barna
2022-05-24 11:33 ` [RFC v2 01/96] celeno: add Kconfig viktor.barna
2022-05-24 11:33 ` [RFC v2 02/96] celeno: add Makefile viktor.barna
2022-05-24 11:33 ` [RFC v2 03/96] cl8k: add Kconfig viktor.barna
2022-05-26 18:18   ` Johannes Berg
2022-05-27  6:09     ` Kalle Valo
2022-07-11 23:04       ` Viktor Barna
2022-07-13  7:32   ` Kalle Valo
2022-05-24 11:33 ` [RFC v2 04/96] cl8k: add Makefile viktor.barna
2022-05-26 18:24   ` Johannes Berg
2022-07-13  7:39   ` Kalle Valo
2022-05-24 11:33 ` [RFC v2 05/96] cl8k: add ampdu.c viktor.barna
2022-05-26 18:19   ` Johannes Berg
2022-05-26 18:22   ` Johannes Berg
2022-05-24 11:33 ` [RFC v2 06/96] cl8k: add ampdu.h viktor.barna
2022-05-24 11:33 ` [RFC v2 07/96] cl8k: add bf.c viktor.barna
2022-05-24 17:24   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 08/96] cl8k: add bf.h viktor.barna
2022-05-24 11:33 ` [RFC v2 09/96] cl8k: add calib.c viktor.barna
2022-05-24 11:33 ` [RFC v2 10/96] cl8k: add calib.h viktor.barna
2022-05-24 11:33 ` [RFC v2 11/96] cl8k: add channel.c viktor.barna
2022-05-24 11:33 ` [RFC v2 12/96] cl8k: add channel.h viktor.barna
2022-05-24 11:33 ` [RFC v2 13/96] cl8k: add chip.c viktor.barna
2022-05-24 11:33 ` [RFC v2 14/96] cl8k: add chip.h viktor.barna
2022-05-24 11:33 ` [RFC v2 15/96] cl8k: add config.c viktor.barna
2022-05-24 11:33 ` [RFC v2 16/96] cl8k: add config.h viktor.barna
2022-05-25 18:31   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 17/96] cl8k: add debug.c viktor.barna
2022-05-24 11:33 ` [RFC v2 18/96] cl8k: add debug.h viktor.barna
2022-05-24 11:33 ` [RFC v2 19/96] cl8k: add def.h viktor.barna
2022-05-25 18:39   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 20/96] cl8k: add dfs.c viktor.barna
2022-05-24 11:33 ` [RFC v2 21/96] cl8k: add dfs.h viktor.barna
2022-05-24 11:33 ` [RFC v2 22/96] cl8k: add dsp.c viktor.barna
2022-05-24 11:33 ` [RFC v2 23/96] cl8k: add dsp.h viktor.barna
2022-05-24 11:33 ` [RFC v2 24/96] cl8k: add e2p.c viktor.barna
2022-05-24 11:33 ` [RFC v2 25/96] cl8k: add e2p.h viktor.barna
2022-05-24 11:33 ` [RFC v2 26/96] cl8k: add eeprom.h viktor.barna
2022-05-24 11:33 ` [RFC v2 27/96] cl8k: add ela.c viktor.barna
2022-05-24 11:33 ` [RFC v2 28/96] cl8k: add ela.h viktor.barna
2022-05-24 11:33 ` [RFC v2 29/96] cl8k: add enhanced_tim.c viktor.barna
2022-05-24 11:33 ` [RFC v2 30/96] cl8k: add enhanced_tim.h viktor.barna
2022-05-24 11:33 ` [RFC v2 31/96] cl8k: add fw.c viktor.barna
2022-05-24 11:33 ` [RFC v2 32/96] cl8k: add fw.h viktor.barna
2022-05-25 18:58   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 33/96] cl8k: add hw.c viktor.barna
2022-05-24 11:34 ` [RFC v2 34/96] cl8k: add hw.h viktor.barna
2022-05-24 11:34 ` [RFC v2 35/96] cl8k: add ipc_shared.h viktor.barna
2022-05-24 11:34 ` [RFC v2 36/96] cl8k: add key.c viktor.barna
2022-05-26 19:38   ` Johannes Berg
2022-07-11 23:10     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 37/96] cl8k: add key.h viktor.barna
2022-05-24 11:34 ` viktor.barna [this message]
2022-05-26 19:49   ` [RFC v2 38/96] cl8k: add mac80211.c Johannes Berg
2022-07-11 23:13     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 39/96] cl8k: add mac80211.h viktor.barna
2022-05-26 19:52   ` Johannes Berg
2022-05-24 11:34 ` [RFC v2 40/96] cl8k: add mac_addr.c viktor.barna
2022-05-26 22:31   ` Jeff Johnson
2022-05-24 11:34 ` [RFC v2 41/96] cl8k: add mac_addr.h viktor.barna
2022-05-24 11:34 ` [RFC v2 42/96] cl8k: add main.c viktor.barna
2022-05-26 23:01   ` Jeff Johnson
2022-05-24 11:34 ` [RFC v2 43/96] cl8k: add main.h viktor.barna
2022-05-24 11:34 ` [RFC v2 44/96] cl8k: add maintenance.c viktor.barna
2022-05-24 11:34 ` [RFC v2 45/96] cl8k: add maintenance.h viktor.barna
2022-05-24 11:34 ` [RFC v2 46/96] cl8k: add motion_sense.c viktor.barna
2022-05-24 11:34 ` [RFC v2 47/96] cl8k: add motion_sense.h viktor.barna
2022-05-24 11:34 ` [RFC v2 48/96] cl8k: add pci.c viktor.barna
2022-05-24 11:34 ` [RFC v2 49/96] cl8k: add pci.h viktor.barna
2022-05-24 11:34 ` [RFC v2 50/96] cl8k: add phy.c viktor.barna
2022-06-01  0:27   ` Jeff Johnson
2022-07-11 23:16     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 51/96] cl8k: add phy.h viktor.barna
2022-05-24 11:34 ` [RFC v2 52/96] cl8k: add platform.c viktor.barna
2022-05-24 11:34 ` [RFC v2 53/96] cl8k: add platform.h viktor.barna
2022-05-24 11:34 ` [RFC v2 54/96] cl8k: add power.c viktor.barna
2022-05-24 11:34 ` [RFC v2 55/96] cl8k: add power.h viktor.barna
2022-05-24 11:34 ` [RFC v2 56/96] cl8k: add radio.c viktor.barna
2022-05-24 11:34 ` [RFC v2 57/96] cl8k: add radio.h viktor.barna
2022-05-24 11:34 ` [RFC v2 58/96] cl8k: add rates.c viktor.barna
2022-05-24 11:34 ` [RFC v2 59/96] cl8k: add rates.h viktor.barna
2022-05-26 19:54   ` Johannes Berg
2022-07-11 23:17     ` Viktor Barna
2022-07-12  7:17       ` Johannes Berg
2022-05-24 11:34 ` [RFC v2 60/96] cl8k: add recovery.c viktor.barna
2022-05-24 11:34 ` [RFC v2 61/96] cl8k: add recovery.h viktor.barna
2022-05-24 11:34 ` [RFC v2 62/96] cl8k: add regdom.c viktor.barna
2022-05-24 11:34 ` [RFC v2 63/96] cl8k: add regdom.h viktor.barna
2022-05-24 11:34 ` [RFC v2 64/96] cl8k: add reg/reg_access.h viktor.barna
2022-05-24 11:34 ` [RFC v2 65/96] cl8k: add reg/reg_defs.h viktor.barna
2022-05-24 11:34 ` [RFC v2 66/96] cl8k: add rfic.c viktor.barna
2022-05-24 11:34 ` [RFC v2 67/96] cl8k: add rfic.h viktor.barna
2022-06-02 20:40   ` Jeff Johnson
2022-07-11 23:18     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 68/96] cl8k: add rx.c viktor.barna
2022-05-24 11:34 ` [RFC v2 69/96] cl8k: add rx.h viktor.barna
2022-05-24 11:34 ` [RFC v2 70/96] cl8k: add scan.c viktor.barna
2022-05-24 11:34 ` [RFC v2 71/96] cl8k: add scan.h viktor.barna
2022-05-24 11:34 ` [RFC v2 72/96] cl8k: add sounding.c viktor.barna
2022-05-24 11:34 ` [RFC v2 73/96] cl8k: add sounding.h viktor.barna
2022-05-24 11:34 ` [RFC v2 74/96] cl8k: add sta.c viktor.barna
2022-05-24 11:34 ` [RFC v2 75/96] cl8k: add sta.h viktor.barna
2022-05-24 11:34 ` [RFC v2 76/96] cl8k: add stats.c viktor.barna
2022-06-02 20:59   ` Jeff Johnson
2022-07-11 23:20     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 77/96] cl8k: add stats.h viktor.barna
2022-05-24 11:34 ` [RFC v2 78/96] cl8k: add tcv.c viktor.barna
2022-05-24 11:34 ` [RFC v2 79/96] cl8k: add tcv.h viktor.barna
2022-05-24 11:34 ` [RFC v2 80/96] cl8k: add temperature.c viktor.barna
2022-05-24 11:34 ` [RFC v2 81/96] cl8k: add temperature.h viktor.barna
2022-05-24 11:34 ` [RFC v2 82/96] cl8k: add traffic.c viktor.barna
2022-05-24 11:34 ` [RFC v2 83/96] cl8k: add traffic.h viktor.barna
2022-05-24 11:34 ` [RFC v2 84/96] cl8k: add tx.c viktor.barna
2022-05-24 11:34 ` [RFC v2 85/96] cl8k: add tx.h viktor.barna
2022-05-24 11:34 ` [RFC v2 86/96] cl8k: add utils.c viktor.barna
2022-05-24 11:34 ` [RFC v2 87/96] cl8k: add utils.h viktor.barna
2022-05-24 11:34 ` [RFC v2 88/96] cl8k: add version.c viktor.barna
2022-05-24 11:34 ` [RFC v2 89/96] cl8k: add version.h viktor.barna
2022-05-24 11:34 ` [RFC v2 90/96] cl8k: add vif.c viktor.barna
2022-05-24 11:34 ` [RFC v2 91/96] cl8k: add vif.h viktor.barna
2022-05-24 11:34 ` [RFC v2 92/96] cl8k: add vns.c viktor.barna
2022-05-24 11:34 ` [RFC v2 93/96] cl8k: add vns.h viktor.barna
2022-05-24 11:35 ` [RFC v2 94/96] cl8k: add wrs.c viktor.barna
2022-05-24 11:35 ` [RFC v2 95/96] cl8k: add wrs.h viktor.barna
2022-05-24 11:35 ` [RFC v2 96/96] wireless: add Celeno vendor viktor.barna

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220524113502.1094459-39-viktor.barna@celeno.com \
    --to=viktor.barna@celeno.com \
    --cc=aviad.brikman@celeno.com \
    --cc=davem@davemloft.net \
    --cc=eliav.farber@gmail.com \
    --cc=kuba@kernel.org \
    --cc=kvalo@codeaurora.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=maksym.kokhan@celeno.com \
    --cc=oleksandr.savchenko@celeno.com \
    --cc=shay.bar@celeno.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.