162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
462306a36Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/etherdevice.h>
862306a36Sopenharmony_ci#include <linux/moduleparam.h>
962306a36Sopenharmony_ci#include <net/netlink.h>
1062306a36Sopenharmony_ci#include <net/cfg80211.h>
1162306a36Sopenharmony_ci#include "wil6210.h"
1262306a36Sopenharmony_ci#include "wmi.h"
1362306a36Sopenharmony_ci#include "fw.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define WIL_MAX_ROC_DURATION_MS 5000
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define WIL_EDMG_CHANNEL_9_SUBCHANNELS	(BIT(0) | BIT(1))
1862306a36Sopenharmony_ci#define WIL_EDMG_CHANNEL_10_SUBCHANNELS	(BIT(1) | BIT(2))
1962306a36Sopenharmony_ci#define WIL_EDMG_CHANNEL_11_SUBCHANNELS	(BIT(2) | BIT(3))
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* WIL_EDMG_BW_CONFIGURATION define the allowed channel bandwidth
2262306a36Sopenharmony_ci * configurations as defined by IEEE 802.11 section 9.4.2.251, Table 13.
2362306a36Sopenharmony_ci * The value 5 allowing CB1 and CB2 of adjacent channels.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci#define WIL_EDMG_BW_CONFIGURATION 5
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* WIL_EDMG_CHANNELS is a bitmap that indicates the 2.16 GHz channel(s) that
2862306a36Sopenharmony_ci * are allowed to be used for EDMG transmissions in the BSS as defined by
2962306a36Sopenharmony_ci * IEEE 802.11 section 9.4.2.251.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci#define WIL_EDMG_CHANNELS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cibool disable_ap_sme;
3462306a36Sopenharmony_cimodule_param(disable_ap_sme, bool, 0444);
3562306a36Sopenharmony_ciMODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifdef CONFIG_PM
3862306a36Sopenharmony_cistatic struct wiphy_wowlan_support wil_wowlan_support = {
3962306a36Sopenharmony_ci	.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define CHAN60G(_channel, _flags) {				\
4462306a36Sopenharmony_ci	.band			= NL80211_BAND_60GHZ,		\
4562306a36Sopenharmony_ci	.center_freq		= 56160 + (2160 * (_channel)),	\
4662306a36Sopenharmony_ci	.hw_value		= (_channel),			\
4762306a36Sopenharmony_ci	.flags			= (_flags),			\
4862306a36Sopenharmony_ci	.max_antenna_gain	= 0,				\
4962306a36Sopenharmony_ci	.max_power		= 40,				\
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct ieee80211_channel wil_60ghz_channels[] = {
5362306a36Sopenharmony_ci	CHAN60G(1, 0),
5462306a36Sopenharmony_ci	CHAN60G(2, 0),
5562306a36Sopenharmony_ci	CHAN60G(3, 0),
5662306a36Sopenharmony_ci	CHAN60G(4, 0),
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Rx channel bonding mode */
6062306a36Sopenharmony_cienum wil_rx_cb_mode {
6162306a36Sopenharmony_ci	WIL_RX_CB_MODE_DMG,
6262306a36Sopenharmony_ci	WIL_RX_CB_MODE_EDMG,
6362306a36Sopenharmony_ci	WIL_RX_CB_MODE_WIDE,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int wil_rx_cb_mode_to_n_bonded(u8 cb_mode)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	switch (cb_mode) {
6962306a36Sopenharmony_ci	case WIL_RX_CB_MODE_DMG:
7062306a36Sopenharmony_ci	case WIL_RX_CB_MODE_EDMG:
7162306a36Sopenharmony_ci		return 1;
7262306a36Sopenharmony_ci	case WIL_RX_CB_MODE_WIDE:
7362306a36Sopenharmony_ci		return 2;
7462306a36Sopenharmony_ci	default:
7562306a36Sopenharmony_ci		return 1;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int wil_tx_cb_mode_to_n_bonded(u8 cb_mode)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	switch (cb_mode) {
8262306a36Sopenharmony_ci	case WMI_TX_MODE_DMG:
8362306a36Sopenharmony_ci	case WMI_TX_MODE_EDMG_CB1:
8462306a36Sopenharmony_ci		return 1;
8562306a36Sopenharmony_ci	case WMI_TX_MODE_EDMG_CB2:
8662306a36Sopenharmony_ci		return 2;
8762306a36Sopenharmony_ci	default:
8862306a36Sopenharmony_ci		return 1;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void
9362306a36Sopenharmony_ciwil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	kfree(*pdst);
9662306a36Sopenharmony_ci	*pdst = NULL;
9762306a36Sopenharmony_ci	*pdst_len = 0;
9862306a36Sopenharmony_ci	if (src_len > 0) {
9962306a36Sopenharmony_ci		*pdst = kmemdup(src, src_len, GFP_KERNEL);
10062306a36Sopenharmony_ci		if (*pdst)
10162306a36Sopenharmony_ci			*pdst_len = src_len;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int wil_num_supported_channels(struct wil6210_priv *wil)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	int num_channels = ARRAY_SIZE(wil_60ghz_channels);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (!test_bit(WMI_FW_CAPABILITY_CHANNEL_4, wil->fw_capabilities))
11062306a36Sopenharmony_ci		num_channels--;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return num_channels;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid update_supported_bands(struct wil6210_priv *wil)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct wiphy *wiphy = wil_to_wiphy(wil);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	wil_dbg_misc(wil, "update supported bands");
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	wiphy->bands[NL80211_BAND_60GHZ]->n_channels =
12262306a36Sopenharmony_ci						wil_num_supported_channels(wil);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (test_bit(WMI_FW_CAPABILITY_CHANNEL_BONDING, wil->fw_capabilities)) {
12562306a36Sopenharmony_ci		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.channels =
12662306a36Sopenharmony_ci							WIL_EDMG_CHANNELS;
12762306a36Sopenharmony_ci		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.bw_config =
12862306a36Sopenharmony_ci						      WIL_EDMG_BW_CONFIGURATION;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* Vendor id to be used in vendor specific command and events
13362306a36Sopenharmony_ci * to user space.
13462306a36Sopenharmony_ci * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID,
13562306a36Sopenharmony_ci * vendor subcmd definitions prefixed with QCA_NL80211_VENDOR_SUBCMD, and
13662306a36Sopenharmony_ci * qca_wlan_vendor_attr is open source file src/common/qca-vendor.h in
13762306a36Sopenharmony_ci * git://w1.fi/srv/git/hostap.git; the values here are just a copy of that
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci#define QCA_NL80211_VENDOR_ID	0x001374
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#define WIL_MAX_RF_SECTORS (128)
14362306a36Sopenharmony_ci#define WIL_CID_ALL (0xff)
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cienum qca_wlan_vendor_attr_rf_sector {
14662306a36Sopenharmony_ci	QCA_ATTR_MAC_ADDR = 6,
14762306a36Sopenharmony_ci	QCA_ATTR_PAD = 13,
14862306a36Sopenharmony_ci	QCA_ATTR_TSF = 29,
14962306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_INDEX = 30,
15062306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_TYPE = 31,
15162306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_MODULE_MASK = 32,
15262306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG = 33,
15362306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_MAX,
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cienum qca_wlan_vendor_attr_dmg_rf_sector_type {
15762306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_TYPE_RX,
15862306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_TYPE_TX,
15962306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cienum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
16362306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
16462306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
16562306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
16662306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
16762306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
16862306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
16962306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
17062306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* keep last */
17362306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
17462306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_MAX =
17562306a36Sopenharmony_ci	QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic const struct
17962306a36Sopenharmony_cinla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = {
18062306a36Sopenharmony_ci	[QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN },
18162306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 },
18262306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 },
18362306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 },
18462306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED },
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic const struct
18862306a36Sopenharmony_cinla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = {
18962306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 },
19062306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 },
19162306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 },
19262306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 },
19362306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 },
19462306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 },
19562306a36Sopenharmony_ci	[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 },
19662306a36Sopenharmony_ci};
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cienum qca_nl80211_vendor_subcmds {
19962306a36Sopenharmony_ci	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
20062306a36Sopenharmony_ci	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
20162306a36Sopenharmony_ci	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
20262306a36Sopenharmony_ci	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int wil_rf_sector_get_cfg(struct wiphy *wiphy,
20662306a36Sopenharmony_ci				 struct wireless_dev *wdev,
20762306a36Sopenharmony_ci				 const void *data, int data_len);
20862306a36Sopenharmony_cistatic int wil_rf_sector_set_cfg(struct wiphy *wiphy,
20962306a36Sopenharmony_ci				 struct wireless_dev *wdev,
21062306a36Sopenharmony_ci				 const void *data, int data_len);
21162306a36Sopenharmony_cistatic int wil_rf_sector_get_selected(struct wiphy *wiphy,
21262306a36Sopenharmony_ci				      struct wireless_dev *wdev,
21362306a36Sopenharmony_ci				      const void *data, int data_len);
21462306a36Sopenharmony_cistatic int wil_rf_sector_set_selected(struct wiphy *wiphy,
21562306a36Sopenharmony_ci				      struct wireless_dev *wdev,
21662306a36Sopenharmony_ci				      const void *data, int data_len);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/* vendor specific commands */
21962306a36Sopenharmony_cistatic const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
22062306a36Sopenharmony_ci	{
22162306a36Sopenharmony_ci		.info.vendor_id = QCA_NL80211_VENDOR_ID,
22262306a36Sopenharmony_ci		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG,
22362306a36Sopenharmony_ci		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
22462306a36Sopenharmony_ci			 WIPHY_VENDOR_CMD_NEED_RUNNING,
22562306a36Sopenharmony_ci		.policy = wil_rf_sector_policy,
22662306a36Sopenharmony_ci		.doit = wil_rf_sector_get_cfg
22762306a36Sopenharmony_ci	},
22862306a36Sopenharmony_ci	{
22962306a36Sopenharmony_ci		.info.vendor_id = QCA_NL80211_VENDOR_ID,
23062306a36Sopenharmony_ci		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG,
23162306a36Sopenharmony_ci		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
23262306a36Sopenharmony_ci			 WIPHY_VENDOR_CMD_NEED_RUNNING,
23362306a36Sopenharmony_ci		.policy = wil_rf_sector_policy,
23462306a36Sopenharmony_ci		.doit = wil_rf_sector_set_cfg
23562306a36Sopenharmony_ci	},
23662306a36Sopenharmony_ci	{
23762306a36Sopenharmony_ci		.info.vendor_id = QCA_NL80211_VENDOR_ID,
23862306a36Sopenharmony_ci		.info.subcmd =
23962306a36Sopenharmony_ci			QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR,
24062306a36Sopenharmony_ci		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
24162306a36Sopenharmony_ci			 WIPHY_VENDOR_CMD_NEED_RUNNING,
24262306a36Sopenharmony_ci		.policy = wil_rf_sector_policy,
24362306a36Sopenharmony_ci		.doit = wil_rf_sector_get_selected
24462306a36Sopenharmony_ci	},
24562306a36Sopenharmony_ci	{
24662306a36Sopenharmony_ci		.info.vendor_id = QCA_NL80211_VENDOR_ID,
24762306a36Sopenharmony_ci		.info.subcmd =
24862306a36Sopenharmony_ci			QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR,
24962306a36Sopenharmony_ci		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
25062306a36Sopenharmony_ci			 WIPHY_VENDOR_CMD_NEED_RUNNING,
25162306a36Sopenharmony_ci		.policy = wil_rf_sector_policy,
25262306a36Sopenharmony_ci		.doit = wil_rf_sector_set_selected
25362306a36Sopenharmony_ci	},
25462306a36Sopenharmony_ci};
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic struct ieee80211_supported_band wil_band_60ghz = {
25762306a36Sopenharmony_ci	.channels = wil_60ghz_channels,
25862306a36Sopenharmony_ci	.n_channels = ARRAY_SIZE(wil_60ghz_channels),
25962306a36Sopenharmony_ci	.ht_cap = {
26062306a36Sopenharmony_ci		.ht_supported = true,
26162306a36Sopenharmony_ci		.cap = 0, /* TODO */
26262306a36Sopenharmony_ci		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
26362306a36Sopenharmony_ci		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
26462306a36Sopenharmony_ci		.mcs = {
26562306a36Sopenharmony_ci				/* MCS 1..12 - SC PHY */
26662306a36Sopenharmony_ci			.rx_mask = {0xfe, 0x1f}, /* 1..12 */
26762306a36Sopenharmony_ci			.tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
26862306a36Sopenharmony_ci		},
26962306a36Sopenharmony_ci	},
27062306a36Sopenharmony_ci};
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic const struct ieee80211_txrx_stypes
27362306a36Sopenharmony_ciwil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
27462306a36Sopenharmony_ci	[NL80211_IFTYPE_STATION] = {
27562306a36Sopenharmony_ci		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
27662306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
27762306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
27862306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
27962306a36Sopenharmony_ci	},
28062306a36Sopenharmony_ci	[NL80211_IFTYPE_AP] = {
28162306a36Sopenharmony_ci		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
28262306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_RESP >> 4) |
28362306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
28462306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_DISASSOC >> 4) |
28562306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_AUTH >> 4) |
28662306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_REASSOC_RESP >> 4),
28762306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
28862306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
28962306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
29062306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_DISASSOC >> 4) |
29162306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_AUTH >> 4) |
29262306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_DEAUTH >> 4) |
29362306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_REASSOC_REQ >> 4)
29462306a36Sopenharmony_ci	},
29562306a36Sopenharmony_ci	[NL80211_IFTYPE_P2P_CLIENT] = {
29662306a36Sopenharmony_ci		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
29762306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
29862306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
29962306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
30062306a36Sopenharmony_ci	},
30162306a36Sopenharmony_ci	[NL80211_IFTYPE_P2P_GO] = {
30262306a36Sopenharmony_ci		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
30362306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
30462306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
30562306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
30662306a36Sopenharmony_ci	},
30762306a36Sopenharmony_ci	[NL80211_IFTYPE_P2P_DEVICE] = {
30862306a36Sopenharmony_ci		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
30962306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
31062306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
31162306a36Sopenharmony_ci		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
31262306a36Sopenharmony_ci	},
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic const u32 wil_cipher_suites[] = {
31662306a36Sopenharmony_ci	WLAN_CIPHER_SUITE_GCMP,
31762306a36Sopenharmony_ci};
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic const char * const key_usage_str[] = {
32062306a36Sopenharmony_ci	[WMI_KEY_USE_PAIRWISE]	= "PTK",
32162306a36Sopenharmony_ci	[WMI_KEY_USE_RX_GROUP]	= "RX_GTK",
32262306a36Sopenharmony_ci	[WMI_KEY_USE_TX_GROUP]	= "TX_GTK",
32362306a36Sopenharmony_ci	[WMI_KEY_USE_STORE_PTK]	= "STORE_PTK",
32462306a36Sopenharmony_ci	[WMI_KEY_USE_APPLY_PTK]	= "APPLY_PTK",
32562306a36Sopenharmony_ci};
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ciint wil_iftype_nl2wmi(enum nl80211_iftype type)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	static const struct {
33062306a36Sopenharmony_ci		enum nl80211_iftype nl;
33162306a36Sopenharmony_ci		enum wmi_network_type wmi;
33262306a36Sopenharmony_ci	} __nl2wmi[] = {
33362306a36Sopenharmony_ci		{NL80211_IFTYPE_ADHOC,		WMI_NETTYPE_ADHOC},
33462306a36Sopenharmony_ci		{NL80211_IFTYPE_STATION,	WMI_NETTYPE_INFRA},
33562306a36Sopenharmony_ci		{NL80211_IFTYPE_AP,		WMI_NETTYPE_AP},
33662306a36Sopenharmony_ci		{NL80211_IFTYPE_P2P_CLIENT,	WMI_NETTYPE_P2P},
33762306a36Sopenharmony_ci		{NL80211_IFTYPE_P2P_GO,		WMI_NETTYPE_P2P},
33862306a36Sopenharmony_ci		{NL80211_IFTYPE_MONITOR,	WMI_NETTYPE_ADHOC}, /* FIXME */
33962306a36Sopenharmony_ci	};
34062306a36Sopenharmony_ci	uint i;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
34362306a36Sopenharmony_ci		if (__nl2wmi[i].nl == type)
34462306a36Sopenharmony_ci			return __nl2wmi[i].wmi;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return -EOPNOTSUPP;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ciint wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	switch (spec_ch) {
35362306a36Sopenharmony_ci	case 1:
35462306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_1;
35562306a36Sopenharmony_ci		break;
35662306a36Sopenharmony_ci	case 2:
35762306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_2;
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	case 3:
36062306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_3;
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci	case 4:
36362306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_4;
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	case 5:
36662306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_5;
36762306a36Sopenharmony_ci		break;
36862306a36Sopenharmony_ci	case 6:
36962306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_6;
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	case 9:
37262306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_9;
37362306a36Sopenharmony_ci		break;
37462306a36Sopenharmony_ci	case 10:
37562306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_10;
37662306a36Sopenharmony_ci		break;
37762306a36Sopenharmony_ci	case 11:
37862306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_11;
37962306a36Sopenharmony_ci		break;
38062306a36Sopenharmony_ci	case 12:
38162306a36Sopenharmony_ci		*wmi_ch = WMI_CHANNEL_12;
38262306a36Sopenharmony_ci		break;
38362306a36Sopenharmony_ci	default:
38462306a36Sopenharmony_ci		return -EINVAL;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ciint wil_wmi2spec_ch(u8 wmi_ch, u8 *spec_ch)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	switch (wmi_ch) {
39362306a36Sopenharmony_ci	case WMI_CHANNEL_1:
39462306a36Sopenharmony_ci		*spec_ch = 1;
39562306a36Sopenharmony_ci		break;
39662306a36Sopenharmony_ci	case WMI_CHANNEL_2:
39762306a36Sopenharmony_ci		*spec_ch = 2;
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci	case WMI_CHANNEL_3:
40062306a36Sopenharmony_ci		*spec_ch = 3;
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	case WMI_CHANNEL_4:
40362306a36Sopenharmony_ci		*spec_ch = 4;
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	case WMI_CHANNEL_5:
40662306a36Sopenharmony_ci		*spec_ch = 5;
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case WMI_CHANNEL_6:
40962306a36Sopenharmony_ci		*spec_ch = 6;
41062306a36Sopenharmony_ci		break;
41162306a36Sopenharmony_ci	case WMI_CHANNEL_9:
41262306a36Sopenharmony_ci		*spec_ch = 9;
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	case WMI_CHANNEL_10:
41562306a36Sopenharmony_ci		*spec_ch = 10;
41662306a36Sopenharmony_ci		break;
41762306a36Sopenharmony_ci	case WMI_CHANNEL_11:
41862306a36Sopenharmony_ci		*spec_ch = 11;
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	case WMI_CHANNEL_12:
42162306a36Sopenharmony_ci		*spec_ch = 12;
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci	default:
42462306a36Sopenharmony_ci		return -EINVAL;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return 0;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ciint wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
43162306a36Sopenharmony_ci		       struct station_info *sinfo)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
43462306a36Sopenharmony_ci	struct wmi_notify_req_cmd cmd = {
43562306a36Sopenharmony_ci		.cid = cid,
43662306a36Sopenharmony_ci		.interval_usec = 0,
43762306a36Sopenharmony_ci	};
43862306a36Sopenharmony_ci	struct {
43962306a36Sopenharmony_ci		struct wmi_cmd_hdr wmi;
44062306a36Sopenharmony_ci		struct wmi_notify_req_done_event evt;
44162306a36Sopenharmony_ci	} __packed reply;
44262306a36Sopenharmony_ci	struct wil_net_stats *stats = &wil->sta[cid].stats;
44362306a36Sopenharmony_ci	int rc;
44462306a36Sopenharmony_ci	u8 tx_mcs, rx_mcs;
44562306a36Sopenharmony_ci	u8 tx_rate_flag = RATE_INFO_FLAGS_DMG;
44662306a36Sopenharmony_ci	u8 rx_rate_flag = RATE_INFO_FLAGS_DMG;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	memset(&reply, 0, sizeof(reply));
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd),
45162306a36Sopenharmony_ci		      WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply),
45262306a36Sopenharmony_ci		      WIL_WMI_CALL_GENERAL_TO_MS);
45362306a36Sopenharmony_ci	if (rc)
45462306a36Sopenharmony_ci		return rc;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	tx_mcs = le16_to_cpu(reply.evt.bf_mcs);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n"
45962306a36Sopenharmony_ci		    "  MCS %s TSF 0x%016llx\n"
46062306a36Sopenharmony_ci		    "  BF status 0x%08x RSSI %d SQI %d%%\n"
46162306a36Sopenharmony_ci		    "  Tx Tpt %d goodput %d Rx goodput %d\n"
46262306a36Sopenharmony_ci		    "  Sectors(rx:tx) my %d:%d peer %d:%d\n"
46362306a36Sopenharmony_ci		    "  Tx mode %d}\n",
46462306a36Sopenharmony_ci		    cid, vif->mid, WIL_EXTENDED_MCS_CHECK(tx_mcs),
46562306a36Sopenharmony_ci		    le64_to_cpu(reply.evt.tsf), reply.evt.status,
46662306a36Sopenharmony_ci		    reply.evt.rssi,
46762306a36Sopenharmony_ci		    reply.evt.sqi,
46862306a36Sopenharmony_ci		    le32_to_cpu(reply.evt.tx_tpt),
46962306a36Sopenharmony_ci		    le32_to_cpu(reply.evt.tx_goodput),
47062306a36Sopenharmony_ci		    le32_to_cpu(reply.evt.rx_goodput),
47162306a36Sopenharmony_ci		    le16_to_cpu(reply.evt.my_rx_sector),
47262306a36Sopenharmony_ci		    le16_to_cpu(reply.evt.my_tx_sector),
47362306a36Sopenharmony_ci		    le16_to_cpu(reply.evt.other_rx_sector),
47462306a36Sopenharmony_ci		    le16_to_cpu(reply.evt.other_tx_sector),
47562306a36Sopenharmony_ci		    reply.evt.tx_mode);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	sinfo->generation = wil->sinfo_gen;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	sinfo->filled = BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
48062306a36Sopenharmony_ci			BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
48162306a36Sopenharmony_ci			BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
48262306a36Sopenharmony_ci			BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
48362306a36Sopenharmony_ci			BIT_ULL(NL80211_STA_INFO_RX_BITRATE) |
48462306a36Sopenharmony_ci			BIT_ULL(NL80211_STA_INFO_TX_BITRATE) |
48562306a36Sopenharmony_ci			BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
48662306a36Sopenharmony_ci			BIT_ULL(NL80211_STA_INFO_TX_FAILED);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (wil->use_enhanced_dma_hw && reply.evt.tx_mode != WMI_TX_MODE_DMG) {
48962306a36Sopenharmony_ci		tx_rate_flag = RATE_INFO_FLAGS_EDMG;
49062306a36Sopenharmony_ci		rx_rate_flag = RATE_INFO_FLAGS_EDMG;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	rx_mcs = stats->last_mcs_rx;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* check extended MCS (12.1) and convert it into
49662306a36Sopenharmony_ci	 * base MCS (7) + EXTENDED_SC_DMG flag
49762306a36Sopenharmony_ci	 */
49862306a36Sopenharmony_ci	if (tx_mcs == WIL_EXTENDED_MCS_26) {
49962306a36Sopenharmony_ci		tx_rate_flag = RATE_INFO_FLAGS_EXTENDED_SC_DMG;
50062306a36Sopenharmony_ci		tx_mcs = WIL_BASE_MCS_FOR_EXTENDED_26;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	if (rx_mcs == WIL_EXTENDED_MCS_26) {
50362306a36Sopenharmony_ci		rx_rate_flag = RATE_INFO_FLAGS_EXTENDED_SC_DMG;
50462306a36Sopenharmony_ci		rx_mcs = WIL_BASE_MCS_FOR_EXTENDED_26;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	sinfo->txrate.flags = tx_rate_flag;
50862306a36Sopenharmony_ci	sinfo->rxrate.flags = rx_rate_flag;
50962306a36Sopenharmony_ci	sinfo->txrate.mcs = tx_mcs;
51062306a36Sopenharmony_ci	sinfo->rxrate.mcs = rx_mcs;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	sinfo->txrate.n_bonded_ch =
51362306a36Sopenharmony_ci				  wil_tx_cb_mode_to_n_bonded(reply.evt.tx_mode);
51462306a36Sopenharmony_ci	sinfo->rxrate.n_bonded_ch =
51562306a36Sopenharmony_ci			     wil_rx_cb_mode_to_n_bonded(stats->last_cb_mode_rx);
51662306a36Sopenharmony_ci	sinfo->rx_bytes = stats->rx_bytes;
51762306a36Sopenharmony_ci	sinfo->rx_packets = stats->rx_packets;
51862306a36Sopenharmony_ci	sinfo->rx_dropped_misc = stats->rx_dropped;
51962306a36Sopenharmony_ci	sinfo->tx_bytes = stats->tx_bytes;
52062306a36Sopenharmony_ci	sinfo->tx_packets = stats->tx_packets;
52162306a36Sopenharmony_ci	sinfo->tx_failed = stats->tx_errors;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (test_bit(wil_vif_fwconnected, vif->status)) {
52462306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
52562306a36Sopenharmony_ci		if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING,
52662306a36Sopenharmony_ci			     wil->fw_capabilities))
52762306a36Sopenharmony_ci			sinfo->signal = reply.evt.rssi;
52862306a36Sopenharmony_ci		else
52962306a36Sopenharmony_ci			sinfo->signal = reply.evt.sqi;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return rc;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int wil_cfg80211_get_station(struct wiphy *wiphy,
53662306a36Sopenharmony_ci				    struct net_device *ndev,
53762306a36Sopenharmony_ci				    const u8 *mac, struct station_info *sinfo)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
54062306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
54162306a36Sopenharmony_ci	int rc;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	int cid = wil_find_cid(wil, vif->mid, mac);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid,
54662306a36Sopenharmony_ci		     vif->mid);
54762306a36Sopenharmony_ci	if (!wil_cid_valid(wil, cid))
54862306a36Sopenharmony_ci		return -ENOENT;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	rc = wil_cid_fill_sinfo(vif, cid, sinfo);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return rc;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci/*
55662306a36Sopenharmony_ci * Find @idx-th active STA for specific MID for station dump.
55762306a36Sopenharmony_ci */
55862306a36Sopenharmony_ciint wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	int i;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	for (i = 0; i < wil->max_assoc_sta; i++) {
56362306a36Sopenharmony_ci		if (wil->sta[i].status == wil_sta_unused)
56462306a36Sopenharmony_ci			continue;
56562306a36Sopenharmony_ci		if (wil->sta[i].mid != mid)
56662306a36Sopenharmony_ci			continue;
56762306a36Sopenharmony_ci		if (idx == 0)
56862306a36Sopenharmony_ci			return i;
56962306a36Sopenharmony_ci		idx--;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return -ENOENT;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int wil_cfg80211_dump_station(struct wiphy *wiphy,
57662306a36Sopenharmony_ci				     struct net_device *dev, int idx,
57762306a36Sopenharmony_ci				     u8 *mac, struct station_info *sinfo)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
58062306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
58162306a36Sopenharmony_ci	int rc;
58262306a36Sopenharmony_ci	int cid = wil_find_cid_by_idx(wil, vif->mid, idx);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	if (!wil_cid_valid(wil, cid))
58562306a36Sopenharmony_ci		return -ENOENT;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ether_addr_copy(mac, wil->sta[cid].addr);
58862306a36Sopenharmony_ci	wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid,
58962306a36Sopenharmony_ci		     vif->mid);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	rc = wil_cid_fill_sinfo(vif, cid, sinfo);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return rc;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
59762306a36Sopenharmony_ci					 struct wireless_dev *wdev)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	wil_dbg_misc(wil, "start_p2p_device: entered\n");
60262306a36Sopenharmony_ci	wil->p2p_dev_started = 1;
60362306a36Sopenharmony_ci	return 0;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
60762306a36Sopenharmony_ci					 struct wireless_dev *wdev)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (!wil->p2p_dev_started)
61262306a36Sopenharmony_ci		return;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	wil_dbg_misc(wil, "stop_p2p_device: entered\n");
61562306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
61662306a36Sopenharmony_ci	mutex_lock(&wil->vif_mutex);
61762306a36Sopenharmony_ci	wil_p2p_stop_radio_operations(wil);
61862306a36Sopenharmony_ci	wil->p2p_dev_started = 0;
61962306a36Sopenharmony_ci	mutex_unlock(&wil->vif_mutex);
62062306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil,
62462306a36Sopenharmony_ci					   enum nl80211_iftype new_type)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	int i;
62762306a36Sopenharmony_ci	struct wireless_dev *wdev;
62862306a36Sopenharmony_ci	struct iface_combination_params params = {
62962306a36Sopenharmony_ci		.num_different_channels = 1,
63062306a36Sopenharmony_ci	};
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
63362306a36Sopenharmony_ci		if (wil->vifs[i]) {
63462306a36Sopenharmony_ci			wdev = vif_to_wdev(wil->vifs[i]);
63562306a36Sopenharmony_ci			params.iftype_num[wdev->iftype]++;
63662306a36Sopenharmony_ci		}
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci	params.iftype_num[new_type]++;
63962306a36Sopenharmony_ci	return cfg80211_check_combinations(wil->wiphy, &params);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil,
64362306a36Sopenharmony_ci					      struct wil6210_vif *vif,
64462306a36Sopenharmony_ci					      enum nl80211_iftype new_type)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	int i, ret = 0;
64762306a36Sopenharmony_ci	struct wireless_dev *wdev;
64862306a36Sopenharmony_ci	struct iface_combination_params params = {
64962306a36Sopenharmony_ci		.num_different_channels = 1,
65062306a36Sopenharmony_ci	};
65162306a36Sopenharmony_ci	bool check_combos = false;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
65462306a36Sopenharmony_ci		struct wil6210_vif *vif_pos = wil->vifs[i];
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		if (vif_pos && vif != vif_pos) {
65762306a36Sopenharmony_ci			wdev = vif_to_wdev(vif_pos);
65862306a36Sopenharmony_ci			params.iftype_num[wdev->iftype]++;
65962306a36Sopenharmony_ci			check_combos = true;
66062306a36Sopenharmony_ci		}
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (check_combos) {
66462306a36Sopenharmony_ci		params.iftype_num[new_type]++;
66562306a36Sopenharmony_ci		ret = cfg80211_check_combinations(wil->wiphy, &params);
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci	return ret;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic struct wireless_dev *
67162306a36Sopenharmony_ciwil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
67262306a36Sopenharmony_ci		       unsigned char name_assign_type,
67362306a36Sopenharmony_ci		       enum nl80211_iftype type,
67462306a36Sopenharmony_ci		       struct vif_params *params)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
67762306a36Sopenharmony_ci	struct net_device *ndev_main = wil->main_ndev, *ndev;
67862306a36Sopenharmony_ci	struct wil6210_vif *vif;
67962306a36Sopenharmony_ci	struct wireless_dev *p2p_wdev, *wdev;
68062306a36Sopenharmony_ci	int rc;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	wil_dbg_misc(wil, "add_iface, type %d\n", type);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* P2P device is not a real virtual interface, it is a management-only
68562306a36Sopenharmony_ci	 * interface that shares the main interface.
68662306a36Sopenharmony_ci	 * Skip concurrency checks here.
68762306a36Sopenharmony_ci	 */
68862306a36Sopenharmony_ci	if (type == NL80211_IFTYPE_P2P_DEVICE) {
68962306a36Sopenharmony_ci		if (wil->p2p_wdev) {
69062306a36Sopenharmony_ci			wil_err(wil, "P2P_DEVICE interface already created\n");
69162306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
69262306a36Sopenharmony_ci		}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
69562306a36Sopenharmony_ci		if (!p2p_wdev)
69662306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		p2p_wdev->iftype = type;
69962306a36Sopenharmony_ci		p2p_wdev->wiphy = wiphy;
70062306a36Sopenharmony_ci		/* use our primary ethernet address */
70162306a36Sopenharmony_ci		ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		wil->p2p_wdev = p2p_wdev;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		return p2p_wdev;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (!wil->wiphy->n_iface_combinations) {
70962306a36Sopenharmony_ci		wil_err(wil, "virtual interfaces not supported\n");
71062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	rc = wil_cfg80211_validate_add_iface(wil, type);
71462306a36Sopenharmony_ci	if (rc) {
71562306a36Sopenharmony_ci		wil_err(wil, "iface validation failed, err=%d\n", rc);
71662306a36Sopenharmony_ci		return ERR_PTR(rc);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	vif = wil_vif_alloc(wil, name, name_assign_type, type);
72062306a36Sopenharmony_ci	if (IS_ERR(vif))
72162306a36Sopenharmony_ci		return ERR_CAST(vif);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	ndev = vif_to_ndev(vif);
72462306a36Sopenharmony_ci	ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr);
72562306a36Sopenharmony_ci	if (is_valid_ether_addr(params->macaddr)) {
72662306a36Sopenharmony_ci		eth_hw_addr_set(ndev, params->macaddr);
72762306a36Sopenharmony_ci	} else {
72862306a36Sopenharmony_ci		u8 addr[ETH_ALEN];
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		ether_addr_copy(addr, ndev_main->perm_addr);
73162306a36Sopenharmony_ci		addr[0] = (addr[0] ^ (1 << vif->mid)) |	0x2; /* locally administered */
73262306a36Sopenharmony_ci		eth_hw_addr_set(ndev, addr);
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci	wdev = vif_to_wdev(vif);
73562306a36Sopenharmony_ci	ether_addr_copy(wdev->address, ndev->dev_addr);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	rc = wil_vif_add(wil, vif);
73862306a36Sopenharmony_ci	if (rc)
73962306a36Sopenharmony_ci		goto out;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n",
74262306a36Sopenharmony_ci		 vif->mid, type, wdev->address);
74362306a36Sopenharmony_ci	return wdev;
74462306a36Sopenharmony_ciout:
74562306a36Sopenharmony_ci	wil_vif_free(vif);
74662306a36Sopenharmony_ci	return ERR_PTR(rc);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ciint wil_vif_prepare_stop(struct wil6210_vif *vif)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
75262306a36Sopenharmony_ci	struct wireless_dev *wdev = vif_to_wdev(vif);
75362306a36Sopenharmony_ci	struct net_device *ndev;
75462306a36Sopenharmony_ci	int rc;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if (wdev->iftype != NL80211_IFTYPE_AP)
75762306a36Sopenharmony_ci		return 0;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	ndev = vif_to_ndev(vif);
76062306a36Sopenharmony_ci	if (netif_carrier_ok(ndev)) {
76162306a36Sopenharmony_ci		rc = wmi_pcp_stop(vif);
76262306a36Sopenharmony_ci		if (rc) {
76362306a36Sopenharmony_ci			wil_info(wil, "failed to stop AP, status %d\n",
76462306a36Sopenharmony_ci				 rc);
76562306a36Sopenharmony_ci			/* continue */
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci		wil_bcast_fini(vif);
76862306a36Sopenharmony_ci		netif_carrier_off(ndev);
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	return 0;
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic int wil_cfg80211_del_iface(struct wiphy *wiphy,
77562306a36Sopenharmony_ci				  struct wireless_dev *wdev)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
77862306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
77962306a36Sopenharmony_ci	int rc;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	wil_dbg_misc(wil, "del_iface\n");
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
78462306a36Sopenharmony_ci		if (wdev != wil->p2p_wdev) {
78562306a36Sopenharmony_ci			wil_err(wil, "delete of incorrect interface 0x%p\n",
78662306a36Sopenharmony_ci				wdev);
78762306a36Sopenharmony_ci			return -EINVAL;
78862306a36Sopenharmony_ci		}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		wil_cfg80211_stop_p2p_device(wiphy, wdev);
79162306a36Sopenharmony_ci		wil_p2p_wdev_free(wil);
79262306a36Sopenharmony_ci		return 0;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (vif->mid == 0) {
79662306a36Sopenharmony_ci		wil_err(wil, "cannot remove the main interface\n");
79762306a36Sopenharmony_ci		return -EINVAL;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	rc = wil_vif_prepare_stop(vif);
80162306a36Sopenharmony_ci	if (rc)
80262306a36Sopenharmony_ci		goto out;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n",
80562306a36Sopenharmony_ci		 vif->mid, wdev->iftype, wdev->address);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	wil_vif_remove(wil, vif->mid);
80862306a36Sopenharmony_ciout:
80962306a36Sopenharmony_ci	return rc;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic bool wil_is_safe_switch(enum nl80211_iftype from,
81362306a36Sopenharmony_ci			       enum nl80211_iftype to)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	if (from == NL80211_IFTYPE_STATION &&
81662306a36Sopenharmony_ci	    to == NL80211_IFTYPE_P2P_CLIENT)
81762306a36Sopenharmony_ci		return true;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return false;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic int wil_cfg80211_change_iface(struct wiphy *wiphy,
82362306a36Sopenharmony_ci				     struct net_device *ndev,
82462306a36Sopenharmony_ci				     enum nl80211_iftype type,
82562306a36Sopenharmony_ci				     struct vif_params *params)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
82862306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
82962306a36Sopenharmony_ci	struct wireless_dev *wdev = vif_to_wdev(vif);
83062306a36Sopenharmony_ci	int rc;
83162306a36Sopenharmony_ci	bool fw_reset = false;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	wil_dbg_misc(wil, "change_iface: type=%d\n", type);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if (wiphy->n_iface_combinations) {
83662306a36Sopenharmony_ci		rc = wil_cfg80211_validate_change_iface(wil, vif, type);
83762306a36Sopenharmony_ci		if (rc) {
83862306a36Sopenharmony_ci			wil_err(wil, "iface validation failed, err=%d\n", rc);
83962306a36Sopenharmony_ci			return rc;
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* do not reset FW when there are active VIFs,
84462306a36Sopenharmony_ci	 * because it can cause significant disruption
84562306a36Sopenharmony_ci	 */
84662306a36Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, true, false) &&
84762306a36Sopenharmony_ci	    netif_running(ndev) && !wil_is_recovery_blocked(wil) &&
84862306a36Sopenharmony_ci	    !wil_is_safe_switch(wdev->iftype, type)) {
84962306a36Sopenharmony_ci		wil_dbg_misc(wil, "interface is up. resetting...\n");
85062306a36Sopenharmony_ci		mutex_lock(&wil->mutex);
85162306a36Sopenharmony_ci		__wil_down(wil);
85262306a36Sopenharmony_ci		rc = __wil_up(wil);
85362306a36Sopenharmony_ci		mutex_unlock(&wil->mutex);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		if (rc)
85662306a36Sopenharmony_ci			return rc;
85762306a36Sopenharmony_ci		fw_reset = true;
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	switch (type) {
86162306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
86262306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
86362306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
86462306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
86562306a36Sopenharmony_ci		break;
86662306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
86762306a36Sopenharmony_ci		if (params->flags)
86862306a36Sopenharmony_ci			wil->monitor_flags = params->flags;
86962306a36Sopenharmony_ci		break;
87062306a36Sopenharmony_ci	default:
87162306a36Sopenharmony_ci		return -EOPNOTSUPP;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) {
87562306a36Sopenharmony_ci		if (!fw_reset)
87662306a36Sopenharmony_ci			wil_vif_prepare_stop(vif);
87762306a36Sopenharmony_ci		rc = wmi_port_delete(wil, vif->mid);
87862306a36Sopenharmony_ci		if (rc)
87962306a36Sopenharmony_ci			return rc;
88062306a36Sopenharmony_ci		rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type);
88162306a36Sopenharmony_ci		if (rc)
88262306a36Sopenharmony_ci			return rc;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	wdev->iftype = type;
88662306a36Sopenharmony_ci	return 0;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic int wil_cfg80211_scan(struct wiphy *wiphy,
89062306a36Sopenharmony_ci			     struct cfg80211_scan_request *request)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
89362306a36Sopenharmony_ci	struct wireless_dev *wdev = request->wdev;
89462306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
89562306a36Sopenharmony_ci	struct {
89662306a36Sopenharmony_ci		struct wmi_start_scan_cmd cmd;
89762306a36Sopenharmony_ci		u16 chnl[4];
89862306a36Sopenharmony_ci	} __packed cmd;
89962306a36Sopenharmony_ci	uint i, n;
90062306a36Sopenharmony_ci	int rc;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	wil_dbg_misc(wil, "scan: wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/* scan is supported on client interfaces and on AP interface */
90562306a36Sopenharmony_ci	switch (wdev->iftype) {
90662306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
90762306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
90862306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
90962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
91062306a36Sopenharmony_ci		break;
91162306a36Sopenharmony_ci	default:
91262306a36Sopenharmony_ci		return -EOPNOTSUPP;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* FW don't support scan after connection attempt */
91662306a36Sopenharmony_ci	if (test_bit(wil_status_dontscan, wil->status)) {
91762306a36Sopenharmony_ci		wil_err(wil, "Can't scan now\n");
91862306a36Sopenharmony_ci		return -EBUSY;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	mutex_lock(&wil->vif_mutex);
92462306a36Sopenharmony_ci	if (vif->scan_request || vif->p2p.discovery_started) {
92562306a36Sopenharmony_ci		wil_err(wil, "Already scanning\n");
92662306a36Sopenharmony_ci		mutex_unlock(&wil->vif_mutex);
92762306a36Sopenharmony_ci		rc = -EAGAIN;
92862306a36Sopenharmony_ci		goto out;
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci	mutex_unlock(&wil->vif_mutex);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
93362306a36Sopenharmony_ci		if (!wil->p2p_dev_started) {
93462306a36Sopenharmony_ci			wil_err(wil, "P2P search requested on stopped P2P device\n");
93562306a36Sopenharmony_ci			rc = -EIO;
93662306a36Sopenharmony_ci			goto out;
93762306a36Sopenharmony_ci		}
93862306a36Sopenharmony_ci		/* social scan on P2P_DEVICE is handled as p2p search */
93962306a36Sopenharmony_ci		if (wil_p2p_is_social_scan(request)) {
94062306a36Sopenharmony_ci			vif->scan_request = request;
94162306a36Sopenharmony_ci			if (vif->mid == 0)
94262306a36Sopenharmony_ci				wil->radio_wdev = wdev;
94362306a36Sopenharmony_ci			rc = wil_p2p_search(vif, request);
94462306a36Sopenharmony_ci			if (rc) {
94562306a36Sopenharmony_ci				if (vif->mid == 0)
94662306a36Sopenharmony_ci					wil->radio_wdev =
94762306a36Sopenharmony_ci						wil->main_ndev->ieee80211_ptr;
94862306a36Sopenharmony_ci				vif->scan_request = NULL;
94962306a36Sopenharmony_ci			}
95062306a36Sopenharmony_ci			goto out;
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	(void)wil_p2p_stop_discovery(vif);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
95762306a36Sopenharmony_ci	wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	for (i = 0; i < request->n_ssids; i++) {
96062306a36Sopenharmony_ci		wil_dbg_misc(wil, "SSID[%d]", i);
96162306a36Sopenharmony_ci		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
96262306a36Sopenharmony_ci				  request->ssids[i].ssid,
96362306a36Sopenharmony_ci				  request->ssids[i].ssid_len, true);
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (request->n_ssids)
96762306a36Sopenharmony_ci		rc = wmi_set_ssid(vif, request->ssids[0].ssid_len,
96862306a36Sopenharmony_ci				  request->ssids[0].ssid);
96962306a36Sopenharmony_ci	else
97062306a36Sopenharmony_ci		rc = wmi_set_ssid(vif, 0, NULL);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (rc) {
97362306a36Sopenharmony_ci		wil_err(wil, "set SSID for scan request failed: %d\n", rc);
97462306a36Sopenharmony_ci		goto out;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	vif->scan_request = request;
97862306a36Sopenharmony_ci	mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
98162306a36Sopenharmony_ci	cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
98262306a36Sopenharmony_ci	cmd.cmd.num_channels = 0;
98362306a36Sopenharmony_ci	n = min(request->n_channels, 4U);
98462306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
98562306a36Sopenharmony_ci		int ch = request->channels[i]->hw_value;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci		if (ch == 0) {
98862306a36Sopenharmony_ci			wil_err(wil,
98962306a36Sopenharmony_ci				"Scan requested for unknown frequency %dMhz\n",
99062306a36Sopenharmony_ci				request->channels[i]->center_freq);
99162306a36Sopenharmony_ci			continue;
99262306a36Sopenharmony_ci		}
99362306a36Sopenharmony_ci		/* 0-based channel indexes */
99462306a36Sopenharmony_ci		cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
99562306a36Sopenharmony_ci		wil_dbg_misc(wil, "Scan for ch %d  : %d MHz\n", ch,
99662306a36Sopenharmony_ci			     request->channels[i]->center_freq);
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (request->ie_len)
100062306a36Sopenharmony_ci		wil_hex_dump_misc("Scan IE ", DUMP_PREFIX_OFFSET, 16, 1,
100162306a36Sopenharmony_ci				  request->ie, request->ie_len, true);
100262306a36Sopenharmony_ci	else
100362306a36Sopenharmony_ci		wil_dbg_misc(wil, "Scan has no IE's\n");
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
100662306a36Sopenharmony_ci			request->ie_len, request->ie);
100762306a36Sopenharmony_ci	if (rc)
100862306a36Sopenharmony_ci		goto out_restore;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) {
101162306a36Sopenharmony_ci		cmd.cmd.discovery_mode = 1;
101262306a36Sopenharmony_ci		wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (vif->mid == 0)
101662306a36Sopenharmony_ci		wil->radio_wdev = wdev;
101762306a36Sopenharmony_ci	rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid,
101862306a36Sopenharmony_ci		      &cmd, sizeof(cmd.cmd) +
101962306a36Sopenharmony_ci		      cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ciout_restore:
102262306a36Sopenharmony_ci	if (rc) {
102362306a36Sopenharmony_ci		del_timer_sync(&vif->scan_timer);
102462306a36Sopenharmony_ci		if (vif->mid == 0)
102562306a36Sopenharmony_ci			wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
102662306a36Sopenharmony_ci		vif->scan_request = NULL;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ciout:
102962306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
103062306a36Sopenharmony_ci	return rc;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic void wil_cfg80211_abort_scan(struct wiphy *wiphy,
103462306a36Sopenharmony_ci				    struct wireless_dev *wdev)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
103762306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
104262306a36Sopenharmony_ci	mutex_lock(&wil->vif_mutex);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (!vif->scan_request)
104562306a36Sopenharmony_ci		goto out;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (wdev != vif->scan_request->wdev) {
104862306a36Sopenharmony_ci		wil_dbg_misc(wil, "abort scan was called on the wrong iface\n");
104962306a36Sopenharmony_ci		goto out;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev)
105362306a36Sopenharmony_ci		wil_p2p_stop_radio_operations(wil);
105462306a36Sopenharmony_ci	else
105562306a36Sopenharmony_ci		wil_abort_scan(vif, true);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ciout:
105862306a36Sopenharmony_ci	mutex_unlock(&wil->vif_mutex);
105962306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic void wil_print_crypto(struct wil6210_priv *wil,
106362306a36Sopenharmony_ci			     struct cfg80211_crypto_settings *c)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	int i, n;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
106862306a36Sopenharmony_ci		     c->wpa_versions, c->cipher_group);
106962306a36Sopenharmony_ci	wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise);
107062306a36Sopenharmony_ci	n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise));
107162306a36Sopenharmony_ci	for (i = 0; i < n; i++)
107262306a36Sopenharmony_ci		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
107362306a36Sopenharmony_ci			     c->ciphers_pairwise[i]);
107462306a36Sopenharmony_ci	wil_dbg_misc(wil, "}\n");
107562306a36Sopenharmony_ci	wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites);
107662306a36Sopenharmony_ci	n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites));
107762306a36Sopenharmony_ci	for (i = 0; i < n; i++)
107862306a36Sopenharmony_ci		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
107962306a36Sopenharmony_ci			     c->akm_suites[i]);
108062306a36Sopenharmony_ci	wil_dbg_misc(wil, "}\n");
108162306a36Sopenharmony_ci	wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
108262306a36Sopenharmony_ci		     c->control_port, be16_to_cpu(c->control_port_ethertype),
108362306a36Sopenharmony_ci		     c->control_port_no_encrypt);
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic const char *
108762306a36Sopenharmony_ciwil_get_auth_type_name(enum nl80211_auth_type auth_type)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	switch (auth_type) {
109062306a36Sopenharmony_ci	case NL80211_AUTHTYPE_OPEN_SYSTEM:
109162306a36Sopenharmony_ci		return "OPEN_SYSTEM";
109262306a36Sopenharmony_ci	case NL80211_AUTHTYPE_SHARED_KEY:
109362306a36Sopenharmony_ci		return "SHARED_KEY";
109462306a36Sopenharmony_ci	case NL80211_AUTHTYPE_FT:
109562306a36Sopenharmony_ci		return "FT";
109662306a36Sopenharmony_ci	case NL80211_AUTHTYPE_NETWORK_EAP:
109762306a36Sopenharmony_ci		return "NETWORK_EAP";
109862306a36Sopenharmony_ci	case NL80211_AUTHTYPE_SAE:
109962306a36Sopenharmony_ci		return "SAE";
110062306a36Sopenharmony_ci	case NL80211_AUTHTYPE_AUTOMATIC:
110162306a36Sopenharmony_ci		return "AUTOMATIC";
110262306a36Sopenharmony_ci	default:
110362306a36Sopenharmony_ci		return "unknown";
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_cistatic void wil_print_connect_params(struct wil6210_priv *wil,
110762306a36Sopenharmony_ci				     struct cfg80211_connect_params *sme)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	wil_info(wil, "Connecting to:\n");
111062306a36Sopenharmony_ci	if (sme->channel) {
111162306a36Sopenharmony_ci		wil_info(wil, "  Channel: %d freq %d\n",
111262306a36Sopenharmony_ci			 sme->channel->hw_value, sme->channel->center_freq);
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci	if (sme->bssid)
111562306a36Sopenharmony_ci		wil_info(wil, "  BSSID: %pM\n", sme->bssid);
111662306a36Sopenharmony_ci	if (sme->ssid)
111762306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "  SSID: ", DUMP_PREFIX_OFFSET,
111862306a36Sopenharmony_ci			       16, 1, sme->ssid, sme->ssid_len, true);
111962306a36Sopenharmony_ci	if (sme->prev_bssid)
112062306a36Sopenharmony_ci		wil_info(wil, "  Previous BSSID=%pM\n", sme->prev_bssid);
112162306a36Sopenharmony_ci	wil_info(wil, "  Auth Type: %s\n",
112262306a36Sopenharmony_ci		 wil_get_auth_type_name(sme->auth_type));
112362306a36Sopenharmony_ci	wil_info(wil, "  Privacy: %s\n", sme->privacy ? "secure" : "open");
112462306a36Sopenharmony_ci	wil_info(wil, "  PBSS: %d\n", sme->pbss);
112562306a36Sopenharmony_ci	wil_print_crypto(wil, &sme->crypto);
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic int wil_ft_connect(struct wiphy *wiphy,
112962306a36Sopenharmony_ci			  struct net_device *ndev,
113062306a36Sopenharmony_ci			  struct cfg80211_connect_params *sme)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
113362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
113462306a36Sopenharmony_ci	struct wmi_ft_auth_cmd auth_cmd;
113562306a36Sopenharmony_ci	int rc;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
113862306a36Sopenharmony_ci		wil_err(wil, "FT: FW does not support FT roaming\n");
113962306a36Sopenharmony_ci		return -EOPNOTSUPP;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	if (!sme->prev_bssid) {
114362306a36Sopenharmony_ci		wil_err(wil, "FT: prev_bssid was not set\n");
114462306a36Sopenharmony_ci		return -EINVAL;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (ether_addr_equal(sme->prev_bssid, sme->bssid)) {
114862306a36Sopenharmony_ci		wil_err(wil, "FT: can not roam to same AP\n");
114962306a36Sopenharmony_ci		return -EINVAL;
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	if (!test_bit(wil_vif_fwconnected, vif->status)) {
115362306a36Sopenharmony_ci		wil_err(wil, "FT: roam while not connected\n");
115462306a36Sopenharmony_ci		return -EINVAL;
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (vif->privacy != sme->privacy) {
115862306a36Sopenharmony_ci		wil_err(wil, "FT: privacy mismatch, current (%d) roam (%d)\n",
115962306a36Sopenharmony_ci			vif->privacy, sme->privacy);
116062306a36Sopenharmony_ci		return -EINVAL;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (sme->pbss) {
116462306a36Sopenharmony_ci		wil_err(wil, "FT: roam is not valid for PBSS\n");
116562306a36Sopenharmony_ci		return -EINVAL;
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	memset(&auth_cmd, 0, sizeof(auth_cmd));
116962306a36Sopenharmony_ci	auth_cmd.channel = sme->channel->hw_value - 1;
117062306a36Sopenharmony_ci	ether_addr_copy(auth_cmd.bssid, sme->bssid);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	wil_info(wil, "FT: roaming\n");
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	set_bit(wil_vif_ft_roam, vif->status);
117562306a36Sopenharmony_ci	rc = wmi_send(wil, WMI_FT_AUTH_CMDID, vif->mid,
117662306a36Sopenharmony_ci		      &auth_cmd, sizeof(auth_cmd));
117762306a36Sopenharmony_ci	if (rc == 0)
117862306a36Sopenharmony_ci		mod_timer(&vif->connect_timer,
117962306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(5000));
118062306a36Sopenharmony_ci	else
118162306a36Sopenharmony_ci		clear_bit(wil_vif_ft_roam, vif->status);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	return rc;
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic int wil_get_wmi_edmg_channel(struct wil6210_priv *wil, u8 edmg_bw_config,
118762306a36Sopenharmony_ci				    u8 edmg_channels, u8 *wmi_ch)
118862306a36Sopenharmony_ci{
118962306a36Sopenharmony_ci	if (!edmg_bw_config) {
119062306a36Sopenharmony_ci		*wmi_ch = 0;
119162306a36Sopenharmony_ci		return 0;
119262306a36Sopenharmony_ci	} else if (edmg_bw_config == WIL_EDMG_BW_CONFIGURATION) {
119362306a36Sopenharmony_ci		/* convert from edmg channel bitmap into edmg channel number */
119462306a36Sopenharmony_ci		switch (edmg_channels) {
119562306a36Sopenharmony_ci		case WIL_EDMG_CHANNEL_9_SUBCHANNELS:
119662306a36Sopenharmony_ci			return wil_spec2wmi_ch(9, wmi_ch);
119762306a36Sopenharmony_ci		case WIL_EDMG_CHANNEL_10_SUBCHANNELS:
119862306a36Sopenharmony_ci			return wil_spec2wmi_ch(10, wmi_ch);
119962306a36Sopenharmony_ci		case WIL_EDMG_CHANNEL_11_SUBCHANNELS:
120062306a36Sopenharmony_ci			return wil_spec2wmi_ch(11, wmi_ch);
120162306a36Sopenharmony_ci		default:
120262306a36Sopenharmony_ci			wil_err(wil, "Unsupported edmg channel bitmap 0x%x\n",
120362306a36Sopenharmony_ci				edmg_channels);
120462306a36Sopenharmony_ci			return -EINVAL;
120562306a36Sopenharmony_ci		}
120662306a36Sopenharmony_ci	} else {
120762306a36Sopenharmony_ci		wil_err(wil, "Unsupported EDMG BW configuration %d\n",
120862306a36Sopenharmony_ci			edmg_bw_config);
120962306a36Sopenharmony_ci		return -EINVAL;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cistatic int wil_cfg80211_connect(struct wiphy *wiphy,
121462306a36Sopenharmony_ci				struct net_device *ndev,
121562306a36Sopenharmony_ci				struct cfg80211_connect_params *sme)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
121862306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
121962306a36Sopenharmony_ci	struct cfg80211_bss *bss;
122062306a36Sopenharmony_ci	struct wmi_connect_cmd conn;
122162306a36Sopenharmony_ci	const u8 *ssid_eid;
122262306a36Sopenharmony_ci	const u8 *rsn_eid;
122362306a36Sopenharmony_ci	int ch;
122462306a36Sopenharmony_ci	int rc = 0;
122562306a36Sopenharmony_ci	bool is_ft_roam = false;
122662306a36Sopenharmony_ci	u8 network_type;
122762306a36Sopenharmony_ci	enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
123062306a36Sopenharmony_ci	wil_print_connect_params(wil, sme);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	if (sme->auth_type == NL80211_AUTHTYPE_FT)
123362306a36Sopenharmony_ci		is_ft_roam = true;
123462306a36Sopenharmony_ci	if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC &&
123562306a36Sopenharmony_ci	    test_bit(wil_vif_fwconnected, vif->status))
123662306a36Sopenharmony_ci		is_ft_roam = true;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	if (!is_ft_roam)
123962306a36Sopenharmony_ci		if (test_bit(wil_vif_fwconnecting, vif->status) ||
124062306a36Sopenharmony_ci		    test_bit(wil_vif_fwconnected, vif->status))
124162306a36Sopenharmony_ci			return -EALREADY;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	if (sme->ie_len > WMI_MAX_IE_LEN) {
124462306a36Sopenharmony_ci		wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len);
124562306a36Sopenharmony_ci		return -ERANGE;
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	rsn_eid = sme->ie ?
124962306a36Sopenharmony_ci			cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
125062306a36Sopenharmony_ci			NULL;
125162306a36Sopenharmony_ci	if (sme->privacy && !rsn_eid) {
125262306a36Sopenharmony_ci		wil_info(wil, "WSC connection\n");
125362306a36Sopenharmony_ci		if (is_ft_roam) {
125462306a36Sopenharmony_ci			wil_err(wil, "No WSC with FT roam\n");
125562306a36Sopenharmony_ci			return -EINVAL;
125662306a36Sopenharmony_ci		}
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	if (sme->pbss)
126062306a36Sopenharmony_ci		bss_type = IEEE80211_BSS_TYPE_PBSS;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
126362306a36Sopenharmony_ci			       sme->ssid, sme->ssid_len,
126462306a36Sopenharmony_ci			       bss_type, IEEE80211_PRIVACY_ANY);
126562306a36Sopenharmony_ci	if (!bss) {
126662306a36Sopenharmony_ci		wil_err(wil, "Unable to find BSS\n");
126762306a36Sopenharmony_ci		return -ENOENT;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
127162306a36Sopenharmony_ci	if (!ssid_eid) {
127262306a36Sopenharmony_ci		wil_err(wil, "No SSID\n");
127362306a36Sopenharmony_ci		rc = -ENOENT;
127462306a36Sopenharmony_ci		goto out;
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci	vif->privacy = sme->privacy;
127762306a36Sopenharmony_ci	vif->pbss = sme->pbss;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
128062306a36Sopenharmony_ci	if (rc)
128162306a36Sopenharmony_ci		goto out;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
128462306a36Sopenharmony_ci	case WLAN_CAPABILITY_DMG_TYPE_AP:
128562306a36Sopenharmony_ci		network_type = WMI_NETTYPE_INFRA;
128662306a36Sopenharmony_ci		break;
128762306a36Sopenharmony_ci	case WLAN_CAPABILITY_DMG_TYPE_PBSS:
128862306a36Sopenharmony_ci		network_type = WMI_NETTYPE_P2P;
128962306a36Sopenharmony_ci		break;
129062306a36Sopenharmony_ci	default:
129162306a36Sopenharmony_ci		wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
129262306a36Sopenharmony_ci			bss->capability);
129362306a36Sopenharmony_ci		rc = -EINVAL;
129462306a36Sopenharmony_ci		goto out;
129562306a36Sopenharmony_ci	}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	ch = bss->channel->hw_value;
129862306a36Sopenharmony_ci	if (ch == 0) {
129962306a36Sopenharmony_ci		wil_err(wil, "BSS at unknown frequency %dMhz\n",
130062306a36Sopenharmony_ci			bss->channel->center_freq);
130162306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
130262306a36Sopenharmony_ci		goto out;
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (is_ft_roam) {
130662306a36Sopenharmony_ci		if (network_type != WMI_NETTYPE_INFRA) {
130762306a36Sopenharmony_ci			wil_err(wil, "FT: Unsupported BSS type, capability= 0x%04x\n",
130862306a36Sopenharmony_ci				bss->capability);
130962306a36Sopenharmony_ci			rc = -EINVAL;
131062306a36Sopenharmony_ci			goto out;
131162306a36Sopenharmony_ci		}
131262306a36Sopenharmony_ci		rc = wil_ft_connect(wiphy, ndev, sme);
131362306a36Sopenharmony_ci		if (rc == 0)
131462306a36Sopenharmony_ci			vif->bss = bss;
131562306a36Sopenharmony_ci		goto out;
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (vif->privacy) {
131962306a36Sopenharmony_ci		/* For secure assoc, remove old keys */
132062306a36Sopenharmony_ci		rc = wmi_del_cipher_key(vif, 0, bss->bssid,
132162306a36Sopenharmony_ci					WMI_KEY_USE_PAIRWISE);
132262306a36Sopenharmony_ci		if (rc) {
132362306a36Sopenharmony_ci			wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
132462306a36Sopenharmony_ci			goto out;
132562306a36Sopenharmony_ci		}
132662306a36Sopenharmony_ci		rc = wmi_del_cipher_key(vif, 0, bss->bssid,
132762306a36Sopenharmony_ci					WMI_KEY_USE_RX_GROUP);
132862306a36Sopenharmony_ci		if (rc) {
132962306a36Sopenharmony_ci			wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
133062306a36Sopenharmony_ci			goto out;
133162306a36Sopenharmony_ci		}
133262306a36Sopenharmony_ci	}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	/* WMI_CONNECT_CMD */
133562306a36Sopenharmony_ci	memset(&conn, 0, sizeof(conn));
133662306a36Sopenharmony_ci	conn.network_type = network_type;
133762306a36Sopenharmony_ci	if (vif->privacy) {
133862306a36Sopenharmony_ci		if (rsn_eid) { /* regular secure connection */
133962306a36Sopenharmony_ci			conn.dot11_auth_mode = WMI_AUTH11_SHARED;
134062306a36Sopenharmony_ci			conn.auth_mode = WMI_AUTH_WPA2_PSK;
134162306a36Sopenharmony_ci			conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
134262306a36Sopenharmony_ci			conn.pairwise_crypto_len = 16;
134362306a36Sopenharmony_ci			conn.group_crypto_type = WMI_CRYPT_AES_GCMP;
134462306a36Sopenharmony_ci			conn.group_crypto_len = 16;
134562306a36Sopenharmony_ci		} else { /* WSC */
134662306a36Sopenharmony_ci			conn.dot11_auth_mode = WMI_AUTH11_WSC;
134762306a36Sopenharmony_ci			conn.auth_mode = WMI_AUTH_NONE;
134862306a36Sopenharmony_ci		}
134962306a36Sopenharmony_ci	} else { /* insecure connection */
135062306a36Sopenharmony_ci		conn.dot11_auth_mode = WMI_AUTH11_OPEN;
135162306a36Sopenharmony_ci		conn.auth_mode = WMI_AUTH_NONE;
135262306a36Sopenharmony_ci	}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	conn.ssid_len = min_t(u8, ssid_eid[1], 32);
135562306a36Sopenharmony_ci	memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
135662306a36Sopenharmony_ci	conn.channel = ch - 1;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	rc = wil_get_wmi_edmg_channel(wil, sme->edmg.bw_config,
135962306a36Sopenharmony_ci				      sme->edmg.channels, &conn.edmg_channel);
136062306a36Sopenharmony_ci	if (rc < 0)
136162306a36Sopenharmony_ci		return rc;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	ether_addr_copy(conn.bssid, bss->bssid);
136462306a36Sopenharmony_ci	ether_addr_copy(conn.dst_mac, bss->bssid);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	set_bit(wil_vif_fwconnecting, vif->status);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn));
136962306a36Sopenharmony_ci	if (rc == 0) {
137062306a36Sopenharmony_ci		netif_carrier_on(ndev);
137162306a36Sopenharmony_ci		if (!wil_has_other_active_ifaces(wil, ndev, false, true))
137262306a36Sopenharmony_ci			wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
137362306a36Sopenharmony_ci		vif->bss = bss;
137462306a36Sopenharmony_ci		/* Connect can take lots of time */
137562306a36Sopenharmony_ci		mod_timer(&vif->connect_timer,
137662306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(5000));
137762306a36Sopenharmony_ci	} else {
137862306a36Sopenharmony_ci		clear_bit(wil_vif_fwconnecting, vif->status);
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci out:
138262306a36Sopenharmony_ci	cfg80211_put_bss(wiphy, bss);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	return rc;
138562306a36Sopenharmony_ci}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_cistatic int wil_cfg80211_disconnect(struct wiphy *wiphy,
138862306a36Sopenharmony_ci				   struct net_device *ndev,
138962306a36Sopenharmony_ci				   u16 reason_code)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	int rc;
139262306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
139362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n",
139662306a36Sopenharmony_ci		     reason_code, vif->mid);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	if (!(test_bit(wil_vif_fwconnecting, vif->status) ||
139962306a36Sopenharmony_ci	      test_bit(wil_vif_fwconnected, vif->status))) {
140062306a36Sopenharmony_ci		wil_err(wil, "Disconnect was called while disconnected\n");
140162306a36Sopenharmony_ci		return 0;
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	vif->locally_generated_disc = true;
140562306a36Sopenharmony_ci	rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0,
140662306a36Sopenharmony_ci		      WMI_DISCONNECT_EVENTID, NULL, 0,
140762306a36Sopenharmony_ci		      WIL6210_DISCONNECT_TO_MS);
140862306a36Sopenharmony_ci	if (rc)
140962306a36Sopenharmony_ci		wil_err(wil, "disconnect error %d\n", rc);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	return rc;
141262306a36Sopenharmony_ci}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_cistatic int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
141762306a36Sopenharmony_ci	int rc;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	/* these parameters are explicitly not supported */
142062306a36Sopenharmony_ci	if (changed & (WIPHY_PARAM_RETRY_LONG |
142162306a36Sopenharmony_ci		       WIPHY_PARAM_FRAG_THRESHOLD |
142262306a36Sopenharmony_ci		       WIPHY_PARAM_RTS_THRESHOLD))
142362306a36Sopenharmony_ci		return -ENOTSUPP;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_SHORT) {
142662306a36Sopenharmony_ci		rc = wmi_set_mgmt_retry(wil, wiphy->retry_short);
142762306a36Sopenharmony_ci		if (rc)
142862306a36Sopenharmony_ci			return rc;
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	return 0;
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ciint wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
143562306a36Sopenharmony_ci			 struct cfg80211_mgmt_tx_params *params,
143662306a36Sopenharmony_ci			 u64 *cookie)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	const u8 *buf = params->buf;
143962306a36Sopenharmony_ci	size_t len = params->len;
144062306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
144162306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
144262306a36Sopenharmony_ci	int rc;
144362306a36Sopenharmony_ci	bool tx_status;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
144662306a36Sopenharmony_ci		     params->chan ? params->chan->hw_value : -1,
144762306a36Sopenharmony_ci		     params->offchan,
144862306a36Sopenharmony_ci		     params->wait);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	/* Note, currently we support the "wait" parameter only on AP mode.
145162306a36Sopenharmony_ci	 * In other modes, user-space must call remain_on_channel before
145262306a36Sopenharmony_ci	 * mgmt_tx or listen on a channel other than active one.
145362306a36Sopenharmony_ci	 */
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (params->chan && params->chan->hw_value == 0) {
145662306a36Sopenharmony_ci		wil_err(wil, "invalid channel\n");
145762306a36Sopenharmony_ci		return -EINVAL;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (wdev->iftype != NL80211_IFTYPE_AP) {
146162306a36Sopenharmony_ci		wil_dbg_misc(wil,
146262306a36Sopenharmony_ci			     "send WMI_SW_TX_REQ_CMDID on non-AP interfaces\n");
146362306a36Sopenharmony_ci		rc = wmi_mgmt_tx(vif, buf, len);
146462306a36Sopenharmony_ci		goto out;
146562306a36Sopenharmony_ci	}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	if (!params->chan || params->chan->hw_value == vif->channel) {
146862306a36Sopenharmony_ci		wil_dbg_misc(wil,
146962306a36Sopenharmony_ci			     "send WMI_SW_TX_REQ_CMDID for on-channel\n");
147062306a36Sopenharmony_ci		rc = wmi_mgmt_tx(vif, buf, len);
147162306a36Sopenharmony_ci		goto out;
147262306a36Sopenharmony_ci	}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	if (params->offchan == 0) {
147562306a36Sopenharmony_ci		wil_err(wil,
147662306a36Sopenharmony_ci			"invalid channel params: current %d requested %d, off-channel not allowed\n",
147762306a36Sopenharmony_ci			vif->channel, params->chan->hw_value);
147862306a36Sopenharmony_ci		return -EBUSY;
147962306a36Sopenharmony_ci	}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	/* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
148262306a36Sopenharmony_ci	rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
148362306a36Sopenharmony_ci			     params->wait);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ciout:
148662306a36Sopenharmony_ci	/* when the sent packet was not acked by receiver(ACK=0), rc will
148762306a36Sopenharmony_ci	 * be -EAGAIN. In this case this function needs to return success,
148862306a36Sopenharmony_ci	 * the ACK=0 will be reflected in tx_status.
148962306a36Sopenharmony_ci	 */
149062306a36Sopenharmony_ci	tx_status = (rc == 0);
149162306a36Sopenharmony_ci	rc = (rc == -EAGAIN) ? 0 : rc;
149262306a36Sopenharmony_ci	cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
149362306a36Sopenharmony_ci				tx_status, GFP_KERNEL);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	return rc;
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic int wil_cfg80211_set_channel(struct wiphy *wiphy,
149962306a36Sopenharmony_ci				    struct cfg80211_chan_def *chandef)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	wil->monitor_chandef = *chandef;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	return 0;
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_cistatic enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev,
150962306a36Sopenharmony_ci					       bool pairwise)
151062306a36Sopenharmony_ci{
151162306a36Sopenharmony_ci	struct wil6210_priv *wil = wdev_to_wil(wdev);
151262306a36Sopenharmony_ci	enum wmi_key_usage rc;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	if (pairwise) {
151562306a36Sopenharmony_ci		rc = WMI_KEY_USE_PAIRWISE;
151662306a36Sopenharmony_ci	} else {
151762306a36Sopenharmony_ci		switch (wdev->iftype) {
151862306a36Sopenharmony_ci		case NL80211_IFTYPE_STATION:
151962306a36Sopenharmony_ci		case NL80211_IFTYPE_P2P_CLIENT:
152062306a36Sopenharmony_ci			rc = WMI_KEY_USE_RX_GROUP;
152162306a36Sopenharmony_ci			break;
152262306a36Sopenharmony_ci		case NL80211_IFTYPE_AP:
152362306a36Sopenharmony_ci		case NL80211_IFTYPE_P2P_GO:
152462306a36Sopenharmony_ci			rc = WMI_KEY_USE_TX_GROUP;
152562306a36Sopenharmony_ci			break;
152662306a36Sopenharmony_ci		default:
152762306a36Sopenharmony_ci			/* TODO: Rx GTK or Tx GTK? */
152862306a36Sopenharmony_ci			wil_err(wil, "Can't determine GTK type\n");
152962306a36Sopenharmony_ci			rc = WMI_KEY_USE_RX_GROUP;
153062306a36Sopenharmony_ci			break;
153162306a36Sopenharmony_ci		}
153262306a36Sopenharmony_ci	}
153362306a36Sopenharmony_ci	wil_dbg_misc(wil, "detect_key_usage: -> %s\n", key_usage_str[rc]);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	return rc;
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_cistatic struct wil_sta_info *
153962306a36Sopenharmony_ciwil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid,
154062306a36Sopenharmony_ci			  enum wmi_key_usage key_usage, const u8 *mac_addr)
154162306a36Sopenharmony_ci{
154262306a36Sopenharmony_ci	int cid = -EINVAL;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	if (key_usage == WMI_KEY_USE_TX_GROUP)
154562306a36Sopenharmony_ci		return NULL; /* not needed */
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	/* supplicant provides Rx group key in STA mode with NULL MAC address */
154862306a36Sopenharmony_ci	if (mac_addr)
154962306a36Sopenharmony_ci		cid = wil_find_cid(wil, mid, mac_addr);
155062306a36Sopenharmony_ci	else if (key_usage == WMI_KEY_USE_RX_GROUP)
155162306a36Sopenharmony_ci		cid = wil_find_cid_by_idx(wil, mid, 0);
155262306a36Sopenharmony_ci	if (cid < 0) {
155362306a36Sopenharmony_ci		wil_err(wil, "No CID for %pM %s\n", mac_addr,
155462306a36Sopenharmony_ci			key_usage_str[key_usage]);
155562306a36Sopenharmony_ci		return ERR_PTR(cid);
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	return &wil->sta[cid];
155962306a36Sopenharmony_ci}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_civoid wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
156262306a36Sopenharmony_ci		       struct wil_sta_info *cs,
156362306a36Sopenharmony_ci		       struct key_params *params)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	struct wil_tid_crypto_rx_single *cc;
156662306a36Sopenharmony_ci	int tid;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	if (!cs)
156962306a36Sopenharmony_ci		return;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	switch (key_usage) {
157262306a36Sopenharmony_ci	case WMI_KEY_USE_STORE_PTK:
157362306a36Sopenharmony_ci	case WMI_KEY_USE_PAIRWISE:
157462306a36Sopenharmony_ci		for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
157562306a36Sopenharmony_ci			cc = &cs->tid_crypto_rx[tid].key_id[key_index];
157662306a36Sopenharmony_ci			if (params->seq)
157762306a36Sopenharmony_ci				memcpy(cc->pn, params->seq,
157862306a36Sopenharmony_ci				       IEEE80211_GCMP_PN_LEN);
157962306a36Sopenharmony_ci			else
158062306a36Sopenharmony_ci				memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
158162306a36Sopenharmony_ci			cc->key_set = true;
158262306a36Sopenharmony_ci		}
158362306a36Sopenharmony_ci		break;
158462306a36Sopenharmony_ci	case WMI_KEY_USE_RX_GROUP:
158562306a36Sopenharmony_ci		cc = &cs->group_crypto_rx.key_id[key_index];
158662306a36Sopenharmony_ci		if (params->seq)
158762306a36Sopenharmony_ci			memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
158862306a36Sopenharmony_ci		else
158962306a36Sopenharmony_ci			memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
159062306a36Sopenharmony_ci		cc->key_set = true;
159162306a36Sopenharmony_ci		break;
159262306a36Sopenharmony_ci	default:
159362306a36Sopenharmony_ci		break;
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_cistatic void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage,
159862306a36Sopenharmony_ci			   struct wil_sta_info *cs)
159962306a36Sopenharmony_ci{
160062306a36Sopenharmony_ci	struct wil_tid_crypto_rx_single *cc;
160162306a36Sopenharmony_ci	int tid;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	if (!cs)
160462306a36Sopenharmony_ci		return;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	switch (key_usage) {
160762306a36Sopenharmony_ci	case WMI_KEY_USE_PAIRWISE:
160862306a36Sopenharmony_ci		for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
160962306a36Sopenharmony_ci			cc = &cs->tid_crypto_rx[tid].key_id[key_index];
161062306a36Sopenharmony_ci			cc->key_set = false;
161162306a36Sopenharmony_ci		}
161262306a36Sopenharmony_ci		break;
161362306a36Sopenharmony_ci	case WMI_KEY_USE_RX_GROUP:
161462306a36Sopenharmony_ci		cc = &cs->group_crypto_rx.key_id[key_index];
161562306a36Sopenharmony_ci		cc->key_set = false;
161662306a36Sopenharmony_ci		break;
161762306a36Sopenharmony_ci	default:
161862306a36Sopenharmony_ci		break;
161962306a36Sopenharmony_ci	}
162062306a36Sopenharmony_ci}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cistatic int wil_cfg80211_add_key(struct wiphy *wiphy,
162362306a36Sopenharmony_ci				struct net_device *ndev, int link_id,
162462306a36Sopenharmony_ci				u8 key_index, bool pairwise,
162562306a36Sopenharmony_ci				const u8 *mac_addr,
162662306a36Sopenharmony_ci				struct key_params *params)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	int rc;
162962306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
163062306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
163162306a36Sopenharmony_ci	struct wireless_dev *wdev = vif_to_wdev(vif);
163262306a36Sopenharmony_ci	enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
163362306a36Sopenharmony_ci	struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
163462306a36Sopenharmony_ci							    key_usage,
163562306a36Sopenharmony_ci							    mac_addr);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	if (!params) {
163862306a36Sopenharmony_ci		wil_err(wil, "NULL params\n");
163962306a36Sopenharmony_ci		return -EINVAL;
164062306a36Sopenharmony_ci	}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	wil_dbg_misc(wil, "add_key: %pM %s[%d] PN %*phN\n",
164362306a36Sopenharmony_ci		     mac_addr, key_usage_str[key_usage], key_index,
164462306a36Sopenharmony_ci		     params->seq_len, params->seq);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	if (IS_ERR(cs)) {
164762306a36Sopenharmony_ci		/* in FT, sta info may not be available as add_key may be
164862306a36Sopenharmony_ci		 * sent by host before FW sends WMI_CONNECT_EVENT
164962306a36Sopenharmony_ci		 */
165062306a36Sopenharmony_ci		if (!test_bit(wil_vif_ft_roam, vif->status)) {
165162306a36Sopenharmony_ci			wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n",
165262306a36Sopenharmony_ci				mac_addr, key_usage_str[key_usage], key_index,
165362306a36Sopenharmony_ci				params->seq_len, params->seq);
165462306a36Sopenharmony_ci			return -EINVAL;
165562306a36Sopenharmony_ci		}
165662306a36Sopenharmony_ci	} else {
165762306a36Sopenharmony_ci		wil_del_rx_key(key_index, key_usage, cs);
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
166162306a36Sopenharmony_ci		wil_err(wil,
166262306a36Sopenharmony_ci			"Wrong PN len %d, %pM %s[%d] PN %*phN\n",
166362306a36Sopenharmony_ci			params->seq_len, mac_addr,
166462306a36Sopenharmony_ci			key_usage_str[key_usage], key_index,
166562306a36Sopenharmony_ci			params->seq_len, params->seq);
166662306a36Sopenharmony_ci		return -EINVAL;
166762306a36Sopenharmony_ci	}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	spin_lock_bh(&wil->eap_lock);
167062306a36Sopenharmony_ci	if (pairwise && wdev->iftype == NL80211_IFTYPE_STATION &&
167162306a36Sopenharmony_ci	    (vif->ptk_rekey_state == WIL_REKEY_M3_RECEIVED ||
167262306a36Sopenharmony_ci	     vif->ptk_rekey_state == WIL_REKEY_WAIT_M4_SENT)) {
167362306a36Sopenharmony_ci		key_usage = WMI_KEY_USE_STORE_PTK;
167462306a36Sopenharmony_ci		vif->ptk_rekey_state = WIL_REKEY_WAIT_M4_SENT;
167562306a36Sopenharmony_ci		wil_dbg_misc(wil, "Store EAPOL key\n");
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci	spin_unlock_bh(&wil->eap_lock);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
168062306a36Sopenharmony_ci				params->key, key_usage);
168162306a36Sopenharmony_ci	if (!rc && !IS_ERR(cs)) {
168262306a36Sopenharmony_ci		/* update local storage used for AP recovery */
168362306a36Sopenharmony_ci		if (key_usage == WMI_KEY_USE_TX_GROUP && params->key &&
168462306a36Sopenharmony_ci		    params->key_len <= WMI_MAX_KEY_LEN) {
168562306a36Sopenharmony_ci			vif->gtk_index = key_index;
168662306a36Sopenharmony_ci			memcpy(vif->gtk, params->key, params->key_len);
168762306a36Sopenharmony_ci			vif->gtk_len = params->key_len;
168862306a36Sopenharmony_ci		}
168962306a36Sopenharmony_ci		/* in FT set crypto will take place upon receiving
169062306a36Sopenharmony_ci		 * WMI_RING_EN_EVENTID event
169162306a36Sopenharmony_ci		 */
169262306a36Sopenharmony_ci		wil_set_crypto_rx(key_index, key_usage, cs, params);
169362306a36Sopenharmony_ci	}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	return rc;
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic int wil_cfg80211_del_key(struct wiphy *wiphy,
169962306a36Sopenharmony_ci				struct net_device *ndev, int link_id,
170062306a36Sopenharmony_ci				u8 key_index, bool pairwise,
170162306a36Sopenharmony_ci				const u8 *mac_addr)
170262306a36Sopenharmony_ci{
170362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
170462306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
170562306a36Sopenharmony_ci	struct wireless_dev *wdev = vif_to_wdev(vif);
170662306a36Sopenharmony_ci	enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
170762306a36Sopenharmony_ci	struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
170862306a36Sopenharmony_ci							    key_usage,
170962306a36Sopenharmony_ci							    mac_addr);
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr,
171262306a36Sopenharmony_ci		     key_usage_str[key_usage], key_index);
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	if (IS_ERR(cs))
171562306a36Sopenharmony_ci		wil_info(wil, "Not connected, %pM %s[%d]\n",
171662306a36Sopenharmony_ci			 mac_addr, key_usage_str[key_usage], key_index);
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(cs))
171962306a36Sopenharmony_ci		wil_del_rx_key(key_index, key_usage, cs);
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage);
172262306a36Sopenharmony_ci}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci/* Need to be present or wiphy_new() will WARN */
172562306a36Sopenharmony_cistatic int wil_cfg80211_set_default_key(struct wiphy *wiphy,
172662306a36Sopenharmony_ci					struct net_device *ndev, int link_id,
172762306a36Sopenharmony_ci					u8 key_index, bool unicast,
172862306a36Sopenharmony_ci					bool multicast)
172962306a36Sopenharmony_ci{
173062306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	wil_dbg_misc(wil, "set_default_key: entered\n");
173362306a36Sopenharmony_ci	return 0;
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic int wil_remain_on_channel(struct wiphy *wiphy,
173762306a36Sopenharmony_ci				 struct wireless_dev *wdev,
173862306a36Sopenharmony_ci				 struct ieee80211_channel *chan,
173962306a36Sopenharmony_ci				 unsigned int duration,
174062306a36Sopenharmony_ci				 u64 *cookie)
174162306a36Sopenharmony_ci{
174262306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
174362306a36Sopenharmony_ci	int rc;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	wil_dbg_misc(wil,
174662306a36Sopenharmony_ci		     "remain_on_channel: center_freq=%d, duration=%d iftype=%d\n",
174762306a36Sopenharmony_ci		     chan->center_freq, duration, wdev->iftype);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	rc = wil_p2p_listen(wil, wdev, duration, chan, cookie);
175062306a36Sopenharmony_ci	return rc;
175162306a36Sopenharmony_ci}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_cistatic int wil_cancel_remain_on_channel(struct wiphy *wiphy,
175462306a36Sopenharmony_ci					struct wireless_dev *wdev,
175562306a36Sopenharmony_ci					u64 cookie)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
175862306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	wil_dbg_misc(wil, "cancel_remain_on_channel\n");
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	return wil_p2p_cancel_listen(vif, cookie);
176362306a36Sopenharmony_ci}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci/*
176662306a36Sopenharmony_ci * find a specific IE in a list of IEs
176762306a36Sopenharmony_ci * return a pointer to the beginning of IE in the list
176862306a36Sopenharmony_ci * or NULL if not found
176962306a36Sopenharmony_ci */
177062306a36Sopenharmony_cistatic const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie,
177162306a36Sopenharmony_ci				       u16 ie_len)
177262306a36Sopenharmony_ci{
177362306a36Sopenharmony_ci	struct ieee80211_vendor_ie *vie;
177462306a36Sopenharmony_ci	u32 oui;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	/* IE tag at offset 0, length at offset 1 */
177762306a36Sopenharmony_ci	if (ie_len < 2 || 2 + ie[1] > ie_len)
177862306a36Sopenharmony_ci		return NULL;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	if (ie[0] != WLAN_EID_VENDOR_SPECIFIC)
178162306a36Sopenharmony_ci		return cfg80211_find_ie(ie[0], ies, ies_len);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	/* make sure there is room for 3 bytes OUI + 1 byte OUI type */
178462306a36Sopenharmony_ci	if (ie[1] < 4)
178562306a36Sopenharmony_ci		return NULL;
178662306a36Sopenharmony_ci	vie = (struct ieee80211_vendor_ie *)ie;
178762306a36Sopenharmony_ci	oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2];
178862306a36Sopenharmony_ci	return cfg80211_find_vendor_ie(oui, vie->oui_type, ies,
178962306a36Sopenharmony_ci				       ies_len);
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci/*
179362306a36Sopenharmony_ci * merge the IEs in two lists into a single list.
179462306a36Sopenharmony_ci * do not include IEs from the second list which exist in the first list.
179562306a36Sopenharmony_ci * add only vendor specific IEs from second list to keep
179662306a36Sopenharmony_ci * the merged list sorted (since vendor-specific IE has the
179762306a36Sopenharmony_ci * highest tag number)
179862306a36Sopenharmony_ci * caller must free the allocated memory for merged IEs
179962306a36Sopenharmony_ci */
180062306a36Sopenharmony_cistatic int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len,
180162306a36Sopenharmony_ci					 const u8 *ies2, u16 ies2_len,
180262306a36Sopenharmony_ci					 u8 **merged_ies, u16 *merged_len)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	u8 *buf, *dpos;
180562306a36Sopenharmony_ci	const u8 *spos;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	if (!ies1)
180862306a36Sopenharmony_ci		ies1_len = 0;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	if (!ies2)
181162306a36Sopenharmony_ci		ies2_len = 0;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	if (ies1_len == 0 && ies2_len == 0) {
181462306a36Sopenharmony_ci		*merged_ies = NULL;
181562306a36Sopenharmony_ci		*merged_len = 0;
181662306a36Sopenharmony_ci		return 0;
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL);
182062306a36Sopenharmony_ci	if (!buf)
182162306a36Sopenharmony_ci		return -ENOMEM;
182262306a36Sopenharmony_ci	if (ies1)
182362306a36Sopenharmony_ci		memcpy(buf, ies1, ies1_len);
182462306a36Sopenharmony_ci	dpos = buf + ies1_len;
182562306a36Sopenharmony_ci	spos = ies2;
182662306a36Sopenharmony_ci	while (spos && (spos + 1 < ies2 + ies2_len)) {
182762306a36Sopenharmony_ci		/* IE tag at offset 0, length at offset 1 */
182862306a36Sopenharmony_ci		u16 ielen = 2 + spos[1];
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci		if (spos + ielen > ies2 + ies2_len)
183162306a36Sopenharmony_ci			break;
183262306a36Sopenharmony_ci		if (spos[0] == WLAN_EID_VENDOR_SPECIFIC &&
183362306a36Sopenharmony_ci		    (!ies1 || !_wil_cfg80211_find_ie(ies1, ies1_len,
183462306a36Sopenharmony_ci						     spos, ielen))) {
183562306a36Sopenharmony_ci			memcpy(dpos, spos, ielen);
183662306a36Sopenharmony_ci			dpos += ielen;
183762306a36Sopenharmony_ci		}
183862306a36Sopenharmony_ci		spos += ielen;
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	*merged_ies = buf;
184262306a36Sopenharmony_ci	*merged_len = dpos - buf;
184362306a36Sopenharmony_ci	return 0;
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic void wil_print_bcon_data(struct cfg80211_beacon_data *b)
184762306a36Sopenharmony_ci{
184862306a36Sopenharmony_ci	wil_hex_dump_misc("head     ", DUMP_PREFIX_OFFSET, 16, 1,
184962306a36Sopenharmony_ci			  b->head, b->head_len, true);
185062306a36Sopenharmony_ci	wil_hex_dump_misc("tail     ", DUMP_PREFIX_OFFSET, 16, 1,
185162306a36Sopenharmony_ci			  b->tail, b->tail_len, true);
185262306a36Sopenharmony_ci	wil_hex_dump_misc("BCON IE  ", DUMP_PREFIX_OFFSET, 16, 1,
185362306a36Sopenharmony_ci			  b->beacon_ies, b->beacon_ies_len, true);
185462306a36Sopenharmony_ci	wil_hex_dump_misc("PROBE    ", DUMP_PREFIX_OFFSET, 16, 1,
185562306a36Sopenharmony_ci			  b->probe_resp, b->probe_resp_len, true);
185662306a36Sopenharmony_ci	wil_hex_dump_misc("PROBE IE ", DUMP_PREFIX_OFFSET, 16, 1,
185762306a36Sopenharmony_ci			  b->proberesp_ies, b->proberesp_ies_len, true);
185862306a36Sopenharmony_ci	wil_hex_dump_misc("ASSOC IE ", DUMP_PREFIX_OFFSET, 16, 1,
185962306a36Sopenharmony_ci			  b->assocresp_ies, b->assocresp_ies_len, true);
186062306a36Sopenharmony_ci}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci/* internal functions for device reset and starting AP */
186362306a36Sopenharmony_cistatic u8 *
186462306a36Sopenharmony_ci_wil_cfg80211_get_proberesp_ies(const u8 *proberesp, u16 proberesp_len,
186562306a36Sopenharmony_ci				u16 *ies_len)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	u8 *ies = NULL;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	if (proberesp) {
187062306a36Sopenharmony_ci		struct ieee80211_mgmt *f =
187162306a36Sopenharmony_ci			(struct ieee80211_mgmt *)proberesp;
187262306a36Sopenharmony_ci		size_t hlen = offsetof(struct ieee80211_mgmt,
187362306a36Sopenharmony_ci				       u.probe_resp.variable);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci		ies = f->u.probe_resp.variable;
187662306a36Sopenharmony_ci		if (ies_len)
187762306a36Sopenharmony_ci			*ies_len = proberesp_len - hlen;
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	return ies;
188162306a36Sopenharmony_ci}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_cistatic int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
188462306a36Sopenharmony_ci				 struct cfg80211_beacon_data *bcon)
188562306a36Sopenharmony_ci{
188662306a36Sopenharmony_ci	int rc;
188762306a36Sopenharmony_ci	u16 len = 0, proberesp_len = 0;
188862306a36Sopenharmony_ci	u8 *ies = NULL, *proberesp;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	/* update local storage used for AP recovery */
189162306a36Sopenharmony_ci	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, bcon->probe_resp,
189262306a36Sopenharmony_ci		      bcon->probe_resp_len);
189362306a36Sopenharmony_ci	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len,
189462306a36Sopenharmony_ci		      bcon->proberesp_ies, bcon->proberesp_ies_len);
189562306a36Sopenharmony_ci	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len,
189662306a36Sopenharmony_ci		      bcon->assocresp_ies, bcon->assocresp_ies_len);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
189962306a36Sopenharmony_ci						    bcon->probe_resp_len,
190062306a36Sopenharmony_ci						    &proberesp_len);
190162306a36Sopenharmony_ci	rc = _wil_cfg80211_merge_extra_ies(proberesp,
190262306a36Sopenharmony_ci					   proberesp_len,
190362306a36Sopenharmony_ci					   bcon->proberesp_ies,
190462306a36Sopenharmony_ci					   bcon->proberesp_ies_len,
190562306a36Sopenharmony_ci					   &ies, &len);
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	if (rc)
190862306a36Sopenharmony_ci		goto out;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies);
191162306a36Sopenharmony_ci	if (rc)
191262306a36Sopenharmony_ci		goto out;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	if (bcon->assocresp_ies)
191562306a36Sopenharmony_ci		rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP,
191662306a36Sopenharmony_ci				bcon->assocresp_ies_len, bcon->assocresp_ies);
191762306a36Sopenharmony_ci	else
191862306a36Sopenharmony_ci		rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies);
191962306a36Sopenharmony_ci#if 0 /* to use beacon IE's, remove this #if 0 */
192062306a36Sopenharmony_ci	if (rc)
192162306a36Sopenharmony_ci		goto out;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	rc = wmi_set_ie(vif, WMI_FRAME_BEACON,
192462306a36Sopenharmony_ci			bcon->tail_len, bcon->tail);
192562306a36Sopenharmony_ci#endif
192662306a36Sopenharmony_ciout:
192762306a36Sopenharmony_ci	kfree(ies);
192862306a36Sopenharmony_ci	return rc;
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic int _wil_cfg80211_start_ap(struct wiphy *wiphy,
193262306a36Sopenharmony_ci				  struct net_device *ndev,
193362306a36Sopenharmony_ci				  const u8 *ssid, size_t ssid_len, u32 privacy,
193462306a36Sopenharmony_ci				  int bi, u8 chan, u8 wmi_edmg_channel,
193562306a36Sopenharmony_ci				  struct cfg80211_beacon_data *bcon,
193662306a36Sopenharmony_ci				  u8 hidden_ssid, u32 pbss)
193762306a36Sopenharmony_ci{
193862306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
193962306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
194062306a36Sopenharmony_ci	int rc;
194162306a36Sopenharmony_ci	struct wireless_dev *wdev = ndev->ieee80211_ptr;
194262306a36Sopenharmony_ci	u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
194362306a36Sopenharmony_ci	u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO);
194462306a36Sopenharmony_ci	u16 proberesp_len = 0;
194562306a36Sopenharmony_ci	u8 *proberesp;
194662306a36Sopenharmony_ci	bool ft = false;
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	if (pbss)
194962306a36Sopenharmony_ci		wmi_nettype = WMI_NETTYPE_P2P;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go);
195262306a36Sopenharmony_ci	if (is_go && !pbss) {
195362306a36Sopenharmony_ci		wil_err(wil, "P2P GO must be in PBSS\n");
195462306a36Sopenharmony_ci		return -ENOTSUPP;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	wil_set_recovery_state(wil, fw_recovery_idle);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
196062306a36Sopenharmony_ci						    bcon->probe_resp_len,
196162306a36Sopenharmony_ci						    &proberesp_len);
196262306a36Sopenharmony_ci	/* check that the probe response IEs has a MDE */
196362306a36Sopenharmony_ci	if ((proberesp && proberesp_len > 0 &&
196462306a36Sopenharmony_ci	     cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN,
196562306a36Sopenharmony_ci			      proberesp,
196662306a36Sopenharmony_ci			      proberesp_len)))
196762306a36Sopenharmony_ci		ft = true;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	if (ft) {
197062306a36Sopenharmony_ci		if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING,
197162306a36Sopenharmony_ci			      wil->fw_capabilities)) {
197262306a36Sopenharmony_ci			wil_err(wil, "FW does not support FT roaming\n");
197362306a36Sopenharmony_ci			return -ENOTSUPP;
197462306a36Sopenharmony_ci		}
197562306a36Sopenharmony_ci		set_bit(wil_vif_ft_roam, vif->status);
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
198162306a36Sopenharmony_ci		__wil_down(wil);
198262306a36Sopenharmony_ci		rc = __wil_up(wil);
198362306a36Sopenharmony_ci		if (rc)
198462306a36Sopenharmony_ci			goto out;
198562306a36Sopenharmony_ci	}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	rc = wmi_set_ssid(vif, ssid_len, ssid);
198862306a36Sopenharmony_ci	if (rc)
198962306a36Sopenharmony_ci		goto out;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	rc = _wil_cfg80211_set_ies(vif, bcon);
199262306a36Sopenharmony_ci	if (rc)
199362306a36Sopenharmony_ci		goto out;
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	vif->privacy = privacy;
199662306a36Sopenharmony_ci	vif->channel = chan;
199762306a36Sopenharmony_ci	vif->wmi_edmg_channel = wmi_edmg_channel;
199862306a36Sopenharmony_ci	vif->hidden_ssid = hidden_ssid;
199962306a36Sopenharmony_ci	vif->pbss = pbss;
200062306a36Sopenharmony_ci	vif->bi = bi;
200162306a36Sopenharmony_ci	memcpy(vif->ssid, ssid, ssid_len);
200262306a36Sopenharmony_ci	vif->ssid_len = ssid_len;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	netif_carrier_on(ndev);
200562306a36Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
200662306a36Sopenharmony_ci		wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, wmi_edmg_channel,
200962306a36Sopenharmony_ci			   hidden_ssid, is_go);
201062306a36Sopenharmony_ci	if (rc)
201162306a36Sopenharmony_ci		goto err_pcp_start;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	rc = wil_bcast_init(vif);
201462306a36Sopenharmony_ci	if (rc)
201562306a36Sopenharmony_ci		goto err_bcast;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	goto out; /* success */
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_cierr_bcast:
202062306a36Sopenharmony_ci	wmi_pcp_stop(vif);
202162306a36Sopenharmony_cierr_pcp_start:
202262306a36Sopenharmony_ci	netif_carrier_off(ndev);
202362306a36Sopenharmony_ci	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
202462306a36Sopenharmony_ci		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
202562306a36Sopenharmony_ciout:
202662306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
202762306a36Sopenharmony_ci	return rc;
202862306a36Sopenharmony_ci}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_civoid wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	int rc, i;
203362306a36Sopenharmony_ci	struct wiphy *wiphy = wil_to_wiphy(wil);
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
203662306a36Sopenharmony_ci		struct wil6210_vif *vif = wil->vifs[i];
203762306a36Sopenharmony_ci		struct net_device *ndev;
203862306a36Sopenharmony_ci		struct cfg80211_beacon_data bcon = {};
203962306a36Sopenharmony_ci		struct key_params key_params = {};
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci		if (!vif || vif->ssid_len == 0)
204262306a36Sopenharmony_ci			continue;
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci		ndev = vif_to_ndev(vif);
204562306a36Sopenharmony_ci		bcon.proberesp_ies = vif->proberesp_ies;
204662306a36Sopenharmony_ci		bcon.assocresp_ies = vif->assocresp_ies;
204762306a36Sopenharmony_ci		bcon.probe_resp = vif->proberesp;
204862306a36Sopenharmony_ci		bcon.proberesp_ies_len = vif->proberesp_ies_len;
204962306a36Sopenharmony_ci		bcon.assocresp_ies_len = vif->assocresp_ies_len;
205062306a36Sopenharmony_ci		bcon.probe_resp_len = vif->proberesp_len;
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci		wil_info(wil,
205362306a36Sopenharmony_ci			 "AP (vif %d) recovery: privacy %d, bi %d, channel %d, hidden %d, pbss %d\n",
205462306a36Sopenharmony_ci			 i, vif->privacy, vif->bi, vif->channel,
205562306a36Sopenharmony_ci			 vif->hidden_ssid, vif->pbss);
205662306a36Sopenharmony_ci		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
205762306a36Sopenharmony_ci				  vif->ssid, vif->ssid_len, true);
205862306a36Sopenharmony_ci		rc = _wil_cfg80211_start_ap(wiphy, ndev,
205962306a36Sopenharmony_ci					    vif->ssid, vif->ssid_len,
206062306a36Sopenharmony_ci					    vif->privacy, vif->bi,
206162306a36Sopenharmony_ci					    vif->channel,
206262306a36Sopenharmony_ci					    vif->wmi_edmg_channel, &bcon,
206362306a36Sopenharmony_ci					    vif->hidden_ssid, vif->pbss);
206462306a36Sopenharmony_ci		if (rc) {
206562306a36Sopenharmony_ci			wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
206662306a36Sopenharmony_ci			continue;
206762306a36Sopenharmony_ci		}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci		if (!vif->privacy || vif->gtk_len == 0)
207062306a36Sopenharmony_ci			continue;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci		key_params.key = vif->gtk;
207362306a36Sopenharmony_ci		key_params.key_len = vif->gtk_len;
207462306a36Sopenharmony_ci		key_params.seq_len = IEEE80211_GCMP_PN_LEN;
207562306a36Sopenharmony_ci		rc = wil_cfg80211_add_key(wiphy, ndev, -1, vif->gtk_index,
207662306a36Sopenharmony_ci					  false, NULL, &key_params);
207762306a36Sopenharmony_ci		if (rc)
207862306a36Sopenharmony_ci			wil_err(wil, "vif %d recovery add key failed (%d)\n",
207962306a36Sopenharmony_ci				i, rc);
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_cistatic int wil_cfg80211_change_beacon(struct wiphy *wiphy,
208462306a36Sopenharmony_ci				      struct net_device *ndev,
208562306a36Sopenharmony_ci				      struct cfg80211_beacon_data *bcon)
208662306a36Sopenharmony_ci{
208762306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
208862306a36Sopenharmony_ci	struct wireless_dev *wdev = ndev->ieee80211_ptr;
208962306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
209062306a36Sopenharmony_ci	int rc;
209162306a36Sopenharmony_ci	u32 privacy = 0;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid);
209462306a36Sopenharmony_ci	wil_print_bcon_data(bcon);
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	if (bcon->tail &&
209762306a36Sopenharmony_ci	    cfg80211_find_ie(WLAN_EID_RSN, bcon->tail,
209862306a36Sopenharmony_ci			     bcon->tail_len))
209962306a36Sopenharmony_ci		privacy = 1;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	memcpy(vif->ssid, wdev->u.ap.ssid, wdev->u.ap.ssid_len);
210262306a36Sopenharmony_ci	vif->ssid_len = wdev->u.ap.ssid_len;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	/* in case privacy has changed, need to restart the AP */
210562306a36Sopenharmony_ci	if (vif->privacy != privacy) {
210662306a36Sopenharmony_ci		wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
210762306a36Sopenharmony_ci			     vif->privacy, privacy);
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci		rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
211062306a36Sopenharmony_ci					    vif->ssid_len, privacy,
211162306a36Sopenharmony_ci					    wdev->links[0].ap.beacon_interval,
211262306a36Sopenharmony_ci					    vif->channel,
211362306a36Sopenharmony_ci					    vif->wmi_edmg_channel, bcon,
211462306a36Sopenharmony_ci					    vif->hidden_ssid,
211562306a36Sopenharmony_ci					    vif->pbss);
211662306a36Sopenharmony_ci	} else {
211762306a36Sopenharmony_ci		rc = _wil_cfg80211_set_ies(vif, bcon);
211862306a36Sopenharmony_ci	}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	return rc;
212162306a36Sopenharmony_ci}
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_cistatic int wil_cfg80211_start_ap(struct wiphy *wiphy,
212462306a36Sopenharmony_ci				 struct net_device *ndev,
212562306a36Sopenharmony_ci				 struct cfg80211_ap_settings *info)
212662306a36Sopenharmony_ci{
212762306a36Sopenharmony_ci	int rc;
212862306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
212962306a36Sopenharmony_ci	struct ieee80211_channel *channel = info->chandef.chan;
213062306a36Sopenharmony_ci	struct cfg80211_beacon_data *bcon = &info->beacon;
213162306a36Sopenharmony_ci	struct cfg80211_crypto_settings *crypto = &info->crypto;
213262306a36Sopenharmony_ci	u8 wmi_edmg_channel;
213362306a36Sopenharmony_ci	u8 hidden_ssid;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	wil_dbg_misc(wil, "start_ap\n");
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	rc = wil_get_wmi_edmg_channel(wil, info->chandef.edmg.bw_config,
213862306a36Sopenharmony_ci				      info->chandef.edmg.channels,
213962306a36Sopenharmony_ci				      &wmi_edmg_channel);
214062306a36Sopenharmony_ci	if (rc < 0)
214162306a36Sopenharmony_ci		return rc;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	if (!channel) {
214462306a36Sopenharmony_ci		wil_err(wil, "AP: No channel???\n");
214562306a36Sopenharmony_ci		return -EINVAL;
214662306a36Sopenharmony_ci	}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	switch (info->hidden_ssid) {
214962306a36Sopenharmony_ci	case NL80211_HIDDEN_SSID_NOT_IN_USE:
215062306a36Sopenharmony_ci		hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
215162306a36Sopenharmony_ci		break;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	case NL80211_HIDDEN_SSID_ZERO_LEN:
215462306a36Sopenharmony_ci		hidden_ssid = WMI_HIDDEN_SSID_SEND_EMPTY;
215562306a36Sopenharmony_ci		break;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
215862306a36Sopenharmony_ci		hidden_ssid = WMI_HIDDEN_SSID_CLEAR;
215962306a36Sopenharmony_ci		break;
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	default:
216262306a36Sopenharmony_ci		wil_err(wil, "AP: Invalid hidden SSID %d\n", info->hidden_ssid);
216362306a36Sopenharmony_ci		return -EOPNOTSUPP;
216462306a36Sopenharmony_ci	}
216562306a36Sopenharmony_ci	wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
216662306a36Sopenharmony_ci		     channel->center_freq, info->privacy ? "secure" : "open");
216762306a36Sopenharmony_ci	wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
216862306a36Sopenharmony_ci		     info->privacy, info->auth_type);
216962306a36Sopenharmony_ci	wil_dbg_misc(wil, "Hidden SSID mode: %d\n",
217062306a36Sopenharmony_ci		     info->hidden_ssid);
217162306a36Sopenharmony_ci	wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
217262306a36Sopenharmony_ci		     info->dtim_period);
217362306a36Sopenharmony_ci	wil_dbg_misc(wil, "PBSS %d\n", info->pbss);
217462306a36Sopenharmony_ci	wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
217562306a36Sopenharmony_ci			  info->ssid, info->ssid_len, true);
217662306a36Sopenharmony_ci	wil_print_bcon_data(bcon);
217762306a36Sopenharmony_ci	wil_print_crypto(wil, crypto);
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	rc = _wil_cfg80211_start_ap(wiphy, ndev,
218062306a36Sopenharmony_ci				    info->ssid, info->ssid_len, info->privacy,
218162306a36Sopenharmony_ci				    info->beacon_interval, channel->hw_value,
218262306a36Sopenharmony_ci				    wmi_edmg_channel, bcon, hidden_ssid,
218362306a36Sopenharmony_ci				    info->pbss);
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	return rc;
218662306a36Sopenharmony_ci}
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_cistatic int wil_cfg80211_stop_ap(struct wiphy *wiphy,
218962306a36Sopenharmony_ci				struct net_device *ndev,
219062306a36Sopenharmony_ci				unsigned int link_id)
219162306a36Sopenharmony_ci{
219262306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
219362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(ndev);
219462306a36Sopenharmony_ci	bool last;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	netif_carrier_off(ndev);
219962306a36Sopenharmony_ci	last = !wil_has_other_active_ifaces(wil, ndev, false, true);
220062306a36Sopenharmony_ci	if (last) {
220162306a36Sopenharmony_ci		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
220262306a36Sopenharmony_ci		wil_set_recovery_state(wil, fw_recovery_idle);
220362306a36Sopenharmony_ci		set_bit(wil_status_resetting, wil->status);
220462306a36Sopenharmony_ci	}
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	wmi_pcp_stop(vif);
220962306a36Sopenharmony_ci	clear_bit(wil_vif_ft_roam, vif->status);
221062306a36Sopenharmony_ci	vif->ssid_len = 0;
221162306a36Sopenharmony_ci	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, NULL, 0);
221262306a36Sopenharmony_ci	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len, NULL, 0);
221362306a36Sopenharmony_ci	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len, NULL, 0);
221462306a36Sopenharmony_ci	memset(vif->gtk, 0, WMI_MAX_KEY_LEN);
221562306a36Sopenharmony_ci	vif->gtk_len = 0;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	if (last)
221862306a36Sopenharmony_ci		__wil_down(wil);
221962306a36Sopenharmony_ci	else
222062306a36Sopenharmony_ci		wil_bcast_fini(vif);
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	return 0;
222562306a36Sopenharmony_ci}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_cistatic int wil_cfg80211_add_station(struct wiphy *wiphy,
222862306a36Sopenharmony_ci				    struct net_device *dev,
222962306a36Sopenharmony_ci				    const u8 *mac,
223062306a36Sopenharmony_ci				    struct station_parameters *params)
223162306a36Sopenharmony_ci{
223262306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
223362306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	wil_dbg_misc(wil, "add station %pM aid %d mid %d mask 0x%x set 0x%x\n",
223662306a36Sopenharmony_ci		     mac, params->aid, vif->mid,
223762306a36Sopenharmony_ci		     params->sta_flags_mask, params->sta_flags_set);
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	if (!disable_ap_sme) {
224062306a36Sopenharmony_ci		wil_err(wil, "not supported with AP SME enabled\n");
224162306a36Sopenharmony_ci		return -EOPNOTSUPP;
224262306a36Sopenharmony_ci	}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	if (params->aid > WIL_MAX_DMG_AID) {
224562306a36Sopenharmony_ci		wil_err(wil, "invalid aid\n");
224662306a36Sopenharmony_ci		return -EINVAL;
224762306a36Sopenharmony_ci	}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	return wmi_new_sta(vif, mac, params->aid);
225062306a36Sopenharmony_ci}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_cistatic int wil_cfg80211_del_station(struct wiphy *wiphy,
225362306a36Sopenharmony_ci				    struct net_device *dev,
225462306a36Sopenharmony_ci				    struct station_del_parameters *params)
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
225762306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n",
226062306a36Sopenharmony_ci		     params->mac, params->reason_code, vif->mid);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
226362306a36Sopenharmony_ci	wil6210_disconnect(vif, params->mac, params->reason_code);
226462306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	return 0;
226762306a36Sopenharmony_ci}
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_cistatic int wil_cfg80211_change_station(struct wiphy *wiphy,
227062306a36Sopenharmony_ci				       struct net_device *dev,
227162306a36Sopenharmony_ci				       const u8 *mac,
227262306a36Sopenharmony_ci				       struct station_parameters *params)
227362306a36Sopenharmony_ci{
227462306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
227562306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
227662306a36Sopenharmony_ci	int authorize;
227762306a36Sopenharmony_ci	int cid, i;
227862306a36Sopenharmony_ci	struct wil_ring_tx_data *txdata = NULL;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n",
228162306a36Sopenharmony_ci		     mac, params->sta_flags_mask, params->sta_flags_set,
228262306a36Sopenharmony_ci		     vif->mid);
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	if (!disable_ap_sme) {
228562306a36Sopenharmony_ci		wil_dbg_misc(wil, "not supported with AP SME enabled\n");
228662306a36Sopenharmony_ci		return -EOPNOTSUPP;
228762306a36Sopenharmony_ci	}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
229062306a36Sopenharmony_ci		return 0;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	cid = wil_find_cid(wil, vif->mid, mac);
229362306a36Sopenharmony_ci	if (cid < 0) {
229462306a36Sopenharmony_ci		wil_err(wil, "station not found\n");
229562306a36Sopenharmony_ci		return -ENOLINK;
229662306a36Sopenharmony_ci	}
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++)
229962306a36Sopenharmony_ci		if (wil->ring2cid_tid[i][0] == cid) {
230062306a36Sopenharmony_ci			txdata = &wil->ring_tx_data[i];
230162306a36Sopenharmony_ci			break;
230262306a36Sopenharmony_ci		}
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	if (!txdata) {
230562306a36Sopenharmony_ci		wil_err(wil, "ring data not found\n");
230662306a36Sopenharmony_ci		return -ENOLINK;
230762306a36Sopenharmony_ci	}
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	authorize = params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED);
231062306a36Sopenharmony_ci	txdata->dot1x_open = authorize ? 1 : 0;
231162306a36Sopenharmony_ci	wil_dbg_misc(wil, "cid %d ring %d authorize %d\n", cid, i,
231262306a36Sopenharmony_ci		     txdata->dot1x_open);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	return 0;
231562306a36Sopenharmony_ci}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci/* probe_client handling */
231862306a36Sopenharmony_cistatic void wil_probe_client_handle(struct wil6210_priv *wil,
231962306a36Sopenharmony_ci				    struct wil6210_vif *vif,
232062306a36Sopenharmony_ci				    struct wil_probe_client_req *req)
232162306a36Sopenharmony_ci{
232262306a36Sopenharmony_ci	struct net_device *ndev = vif_to_ndev(vif);
232362306a36Sopenharmony_ci	struct wil_sta_info *sta = &wil->sta[req->cid];
232462306a36Sopenharmony_ci	/* assume STA is alive if it is still connected,
232562306a36Sopenharmony_ci	 * else FW will disconnect it
232662306a36Sopenharmony_ci	 */
232762306a36Sopenharmony_ci	bool alive = (sta->status == wil_sta_connected);
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	cfg80211_probe_status(ndev, sta->addr, req->cookie, alive,
233062306a36Sopenharmony_ci			      0, false, GFP_KERNEL);
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic struct list_head *next_probe_client(struct wil6210_vif *vif)
233462306a36Sopenharmony_ci{
233562306a36Sopenharmony_ci	struct list_head *ret = NULL;
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	mutex_lock(&vif->probe_client_mutex);
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	if (!list_empty(&vif->probe_client_pending)) {
234062306a36Sopenharmony_ci		ret = vif->probe_client_pending.next;
234162306a36Sopenharmony_ci		list_del(ret);
234262306a36Sopenharmony_ci	}
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	mutex_unlock(&vif->probe_client_mutex);
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	return ret;
234762306a36Sopenharmony_ci}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_civoid wil_probe_client_worker(struct work_struct *work)
235062306a36Sopenharmony_ci{
235162306a36Sopenharmony_ci	struct wil6210_vif *vif = container_of(work, struct wil6210_vif,
235262306a36Sopenharmony_ci					       probe_client_worker);
235362306a36Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
235462306a36Sopenharmony_ci	struct wil_probe_client_req *req;
235562306a36Sopenharmony_ci	struct list_head *lh;
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	while ((lh = next_probe_client(vif)) != NULL) {
235862306a36Sopenharmony_ci		req = list_entry(lh, struct wil_probe_client_req, list);
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci		wil_probe_client_handle(wil, vif, req);
236162306a36Sopenharmony_ci		kfree(req);
236262306a36Sopenharmony_ci	}
236362306a36Sopenharmony_ci}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_civoid wil_probe_client_flush(struct wil6210_vif *vif)
236662306a36Sopenharmony_ci{
236762306a36Sopenharmony_ci	struct wil_probe_client_req *req, *t;
236862306a36Sopenharmony_ci	struct wil6210_priv *wil = vif_to_wil(vif);
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	wil_dbg_misc(wil, "probe_client_flush\n");
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	mutex_lock(&vif->probe_client_mutex);
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) {
237562306a36Sopenharmony_ci		list_del(&req->list);
237662306a36Sopenharmony_ci		kfree(req);
237762306a36Sopenharmony_ci	}
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	mutex_unlock(&vif->probe_client_mutex);
238062306a36Sopenharmony_ci}
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_cistatic int wil_cfg80211_probe_client(struct wiphy *wiphy,
238362306a36Sopenharmony_ci				     struct net_device *dev,
238462306a36Sopenharmony_ci				     const u8 *peer, u64 *cookie)
238562306a36Sopenharmony_ci{
238662306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
238762306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
238862306a36Sopenharmony_ci	struct wil_probe_client_req *req;
238962306a36Sopenharmony_ci	int cid = wil_find_cid(wil, vif->mid, peer);
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n",
239262306a36Sopenharmony_ci		     peer, cid, vif->mid);
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	if (cid < 0)
239562306a36Sopenharmony_ci		return -ENOLINK;
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
239862306a36Sopenharmony_ci	if (!req)
239962306a36Sopenharmony_ci		return -ENOMEM;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	req->cid = cid;
240262306a36Sopenharmony_ci	req->cookie = cid;
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	mutex_lock(&vif->probe_client_mutex);
240562306a36Sopenharmony_ci	list_add_tail(&req->list, &vif->probe_client_pending);
240662306a36Sopenharmony_ci	mutex_unlock(&vif->probe_client_mutex);
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	*cookie = req->cookie;
240962306a36Sopenharmony_ci	queue_work(wil->wq_service, &vif->probe_client_worker);
241062306a36Sopenharmony_ci	return 0;
241162306a36Sopenharmony_ci}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_cistatic int wil_cfg80211_change_bss(struct wiphy *wiphy,
241462306a36Sopenharmony_ci				   struct net_device *dev,
241562306a36Sopenharmony_ci				   struct bss_parameters *params)
241662306a36Sopenharmony_ci{
241762306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
241862306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	if (params->ap_isolate >= 0) {
242162306a36Sopenharmony_ci		wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n",
242262306a36Sopenharmony_ci			     vif->mid, vif->ap_isolate, params->ap_isolate);
242362306a36Sopenharmony_ci		vif->ap_isolate = params->ap_isolate;
242462306a36Sopenharmony_ci	}
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	return 0;
242762306a36Sopenharmony_ci}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_cistatic int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
243062306a36Sopenharmony_ci				       struct net_device *dev,
243162306a36Sopenharmony_ci				       bool enabled, int timeout)
243262306a36Sopenharmony_ci{
243362306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
243462306a36Sopenharmony_ci	enum wmi_ps_profile_type ps_profile;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	wil_dbg_misc(wil, "enabled=%d, timeout=%d\n",
243762306a36Sopenharmony_ci		     enabled, timeout);
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	if (enabled)
244062306a36Sopenharmony_ci		ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT;
244162306a36Sopenharmony_ci	else
244262306a36Sopenharmony_ci		ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	return wil_ps_update(wil, ps_profile);
244562306a36Sopenharmony_ci}
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_cistatic int wil_cfg80211_suspend(struct wiphy *wiphy,
244862306a36Sopenharmony_ci				struct cfg80211_wowlan *wow)
244962306a36Sopenharmony_ci{
245062306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
245162306a36Sopenharmony_ci	int rc;
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	/* Setting the wakeup trigger based on wow is TBD */
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	if (test_bit(wil_status_suspended, wil->status)) {
245662306a36Sopenharmony_ci		wil_dbg_pm(wil, "trying to suspend while suspended\n");
245762306a36Sopenharmony_ci		return 0;
245862306a36Sopenharmony_ci	}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	rc = wil_can_suspend(wil, false);
246162306a36Sopenharmony_ci	if (rc)
246262306a36Sopenharmony_ci		goto out;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	wil_dbg_pm(wil, "suspending\n");
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	mutex_lock(&wil->mutex);
246762306a36Sopenharmony_ci	mutex_lock(&wil->vif_mutex);
246862306a36Sopenharmony_ci	wil_p2p_stop_radio_operations(wil);
246962306a36Sopenharmony_ci	wil_abort_scan_all_vifs(wil, true);
247062306a36Sopenharmony_ci	mutex_unlock(&wil->vif_mutex);
247162306a36Sopenharmony_ci	mutex_unlock(&wil->mutex);
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ciout:
247462306a36Sopenharmony_ci	return rc;
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_cistatic int wil_cfg80211_resume(struct wiphy *wiphy)
247862306a36Sopenharmony_ci{
247962306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	wil_dbg_pm(wil, "resuming\n");
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	return 0;
248462306a36Sopenharmony_ci}
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_cistatic int
248762306a36Sopenharmony_ciwil_cfg80211_sched_scan_start(struct wiphy *wiphy,
248862306a36Sopenharmony_ci			      struct net_device *dev,
248962306a36Sopenharmony_ci			      struct cfg80211_sched_scan_request *request)
249062306a36Sopenharmony_ci{
249162306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
249262306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
249362306a36Sopenharmony_ci	int i, rc;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	if (vif->mid != 0)
249662306a36Sopenharmony_ci		return -EOPNOTSUPP;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	wil_dbg_misc(wil,
249962306a36Sopenharmony_ci		     "sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
250062306a36Sopenharmony_ci		     request->n_ssids, request->ie_len, request->flags);
250162306a36Sopenharmony_ci	for (i = 0; i < request->n_ssids; i++) {
250262306a36Sopenharmony_ci		wil_dbg_misc(wil, "SSID[%d]:", i);
250362306a36Sopenharmony_ci		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
250462306a36Sopenharmony_ci				  request->ssids[i].ssid,
250562306a36Sopenharmony_ci				  request->ssids[i].ssid_len, true);
250662306a36Sopenharmony_ci	}
250762306a36Sopenharmony_ci	wil_dbg_misc(wil, "channels:");
250862306a36Sopenharmony_ci	for (i = 0; i < request->n_channels; i++)
250962306a36Sopenharmony_ci		wil_dbg_misc(wil, " %d%s", request->channels[i]->hw_value,
251062306a36Sopenharmony_ci			     i == request->n_channels - 1 ? "\n" : "");
251162306a36Sopenharmony_ci	wil_dbg_misc(wil, "n_match_sets %d, min_rssi_thold %d, delay %d\n",
251262306a36Sopenharmony_ci		     request->n_match_sets, request->min_rssi_thold,
251362306a36Sopenharmony_ci		     request->delay);
251462306a36Sopenharmony_ci	for (i = 0; i < request->n_match_sets; i++) {
251562306a36Sopenharmony_ci		struct cfg80211_match_set *ms = &request->match_sets[i];
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci		wil_dbg_misc(wil, "MATCHSET[%d]: rssi_thold %d\n",
251862306a36Sopenharmony_ci			     i, ms->rssi_thold);
251962306a36Sopenharmony_ci		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
252062306a36Sopenharmony_ci				  ms->ssid.ssid,
252162306a36Sopenharmony_ci				  ms->ssid.ssid_len, true);
252262306a36Sopenharmony_ci	}
252362306a36Sopenharmony_ci	wil_dbg_misc(wil, "n_scan_plans %d\n", request->n_scan_plans);
252462306a36Sopenharmony_ci	for (i = 0; i < request->n_scan_plans; i++) {
252562306a36Sopenharmony_ci		struct cfg80211_sched_scan_plan *sp = &request->scan_plans[i];
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci		wil_dbg_misc(wil, "SCAN PLAN[%d]: interval %d iterations %d\n",
252862306a36Sopenharmony_ci			     i, sp->interval, sp->iterations);
252962306a36Sopenharmony_ci	}
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
253262306a36Sopenharmony_ci			request->ie_len, request->ie);
253362306a36Sopenharmony_ci	if (rc)
253462306a36Sopenharmony_ci		return rc;
253562306a36Sopenharmony_ci	return wmi_start_sched_scan(wil, request);
253662306a36Sopenharmony_ci}
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_cistatic int
253962306a36Sopenharmony_ciwil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
254062306a36Sopenharmony_ci			     u64 reqid)
254162306a36Sopenharmony_ci{
254262306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
254362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
254462306a36Sopenharmony_ci	int rc;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	if (vif->mid != 0)
254762306a36Sopenharmony_ci		return -EOPNOTSUPP;
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	rc = wmi_stop_sched_scan(wil);
255062306a36Sopenharmony_ci	/* device would return error if it thinks PNO is already stopped.
255162306a36Sopenharmony_ci	 * ignore the return code so user space and driver gets back in-sync
255262306a36Sopenharmony_ci	 */
255362306a36Sopenharmony_ci	wil_dbg_misc(wil, "sched scan stopped (%d)\n", rc);
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	return 0;
255662306a36Sopenharmony_ci}
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_cistatic int
255962306a36Sopenharmony_ciwil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
256062306a36Sopenharmony_ci			   struct cfg80211_update_ft_ies_params *ftie)
256162306a36Sopenharmony_ci{
256262306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
256362306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(dev);
256462306a36Sopenharmony_ci	struct cfg80211_bss *bss;
256562306a36Sopenharmony_ci	struct wmi_ft_reassoc_cmd reassoc;
256662306a36Sopenharmony_ci	int rc = 0;
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	wil_dbg_misc(wil, "update ft ies, mid=%d\n", vif->mid);
256962306a36Sopenharmony_ci	wil_hex_dump_misc("FT IE ", DUMP_PREFIX_OFFSET, 16, 1,
257062306a36Sopenharmony_ci			  ftie->ie, ftie->ie_len, true);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
257362306a36Sopenharmony_ci		wil_err(wil, "FW does not support FT roaming\n");
257462306a36Sopenharmony_ci		return -EOPNOTSUPP;
257562306a36Sopenharmony_ci	}
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	rc = wmi_update_ft_ies(vif, ftie->ie_len, ftie->ie);
257862306a36Sopenharmony_ci	if (rc)
257962306a36Sopenharmony_ci		return rc;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	if (!test_bit(wil_vif_ft_roam, vif->status))
258262306a36Sopenharmony_ci		/* vif is not roaming */
258362306a36Sopenharmony_ci		return 0;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	/* wil_vif_ft_roam is set. wil_cfg80211_update_ft_ies is used as
258662306a36Sopenharmony_ci	 * a trigger for reassoc
258762306a36Sopenharmony_ci	 */
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	bss = vif->bss;
259062306a36Sopenharmony_ci	if (!bss) {
259162306a36Sopenharmony_ci		wil_err(wil, "FT: bss is NULL\n");
259262306a36Sopenharmony_ci		return -EINVAL;
259362306a36Sopenharmony_ci	}
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_ci	memset(&reassoc, 0, sizeof(reassoc));
259662306a36Sopenharmony_ci	ether_addr_copy(reassoc.bssid, bss->bssid);
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	rc = wmi_send(wil, WMI_FT_REASSOC_CMDID, vif->mid,
259962306a36Sopenharmony_ci		      &reassoc, sizeof(reassoc));
260062306a36Sopenharmony_ci	if (rc)
260162306a36Sopenharmony_ci		wil_err(wil, "FT: reassoc failed (%d)\n", rc);
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_ci	return rc;
260462306a36Sopenharmony_ci}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_cistatic int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy,
260762306a36Sopenharmony_ci						 struct net_device *dev,
260862306a36Sopenharmony_ci						 const bool enabled)
260962306a36Sopenharmony_ci{
261062306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	if (wil->multicast_to_unicast == enabled)
261362306a36Sopenharmony_ci		return 0;
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	wil_info(wil, "set multicast to unicast, enabled=%d\n", enabled);
261662306a36Sopenharmony_ci	wil->multicast_to_unicast = enabled;
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	return 0;
261962306a36Sopenharmony_ci}
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_cistatic int wil_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
262262306a36Sopenharmony_ci					    struct net_device *dev,
262362306a36Sopenharmony_ci					    s32 rssi_thold, u32 rssi_hyst)
262462306a36Sopenharmony_ci{
262562306a36Sopenharmony_ci	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
262662306a36Sopenharmony_ci	int rc;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	wil->cqm_rssi_thold = rssi_thold;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	rc = wmi_set_cqm_rssi_config(wil, rssi_thold, rssi_hyst);
263162306a36Sopenharmony_ci	if (rc)
263262306a36Sopenharmony_ci		/* reset stored value upon failure */
263362306a36Sopenharmony_ci		wil->cqm_rssi_thold = 0;
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_ci	return rc;
263662306a36Sopenharmony_ci}
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_cistatic const struct cfg80211_ops wil_cfg80211_ops = {
263962306a36Sopenharmony_ci	.add_virtual_intf = wil_cfg80211_add_iface,
264062306a36Sopenharmony_ci	.del_virtual_intf = wil_cfg80211_del_iface,
264162306a36Sopenharmony_ci	.scan = wil_cfg80211_scan,
264262306a36Sopenharmony_ci	.abort_scan = wil_cfg80211_abort_scan,
264362306a36Sopenharmony_ci	.connect = wil_cfg80211_connect,
264462306a36Sopenharmony_ci	.disconnect = wil_cfg80211_disconnect,
264562306a36Sopenharmony_ci	.set_wiphy_params = wil_cfg80211_set_wiphy_params,
264662306a36Sopenharmony_ci	.change_virtual_intf = wil_cfg80211_change_iface,
264762306a36Sopenharmony_ci	.get_station = wil_cfg80211_get_station,
264862306a36Sopenharmony_ci	.dump_station = wil_cfg80211_dump_station,
264962306a36Sopenharmony_ci	.remain_on_channel = wil_remain_on_channel,
265062306a36Sopenharmony_ci	.cancel_remain_on_channel = wil_cancel_remain_on_channel,
265162306a36Sopenharmony_ci	.mgmt_tx = wil_cfg80211_mgmt_tx,
265262306a36Sopenharmony_ci	.set_monitor_channel = wil_cfg80211_set_channel,
265362306a36Sopenharmony_ci	.add_key = wil_cfg80211_add_key,
265462306a36Sopenharmony_ci	.del_key = wil_cfg80211_del_key,
265562306a36Sopenharmony_ci	.set_default_key = wil_cfg80211_set_default_key,
265662306a36Sopenharmony_ci	/* AP mode */
265762306a36Sopenharmony_ci	.change_beacon = wil_cfg80211_change_beacon,
265862306a36Sopenharmony_ci	.start_ap = wil_cfg80211_start_ap,
265962306a36Sopenharmony_ci	.stop_ap = wil_cfg80211_stop_ap,
266062306a36Sopenharmony_ci	.add_station = wil_cfg80211_add_station,
266162306a36Sopenharmony_ci	.del_station = wil_cfg80211_del_station,
266262306a36Sopenharmony_ci	.change_station = wil_cfg80211_change_station,
266362306a36Sopenharmony_ci	.probe_client = wil_cfg80211_probe_client,
266462306a36Sopenharmony_ci	.change_bss = wil_cfg80211_change_bss,
266562306a36Sopenharmony_ci	/* P2P device */
266662306a36Sopenharmony_ci	.start_p2p_device = wil_cfg80211_start_p2p_device,
266762306a36Sopenharmony_ci	.stop_p2p_device = wil_cfg80211_stop_p2p_device,
266862306a36Sopenharmony_ci	.set_power_mgmt = wil_cfg80211_set_power_mgmt,
266962306a36Sopenharmony_ci	.set_cqm_rssi_config = wil_cfg80211_set_cqm_rssi_config,
267062306a36Sopenharmony_ci	.suspend = wil_cfg80211_suspend,
267162306a36Sopenharmony_ci	.resume = wil_cfg80211_resume,
267262306a36Sopenharmony_ci	.sched_scan_start = wil_cfg80211_sched_scan_start,
267362306a36Sopenharmony_ci	.sched_scan_stop = wil_cfg80211_sched_scan_stop,
267462306a36Sopenharmony_ci	.update_ft_ies = wil_cfg80211_update_ft_ies,
267562306a36Sopenharmony_ci	.set_multicast_to_unicast = wil_cfg80211_set_multicast_to_unicast,
267662306a36Sopenharmony_ci};
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_cistatic void wil_wiphy_init(struct wiphy *wiphy)
267962306a36Sopenharmony_ci{
268062306a36Sopenharmony_ci	wiphy->max_scan_ssids = 1;
268162306a36Sopenharmony_ci	wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
268262306a36Sopenharmony_ci	wiphy->max_remain_on_channel_duration = WIL_MAX_ROC_DURATION_MS;
268362306a36Sopenharmony_ci	wiphy->max_num_pmkids = 0 /* TODO: */;
268462306a36Sopenharmony_ci	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
268562306a36Sopenharmony_ci				 BIT(NL80211_IFTYPE_AP) |
268662306a36Sopenharmony_ci				 BIT(NL80211_IFTYPE_P2P_CLIENT) |
268762306a36Sopenharmony_ci				 BIT(NL80211_IFTYPE_P2P_GO) |
268862306a36Sopenharmony_ci				 BIT(NL80211_IFTYPE_P2P_DEVICE) |
268962306a36Sopenharmony_ci				 BIT(NL80211_IFTYPE_MONITOR);
269062306a36Sopenharmony_ci	wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
269162306a36Sopenharmony_ci			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
269262306a36Sopenharmony_ci			WIPHY_FLAG_PS_ON_BY_DEFAULT;
269362306a36Sopenharmony_ci	if (!disable_ap_sme)
269462306a36Sopenharmony_ci		wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
269562306a36Sopenharmony_ci	dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
269662306a36Sopenharmony_ci		__func__, wiphy->flags);
269762306a36Sopenharmony_ci	wiphy->probe_resp_offload =
269862306a36Sopenharmony_ci		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
269962306a36Sopenharmony_ci		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
270062306a36Sopenharmony_ci		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	wiphy->bands[NL80211_BAND_60GHZ] = &wil_band_60ghz;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	/* may change after reading FW capabilities */
270562306a36Sopenharmony_ci	wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	wiphy->cipher_suites = wil_cipher_suites;
270862306a36Sopenharmony_ci	wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
270962306a36Sopenharmony_ci	wiphy->mgmt_stypes = wil_mgmt_stypes;
271062306a36Sopenharmony_ci	wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
271362306a36Sopenharmony_ci	wiphy->vendor_commands = wil_nl80211_vendor_commands;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci#ifdef CONFIG_PM
271662306a36Sopenharmony_ci	wiphy->wowlan = &wil_wowlan_support;
271762306a36Sopenharmony_ci#endif
271862306a36Sopenharmony_ci}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ciint wil_cfg80211_iface_combinations_from_fw(
272162306a36Sopenharmony_ci	struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc)
272262306a36Sopenharmony_ci{
272362306a36Sopenharmony_ci	struct wiphy *wiphy = wil_to_wiphy(wil);
272462306a36Sopenharmony_ci	u32 total_limits = 0;
272562306a36Sopenharmony_ci	u16 n_combos;
272662306a36Sopenharmony_ci	const struct wil_fw_concurrency_combo *combo;
272762306a36Sopenharmony_ci	const struct wil_fw_concurrency_limit *limit;
272862306a36Sopenharmony_ci	struct ieee80211_iface_combination *iface_combinations;
272962306a36Sopenharmony_ci	struct ieee80211_iface_limit *iface_limit;
273062306a36Sopenharmony_ci	int i, j;
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	if (wiphy->iface_combinations) {
273362306a36Sopenharmony_ci		wil_dbg_misc(wil, "iface_combinations already set, skipping\n");
273462306a36Sopenharmony_ci		return 0;
273562306a36Sopenharmony_ci	}
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	combo = conc->combos;
273862306a36Sopenharmony_ci	n_combos = le16_to_cpu(conc->n_combos);
273962306a36Sopenharmony_ci	for (i = 0; i < n_combos; i++) {
274062306a36Sopenharmony_ci		total_limits += combo->n_limits;
274162306a36Sopenharmony_ci		limit = combo->limits + combo->n_limits;
274262306a36Sopenharmony_ci		combo = (struct wil_fw_concurrency_combo *)limit;
274362306a36Sopenharmony_ci	}
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	iface_combinations =
274662306a36Sopenharmony_ci		kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) +
274762306a36Sopenharmony_ci			total_limits * sizeof(struct ieee80211_iface_limit),
274862306a36Sopenharmony_ci			GFP_KERNEL);
274962306a36Sopenharmony_ci	if (!iface_combinations)
275062306a36Sopenharmony_ci		return -ENOMEM;
275162306a36Sopenharmony_ci	iface_limit = (struct ieee80211_iface_limit *)(iface_combinations +
275262306a36Sopenharmony_ci						       n_combos);
275362306a36Sopenharmony_ci	combo = conc->combos;
275462306a36Sopenharmony_ci	for (i = 0; i < n_combos; i++) {
275562306a36Sopenharmony_ci		iface_combinations[i].max_interfaces = combo->max_interfaces;
275662306a36Sopenharmony_ci		iface_combinations[i].num_different_channels =
275762306a36Sopenharmony_ci			combo->n_diff_channels;
275862306a36Sopenharmony_ci		iface_combinations[i].beacon_int_infra_match =
275962306a36Sopenharmony_ci			combo->same_bi;
276062306a36Sopenharmony_ci		iface_combinations[i].n_limits = combo->n_limits;
276162306a36Sopenharmony_ci		wil_dbg_misc(wil,
276262306a36Sopenharmony_ci			     "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n",
276362306a36Sopenharmony_ci			     i, iface_combinations[i].max_interfaces,
276462306a36Sopenharmony_ci			     iface_combinations[i].num_different_channels,
276562306a36Sopenharmony_ci			     iface_combinations[i].beacon_int_infra_match);
276662306a36Sopenharmony_ci		limit = combo->limits;
276762306a36Sopenharmony_ci		for (j = 0; j < combo->n_limits; j++) {
276862306a36Sopenharmony_ci			iface_limit[j].max = le16_to_cpu(limit[j].max);
276962306a36Sopenharmony_ci			iface_limit[j].types = le16_to_cpu(limit[j].types);
277062306a36Sopenharmony_ci			wil_dbg_misc(wil,
277162306a36Sopenharmony_ci				     "limit %d: max %d types 0x%x\n", j,
277262306a36Sopenharmony_ci				     iface_limit[j].max, iface_limit[j].types);
277362306a36Sopenharmony_ci		}
277462306a36Sopenharmony_ci		iface_combinations[i].limits = iface_limit;
277562306a36Sopenharmony_ci		iface_limit += combo->n_limits;
277662306a36Sopenharmony_ci		limit += combo->n_limits;
277762306a36Sopenharmony_ci		combo = (struct wil_fw_concurrency_combo *)limit;
277862306a36Sopenharmony_ci	}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids);
278162306a36Sopenharmony_ci	wil->max_vifs = conc->n_mids + 1; /* including main interface */
278262306a36Sopenharmony_ci	if (wil->max_vifs > WIL_MAX_VIFS) {
278362306a36Sopenharmony_ci		wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n",
278462306a36Sopenharmony_ci			 WIL_MAX_VIFS, wil->max_vifs);
278562306a36Sopenharmony_ci		wil->max_vifs = WIL_MAX_VIFS;
278662306a36Sopenharmony_ci	}
278762306a36Sopenharmony_ci	wiphy->n_iface_combinations = n_combos;
278862306a36Sopenharmony_ci	wiphy->iface_combinations = iface_combinations;
278962306a36Sopenharmony_ci	return 0;
279062306a36Sopenharmony_ci}
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_cistruct wil6210_priv *wil_cfg80211_init(struct device *dev)
279362306a36Sopenharmony_ci{
279462306a36Sopenharmony_ci	struct wiphy *wiphy;
279562306a36Sopenharmony_ci	struct wil6210_priv *wil;
279662306a36Sopenharmony_ci	struct ieee80211_channel *ch;
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	dev_dbg(dev, "%s()\n", __func__);
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_ci	/* Note: the wireless_dev structure is no longer allocated here.
280162306a36Sopenharmony_ci	 * Instead, it is allocated as part of the net_device structure
280262306a36Sopenharmony_ci	 * for main interface and each VIF.
280362306a36Sopenharmony_ci	 */
280462306a36Sopenharmony_ci	wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv));
280562306a36Sopenharmony_ci	if (!wiphy)
280662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	set_wiphy_dev(wiphy, dev);
280962306a36Sopenharmony_ci	wil_wiphy_init(wiphy);
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	wil = wiphy_to_wil(wiphy);
281262306a36Sopenharmony_ci	wil->wiphy = wiphy;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	/* default monitor channel */
281562306a36Sopenharmony_ci	ch = wiphy->bands[NL80211_BAND_60GHZ]->channels;
281662306a36Sopenharmony_ci	cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT);
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	return wil;
281962306a36Sopenharmony_ci}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_civoid wil_cfg80211_deinit(struct wil6210_priv *wil)
282262306a36Sopenharmony_ci{
282362306a36Sopenharmony_ci	struct wiphy *wiphy = wil_to_wiphy(wil);
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	if (!wiphy)
282862306a36Sopenharmony_ci		return;
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	kfree(wiphy->iface_combinations);
283162306a36Sopenharmony_ci	wiphy->iface_combinations = NULL;
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	wiphy_free(wiphy);
283462306a36Sopenharmony_ci	/* do not access wil6210_priv after returning from here */
283562306a36Sopenharmony_ci}
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_civoid wil_p2p_wdev_free(struct wil6210_priv *wil)
283862306a36Sopenharmony_ci{
283962306a36Sopenharmony_ci	struct wireless_dev *p2p_wdev;
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	mutex_lock(&wil->vif_mutex);
284262306a36Sopenharmony_ci	p2p_wdev = wil->p2p_wdev;
284362306a36Sopenharmony_ci	wil->p2p_wdev = NULL;
284462306a36Sopenharmony_ci	wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
284562306a36Sopenharmony_ci	mutex_unlock(&wil->vif_mutex);
284662306a36Sopenharmony_ci	if (p2p_wdev) {
284762306a36Sopenharmony_ci		cfg80211_unregister_wdev(p2p_wdev);
284862306a36Sopenharmony_ci		kfree(p2p_wdev);
284962306a36Sopenharmony_ci	}
285062306a36Sopenharmony_ci}
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_cistatic int wil_rf_sector_status_to_rc(u8 status)
285362306a36Sopenharmony_ci{
285462306a36Sopenharmony_ci	switch (status) {
285562306a36Sopenharmony_ci	case WMI_RF_SECTOR_STATUS_SUCCESS:
285662306a36Sopenharmony_ci		return 0;
285762306a36Sopenharmony_ci	case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR:
285862306a36Sopenharmony_ci		return -EINVAL;
285962306a36Sopenharmony_ci	case WMI_RF_SECTOR_STATUS_BUSY_ERROR:
286062306a36Sopenharmony_ci		return -EAGAIN;
286162306a36Sopenharmony_ci	case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR:
286262306a36Sopenharmony_ci		return -EOPNOTSUPP;
286362306a36Sopenharmony_ci	default:
286462306a36Sopenharmony_ci		return -EINVAL;
286562306a36Sopenharmony_ci	}
286662306a36Sopenharmony_ci}
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_cistatic int wil_rf_sector_get_cfg(struct wiphy *wiphy,
286962306a36Sopenharmony_ci				 struct wireless_dev *wdev,
287062306a36Sopenharmony_ci				 const void *data, int data_len)
287162306a36Sopenharmony_ci{
287262306a36Sopenharmony_ci	struct wil6210_priv *wil = wdev_to_wil(wdev);
287362306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
287462306a36Sopenharmony_ci	int rc;
287562306a36Sopenharmony_ci	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
287662306a36Sopenharmony_ci	u16 sector_index;
287762306a36Sopenharmony_ci	u8 sector_type;
287862306a36Sopenharmony_ci	u32 rf_modules_vec;
287962306a36Sopenharmony_ci	struct wmi_get_rf_sector_params_cmd cmd;
288062306a36Sopenharmony_ci	struct {
288162306a36Sopenharmony_ci		struct wmi_cmd_hdr wmi;
288262306a36Sopenharmony_ci		struct wmi_get_rf_sector_params_done_event evt;
288362306a36Sopenharmony_ci	} __packed reply = {
288462306a36Sopenharmony_ci		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
288562306a36Sopenharmony_ci	};
288662306a36Sopenharmony_ci	struct sk_buff *msg;
288762306a36Sopenharmony_ci	struct nlattr *nl_cfgs, *nl_cfg;
288862306a36Sopenharmony_ci	u32 i;
288962306a36Sopenharmony_ci	struct wmi_rf_sector_info *si;
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
289262306a36Sopenharmony_ci		return -EOPNOTSUPP;
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
289562306a36Sopenharmony_ci				  data_len, wil_rf_sector_policy, NULL);
289662306a36Sopenharmony_ci	if (rc) {
289762306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector ATTR\n");
289862306a36Sopenharmony_ci		return rc;
289962306a36Sopenharmony_ci	}
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
290262306a36Sopenharmony_ci	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
290362306a36Sopenharmony_ci	    !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) {
290462306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector spec\n");
290562306a36Sopenharmony_ci		return -EINVAL;
290662306a36Sopenharmony_ci	}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	sector_index = nla_get_u16(
290962306a36Sopenharmony_ci		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
291062306a36Sopenharmony_ci	if (sector_index >= WIL_MAX_RF_SECTORS) {
291162306a36Sopenharmony_ci		wil_err(wil, "Invalid sector index %d\n", sector_index);
291262306a36Sopenharmony_ci		return -EINVAL;
291362306a36Sopenharmony_ci	}
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
291662306a36Sopenharmony_ci	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
291762306a36Sopenharmony_ci		wil_err(wil, "Invalid sector type %d\n", sector_type);
291862306a36Sopenharmony_ci		return -EINVAL;
291962306a36Sopenharmony_ci	}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	rf_modules_vec = nla_get_u32(
292262306a36Sopenharmony_ci		tb[QCA_ATTR_DMG_RF_MODULE_MASK]);
292362306a36Sopenharmony_ci	if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) {
292462306a36Sopenharmony_ci		wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec);
292562306a36Sopenharmony_ci		return -EINVAL;
292662306a36Sopenharmony_ci	}
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	cmd.sector_idx = cpu_to_le16(sector_index);
292962306a36Sopenharmony_ci	cmd.sector_type = sector_type;
293062306a36Sopenharmony_ci	cmd.rf_modules_vec = rf_modules_vec & 0xFF;
293162306a36Sopenharmony_ci	rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid,
293262306a36Sopenharmony_ci		      &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
293362306a36Sopenharmony_ci		      &reply, sizeof(reply),
293462306a36Sopenharmony_ci		      500);
293562306a36Sopenharmony_ci	if (rc)
293662306a36Sopenharmony_ci		return rc;
293762306a36Sopenharmony_ci	if (reply.evt.status) {
293862306a36Sopenharmony_ci		wil_err(wil, "get rf sector cfg failed with status %d\n",
293962306a36Sopenharmony_ci			reply.evt.status);
294062306a36Sopenharmony_ci		return wil_rf_sector_status_to_rc(reply.evt.status);
294162306a36Sopenharmony_ci	}
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci	msg = cfg80211_vendor_cmd_alloc_reply_skb(
294462306a36Sopenharmony_ci		wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
294562306a36Sopenharmony_ci	if (!msg)
294662306a36Sopenharmony_ci		return -ENOMEM;
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
294962306a36Sopenharmony_ci			      le64_to_cpu(reply.evt.tsf),
295062306a36Sopenharmony_ci			      QCA_ATTR_PAD))
295162306a36Sopenharmony_ci		goto nla_put_failure;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	nl_cfgs = nla_nest_start_noflag(msg, QCA_ATTR_DMG_RF_SECTOR_CFG);
295462306a36Sopenharmony_ci	if (!nl_cfgs)
295562306a36Sopenharmony_ci		goto nla_put_failure;
295662306a36Sopenharmony_ci	for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) {
295762306a36Sopenharmony_ci		if (!(rf_modules_vec & BIT(i)))
295862306a36Sopenharmony_ci			continue;
295962306a36Sopenharmony_ci		nl_cfg = nla_nest_start_noflag(msg, i);
296062306a36Sopenharmony_ci		if (!nl_cfg)
296162306a36Sopenharmony_ci			goto nla_put_failure;
296262306a36Sopenharmony_ci		si = &reply.evt.sectors_info[i];
296362306a36Sopenharmony_ci		if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
296462306a36Sopenharmony_ci			       i) ||
296562306a36Sopenharmony_ci		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
296662306a36Sopenharmony_ci				le32_to_cpu(si->etype0)) ||
296762306a36Sopenharmony_ci		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
296862306a36Sopenharmony_ci				le32_to_cpu(si->etype1)) ||
296962306a36Sopenharmony_ci		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
297062306a36Sopenharmony_ci				le32_to_cpu(si->etype2)) ||
297162306a36Sopenharmony_ci		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
297262306a36Sopenharmony_ci				le32_to_cpu(si->psh_hi)) ||
297362306a36Sopenharmony_ci		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
297462306a36Sopenharmony_ci				le32_to_cpu(si->psh_lo)) ||
297562306a36Sopenharmony_ci		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
297662306a36Sopenharmony_ci				le32_to_cpu(si->dtype_swch_off)))
297762306a36Sopenharmony_ci			goto nla_put_failure;
297862306a36Sopenharmony_ci		nla_nest_end(msg, nl_cfg);
297962306a36Sopenharmony_ci	}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	nla_nest_end(msg, nl_cfgs);
298262306a36Sopenharmony_ci	rc = cfg80211_vendor_cmd_reply(msg);
298362306a36Sopenharmony_ci	return rc;
298462306a36Sopenharmony_cinla_put_failure:
298562306a36Sopenharmony_ci	kfree_skb(msg);
298662306a36Sopenharmony_ci	return -ENOBUFS;
298762306a36Sopenharmony_ci}
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_cistatic int wil_rf_sector_set_cfg(struct wiphy *wiphy,
299062306a36Sopenharmony_ci				 struct wireless_dev *wdev,
299162306a36Sopenharmony_ci				 const void *data, int data_len)
299262306a36Sopenharmony_ci{
299362306a36Sopenharmony_ci	struct wil6210_priv *wil = wdev_to_wil(wdev);
299462306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
299562306a36Sopenharmony_ci	int rc, tmp;
299662306a36Sopenharmony_ci	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
299762306a36Sopenharmony_ci	struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
299862306a36Sopenharmony_ci	u16 sector_index, rf_module_index;
299962306a36Sopenharmony_ci	u8 sector_type;
300062306a36Sopenharmony_ci	u32 rf_modules_vec = 0;
300162306a36Sopenharmony_ci	struct wmi_set_rf_sector_params_cmd cmd;
300262306a36Sopenharmony_ci	struct {
300362306a36Sopenharmony_ci		struct wmi_cmd_hdr wmi;
300462306a36Sopenharmony_ci		struct wmi_set_rf_sector_params_done_event evt;
300562306a36Sopenharmony_ci	} __packed reply = {
300662306a36Sopenharmony_ci		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
300762306a36Sopenharmony_ci	};
300862306a36Sopenharmony_ci	struct nlattr *nl_cfg;
300962306a36Sopenharmony_ci	struct wmi_rf_sector_info *si;
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
301262306a36Sopenharmony_ci		return -EOPNOTSUPP;
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
301562306a36Sopenharmony_ci				  data_len, wil_rf_sector_policy, NULL);
301662306a36Sopenharmony_ci	if (rc) {
301762306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector ATTR\n");
301862306a36Sopenharmony_ci		return rc;
301962306a36Sopenharmony_ci	}
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_ci	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
302262306a36Sopenharmony_ci	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
302362306a36Sopenharmony_ci	    !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) {
302462306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector spec\n");
302562306a36Sopenharmony_ci		return -EINVAL;
302662306a36Sopenharmony_ci	}
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	sector_index = nla_get_u16(
302962306a36Sopenharmony_ci		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
303062306a36Sopenharmony_ci	if (sector_index >= WIL_MAX_RF_SECTORS) {
303162306a36Sopenharmony_ci		wil_err(wil, "Invalid sector index %d\n", sector_index);
303262306a36Sopenharmony_ci		return -EINVAL;
303362306a36Sopenharmony_ci	}
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
303662306a36Sopenharmony_ci	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
303762306a36Sopenharmony_ci		wil_err(wil, "Invalid sector type %d\n", sector_type);
303862306a36Sopenharmony_ci		return -EINVAL;
303962306a36Sopenharmony_ci	}
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci	cmd.sector_idx = cpu_to_le16(sector_index);
304462306a36Sopenharmony_ci	cmd.sector_type = sector_type;
304562306a36Sopenharmony_ci	nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG],
304662306a36Sopenharmony_ci			    tmp) {
304762306a36Sopenharmony_ci		rc = nla_parse_nested_deprecated(tb2,
304862306a36Sopenharmony_ci						 QCA_ATTR_DMG_RF_SECTOR_CFG_MAX,
304962306a36Sopenharmony_ci						 nl_cfg,
305062306a36Sopenharmony_ci						 wil_rf_sector_cfg_policy,
305162306a36Sopenharmony_ci						 NULL);
305262306a36Sopenharmony_ci		if (rc) {
305362306a36Sopenharmony_ci			wil_err(wil, "invalid sector cfg\n");
305462306a36Sopenharmony_ci			return -EINVAL;
305562306a36Sopenharmony_ci		}
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci		if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] ||
305862306a36Sopenharmony_ci		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] ||
305962306a36Sopenharmony_ci		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] ||
306062306a36Sopenharmony_ci		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] ||
306162306a36Sopenharmony_ci		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] ||
306262306a36Sopenharmony_ci		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] ||
306362306a36Sopenharmony_ci		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) {
306462306a36Sopenharmony_ci			wil_err(wil, "missing cfg params\n");
306562306a36Sopenharmony_ci			return -EINVAL;
306662306a36Sopenharmony_ci		}
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci		rf_module_index = nla_get_u8(
306962306a36Sopenharmony_ci			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]);
307062306a36Sopenharmony_ci		if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) {
307162306a36Sopenharmony_ci			wil_err(wil, "invalid RF module index %d\n",
307262306a36Sopenharmony_ci				rf_module_index);
307362306a36Sopenharmony_ci			return -EINVAL;
307462306a36Sopenharmony_ci		}
307562306a36Sopenharmony_ci		rf_modules_vec |= BIT(rf_module_index);
307662306a36Sopenharmony_ci		si = &cmd.sectors_info[rf_module_index];
307762306a36Sopenharmony_ci		si->etype0 = cpu_to_le32(nla_get_u32(
307862306a36Sopenharmony_ci			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0]));
307962306a36Sopenharmony_ci		si->etype1 = cpu_to_le32(nla_get_u32(
308062306a36Sopenharmony_ci			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1]));
308162306a36Sopenharmony_ci		si->etype2 = cpu_to_le32(nla_get_u32(
308262306a36Sopenharmony_ci			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2]));
308362306a36Sopenharmony_ci		si->psh_hi = cpu_to_le32(nla_get_u32(
308462306a36Sopenharmony_ci			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI]));
308562306a36Sopenharmony_ci		si->psh_lo = cpu_to_le32(nla_get_u32(
308662306a36Sopenharmony_ci			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO]));
308762306a36Sopenharmony_ci		si->dtype_swch_off = cpu_to_le32(nla_get_u32(
308862306a36Sopenharmony_ci			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]));
308962306a36Sopenharmony_ci	}
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	cmd.rf_modules_vec = rf_modules_vec & 0xFF;
309262306a36Sopenharmony_ci	rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid,
309362306a36Sopenharmony_ci		      &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
309462306a36Sopenharmony_ci		      &reply, sizeof(reply),
309562306a36Sopenharmony_ci		      500);
309662306a36Sopenharmony_ci	if (rc)
309762306a36Sopenharmony_ci		return rc;
309862306a36Sopenharmony_ci	return wil_rf_sector_status_to_rc(reply.evt.status);
309962306a36Sopenharmony_ci}
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_cistatic int wil_rf_sector_get_selected(struct wiphy *wiphy,
310262306a36Sopenharmony_ci				      struct wireless_dev *wdev,
310362306a36Sopenharmony_ci				      const void *data, int data_len)
310462306a36Sopenharmony_ci{
310562306a36Sopenharmony_ci	struct wil6210_priv *wil = wdev_to_wil(wdev);
310662306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
310762306a36Sopenharmony_ci	int rc;
310862306a36Sopenharmony_ci	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
310962306a36Sopenharmony_ci	u8 sector_type, mac_addr[ETH_ALEN];
311062306a36Sopenharmony_ci	int cid = 0;
311162306a36Sopenharmony_ci	struct wmi_get_selected_rf_sector_index_cmd cmd;
311262306a36Sopenharmony_ci	struct {
311362306a36Sopenharmony_ci		struct wmi_cmd_hdr wmi;
311462306a36Sopenharmony_ci		struct wmi_get_selected_rf_sector_index_done_event evt;
311562306a36Sopenharmony_ci	} __packed reply = {
311662306a36Sopenharmony_ci		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
311762306a36Sopenharmony_ci	};
311862306a36Sopenharmony_ci	struct sk_buff *msg;
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
312162306a36Sopenharmony_ci		return -EOPNOTSUPP;
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
312462306a36Sopenharmony_ci				  data_len, wil_rf_sector_policy, NULL);
312562306a36Sopenharmony_ci	if (rc) {
312662306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector ATTR\n");
312762306a36Sopenharmony_ci		return rc;
312862306a36Sopenharmony_ci	}
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
313162306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector spec\n");
313262306a36Sopenharmony_ci		return -EINVAL;
313362306a36Sopenharmony_ci	}
313462306a36Sopenharmony_ci	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
313562306a36Sopenharmony_ci	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
313662306a36Sopenharmony_ci		wil_err(wil, "Invalid sector type %d\n", sector_type);
313762306a36Sopenharmony_ci		return -EINVAL;
313862306a36Sopenharmony_ci	}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	if (tb[QCA_ATTR_MAC_ADDR]) {
314162306a36Sopenharmony_ci		ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
314262306a36Sopenharmony_ci		cid = wil_find_cid(wil, vif->mid, mac_addr);
314362306a36Sopenharmony_ci		if (cid < 0) {
314462306a36Sopenharmony_ci			wil_err(wil, "invalid MAC address %pM\n", mac_addr);
314562306a36Sopenharmony_ci			return -ENOENT;
314662306a36Sopenharmony_ci		}
314762306a36Sopenharmony_ci	} else {
314862306a36Sopenharmony_ci		if (test_bit(wil_vif_fwconnected, vif->status)) {
314962306a36Sopenharmony_ci			wil_err(wil, "must specify MAC address when connected\n");
315062306a36Sopenharmony_ci			return -EINVAL;
315162306a36Sopenharmony_ci		}
315262306a36Sopenharmony_ci	}
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
315562306a36Sopenharmony_ci	cmd.cid = (u8)cid;
315662306a36Sopenharmony_ci	cmd.sector_type = sector_type;
315762306a36Sopenharmony_ci	rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid,
315862306a36Sopenharmony_ci		      &cmd, sizeof(cmd),
315962306a36Sopenharmony_ci		      WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
316062306a36Sopenharmony_ci		      &reply, sizeof(reply),
316162306a36Sopenharmony_ci		      500);
316262306a36Sopenharmony_ci	if (rc)
316362306a36Sopenharmony_ci		return rc;
316462306a36Sopenharmony_ci	if (reply.evt.status) {
316562306a36Sopenharmony_ci		wil_err(wil, "get rf selected sector cfg failed with status %d\n",
316662306a36Sopenharmony_ci			reply.evt.status);
316762306a36Sopenharmony_ci		return wil_rf_sector_status_to_rc(reply.evt.status);
316862306a36Sopenharmony_ci	}
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	msg = cfg80211_vendor_cmd_alloc_reply_skb(
317162306a36Sopenharmony_ci		wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
317262306a36Sopenharmony_ci	if (!msg)
317362306a36Sopenharmony_ci		return -ENOMEM;
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
317662306a36Sopenharmony_ci			      le64_to_cpu(reply.evt.tsf),
317762306a36Sopenharmony_ci			      QCA_ATTR_PAD) ||
317862306a36Sopenharmony_ci	    nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX,
317962306a36Sopenharmony_ci			le16_to_cpu(reply.evt.sector_idx)))
318062306a36Sopenharmony_ci		goto nla_put_failure;
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci	rc = cfg80211_vendor_cmd_reply(msg);
318362306a36Sopenharmony_ci	return rc;
318462306a36Sopenharmony_cinla_put_failure:
318562306a36Sopenharmony_ci	kfree_skb(msg);
318662306a36Sopenharmony_ci	return -ENOBUFS;
318762306a36Sopenharmony_ci}
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_cistatic int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
319062306a36Sopenharmony_ci					  u8 mid, u16 sector_index,
319162306a36Sopenharmony_ci					  u8 sector_type, u8 cid)
319262306a36Sopenharmony_ci{
319362306a36Sopenharmony_ci	struct wmi_set_selected_rf_sector_index_cmd cmd;
319462306a36Sopenharmony_ci	struct {
319562306a36Sopenharmony_ci		struct wmi_cmd_hdr wmi;
319662306a36Sopenharmony_ci		struct wmi_set_selected_rf_sector_index_done_event evt;
319762306a36Sopenharmony_ci	} __packed reply = {
319862306a36Sopenharmony_ci		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
319962306a36Sopenharmony_ci	};
320062306a36Sopenharmony_ci	int rc;
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
320362306a36Sopenharmony_ci	cmd.sector_idx = cpu_to_le16(sector_index);
320462306a36Sopenharmony_ci	cmd.sector_type = sector_type;
320562306a36Sopenharmony_ci	cmd.cid = (u8)cid;
320662306a36Sopenharmony_ci	rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid,
320762306a36Sopenharmony_ci		      &cmd, sizeof(cmd),
320862306a36Sopenharmony_ci		      WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
320962306a36Sopenharmony_ci		      &reply, sizeof(reply),
321062306a36Sopenharmony_ci		      500);
321162306a36Sopenharmony_ci	if (rc)
321262306a36Sopenharmony_ci		return rc;
321362306a36Sopenharmony_ci	return wil_rf_sector_status_to_rc(reply.evt.status);
321462306a36Sopenharmony_ci}
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_cistatic int wil_rf_sector_set_selected(struct wiphy *wiphy,
321762306a36Sopenharmony_ci				      struct wireless_dev *wdev,
321862306a36Sopenharmony_ci				      const void *data, int data_len)
321962306a36Sopenharmony_ci{
322062306a36Sopenharmony_ci	struct wil6210_priv *wil = wdev_to_wil(wdev);
322162306a36Sopenharmony_ci	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
322262306a36Sopenharmony_ci	int rc;
322362306a36Sopenharmony_ci	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
322462306a36Sopenharmony_ci	u16 sector_index;
322562306a36Sopenharmony_ci	u8 sector_type, mac_addr[ETH_ALEN], i;
322662306a36Sopenharmony_ci	int cid = 0;
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
322962306a36Sopenharmony_ci		return -EOPNOTSUPP;
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
323262306a36Sopenharmony_ci				  data_len, wil_rf_sector_policy, NULL);
323362306a36Sopenharmony_ci	if (rc) {
323462306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector ATTR\n");
323562306a36Sopenharmony_ci		return rc;
323662306a36Sopenharmony_ci	}
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
323962306a36Sopenharmony_ci	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
324062306a36Sopenharmony_ci		wil_err(wil, "Invalid rf sector spec\n");
324162306a36Sopenharmony_ci		return -EINVAL;
324262306a36Sopenharmony_ci	}
324362306a36Sopenharmony_ci
324462306a36Sopenharmony_ci	sector_index = nla_get_u16(
324562306a36Sopenharmony_ci		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
324662306a36Sopenharmony_ci	if (sector_index >= WIL_MAX_RF_SECTORS &&
324762306a36Sopenharmony_ci	    sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
324862306a36Sopenharmony_ci		wil_err(wil, "Invalid sector index %d\n", sector_index);
324962306a36Sopenharmony_ci		return -EINVAL;
325062306a36Sopenharmony_ci	}
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
325362306a36Sopenharmony_ci	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
325462306a36Sopenharmony_ci		wil_err(wil, "Invalid sector type %d\n", sector_type);
325562306a36Sopenharmony_ci		return -EINVAL;
325662306a36Sopenharmony_ci	}
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	if (tb[QCA_ATTR_MAC_ADDR]) {
325962306a36Sopenharmony_ci		ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
326062306a36Sopenharmony_ci		if (!is_broadcast_ether_addr(mac_addr)) {
326162306a36Sopenharmony_ci			cid = wil_find_cid(wil, vif->mid, mac_addr);
326262306a36Sopenharmony_ci			if (cid < 0) {
326362306a36Sopenharmony_ci				wil_err(wil, "invalid MAC address %pM\n",
326462306a36Sopenharmony_ci					mac_addr);
326562306a36Sopenharmony_ci				return -ENOENT;
326662306a36Sopenharmony_ci			}
326762306a36Sopenharmony_ci		} else {
326862306a36Sopenharmony_ci			if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
326962306a36Sopenharmony_ci				wil_err(wil, "broadcast MAC valid only with unlocking\n");
327062306a36Sopenharmony_ci				return -EINVAL;
327162306a36Sopenharmony_ci			}
327262306a36Sopenharmony_ci			cid = -1;
327362306a36Sopenharmony_ci		}
327462306a36Sopenharmony_ci	} else {
327562306a36Sopenharmony_ci		if (test_bit(wil_vif_fwconnected, vif->status)) {
327662306a36Sopenharmony_ci			wil_err(wil, "must specify MAC address when connected\n");
327762306a36Sopenharmony_ci			return -EINVAL;
327862306a36Sopenharmony_ci		}
327962306a36Sopenharmony_ci		/* otherwise, using cid=0 for unassociated station */
328062306a36Sopenharmony_ci	}
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci	if (cid >= 0) {
328362306a36Sopenharmony_ci		rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index,
328462306a36Sopenharmony_ci						    sector_type, cid);
328562306a36Sopenharmony_ci	} else {
328662306a36Sopenharmony_ci		/* unlock all cids */
328762306a36Sopenharmony_ci		rc = wil_rf_sector_wmi_set_selected(
328862306a36Sopenharmony_ci			wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX,
328962306a36Sopenharmony_ci			sector_type, WIL_CID_ALL);
329062306a36Sopenharmony_ci		if (rc == -EINVAL) {
329162306a36Sopenharmony_ci			for (i = 0; i < wil->max_assoc_sta; i++) {
329262306a36Sopenharmony_ci				if (wil->sta[i].mid != vif->mid)
329362306a36Sopenharmony_ci					continue;
329462306a36Sopenharmony_ci				rc = wil_rf_sector_wmi_set_selected(
329562306a36Sopenharmony_ci					wil, vif->mid,
329662306a36Sopenharmony_ci					WMI_INVALID_RF_SECTOR_INDEX,
329762306a36Sopenharmony_ci					sector_type, i);
329862306a36Sopenharmony_ci				/* the FW will silently ignore and return
329962306a36Sopenharmony_ci				 * success for unused cid, so abort the loop
330062306a36Sopenharmony_ci				 * on any other error
330162306a36Sopenharmony_ci				 */
330262306a36Sopenharmony_ci				if (rc) {
330362306a36Sopenharmony_ci					wil_err(wil, "unlock cid %d failed with status %d\n",
330462306a36Sopenharmony_ci						i, rc);
330562306a36Sopenharmony_ci					break;
330662306a36Sopenharmony_ci				}
330762306a36Sopenharmony_ci			}
330862306a36Sopenharmony_ci		}
330962306a36Sopenharmony_ci	}
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	return rc;
331262306a36Sopenharmony_ci}
3313