185630469SLior David // SPDX-License-Identifier: ISC
22be7d22fSVladimir Kondratiev /*
3849a564bSDedy Lansky  * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
4bf0353a6SAhmad Masri  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
52be7d22fSVladimir Kondratiev  */
62be7d22fSVladimir Kondratiev 
7a82553bbSVladimir Kondratiev #include <linux/etherdevice.h>
8949c2d00SJohannes Berg #include <linux/moduleparam.h>
90216a895SLior David #include <net/netlink.h>
104aebd3bdSLior David #include <net/cfg80211.h>
112be7d22fSVladimir Kondratiev #include "wil6210.h"
122be7d22fSVladimir Kondratiev #include "wmi.h"
137bfe9e22SLior David #include "fw.h"
142be7d22fSVladimir Kondratiev 
15e6d68341SDedy Lansky #define WIL_MAX_ROC_DURATION_MS 5000
16e6d68341SDedy Lansky 
179abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_9_SUBCHANNELS	(BIT(0) | BIT(1))
189abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_10_SUBCHANNELS	(BIT(1) | BIT(2))
199abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_11_SUBCHANNELS	(BIT(2) | BIT(3))
209abe3e30SAlexei Avshalom Lazar 
219abe3e30SAlexei Avshalom Lazar /* WIL_EDMG_BW_CONFIGURATION define the allowed channel bandwidth
229abe3e30SAlexei Avshalom Lazar  * configurations as defined by IEEE 802.11 section 9.4.2.251, Table 13.
239abe3e30SAlexei Avshalom Lazar  * The value 5 allowing CB1 and CB2 of adjacent channels.
249abe3e30SAlexei Avshalom Lazar  */
259abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_BW_CONFIGURATION 5
269abe3e30SAlexei Avshalom Lazar 
279abe3e30SAlexei Avshalom Lazar /* WIL_EDMG_CHANNELS is a bitmap that indicates the 2.16 GHz channel(s) that
289abe3e30SAlexei Avshalom Lazar  * are allowed to be used for EDMG transmissions in the BSS as defined by
299abe3e30SAlexei Avshalom Lazar  * IEEE 802.11 section 9.4.2.251.
309abe3e30SAlexei Avshalom Lazar  */
319abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNELS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
329abe3e30SAlexei Avshalom Lazar 
33849a564bSDedy Lansky bool disable_ap_sme;
3478484c44SMaya Erez module_param(disable_ap_sme, bool, 0444);
35849a564bSDedy Lansky MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
36849a564bSDedy Lansky 
37d1fbf075SMaya Erez #ifdef CONFIG_PM
38d1fbf075SMaya Erez static struct wiphy_wowlan_support wil_wowlan_support = {
39d1fbf075SMaya Erez 	.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
40d1fbf075SMaya Erez };
41d1fbf075SMaya Erez #endif
42d1fbf075SMaya Erez 
432be7d22fSVladimir Kondratiev #define CHAN60G(_channel, _flags) {				\
4457fbcce3SJohannes Berg 	.band			= NL80211_BAND_60GHZ,		\
452be7d22fSVladimir Kondratiev 	.center_freq		= 56160 + (2160 * (_channel)),	\
462be7d22fSVladimir Kondratiev 	.hw_value		= (_channel),			\
472be7d22fSVladimir Kondratiev 	.flags			= (_flags),			\
482be7d22fSVladimir Kondratiev 	.max_antenna_gain	= 0,				\
492be7d22fSVladimir Kondratiev 	.max_power		= 40,				\
502be7d22fSVladimir Kondratiev }
512be7d22fSVladimir Kondratiev 
522be7d22fSVladimir Kondratiev static struct ieee80211_channel wil_60ghz_channels[] = {
532be7d22fSVladimir Kondratiev 	CHAN60G(1, 0),
542be7d22fSVladimir Kondratiev 	CHAN60G(2, 0),
552be7d22fSVladimir Kondratiev 	CHAN60G(3, 0),
5622b9610eSAlexei Avshalom Lazar 	CHAN60G(4, 0),
572be7d22fSVladimir Kondratiev };
582be7d22fSVladimir Kondratiev 
599abe3e30SAlexei Avshalom Lazar /* Rx channel bonding mode */
609abe3e30SAlexei Avshalom Lazar enum wil_rx_cb_mode {
619abe3e30SAlexei Avshalom Lazar 	WIL_RX_CB_MODE_DMG,
629abe3e30SAlexei Avshalom Lazar 	WIL_RX_CB_MODE_EDMG,
639abe3e30SAlexei Avshalom Lazar 	WIL_RX_CB_MODE_WIDE,
649abe3e30SAlexei Avshalom Lazar };
659abe3e30SAlexei Avshalom Lazar 
wil_rx_cb_mode_to_n_bonded(u8 cb_mode)669abe3e30SAlexei Avshalom Lazar static int wil_rx_cb_mode_to_n_bonded(u8 cb_mode)
679abe3e30SAlexei Avshalom Lazar {
689abe3e30SAlexei Avshalom Lazar 	switch (cb_mode) {
699abe3e30SAlexei Avshalom Lazar 	case WIL_RX_CB_MODE_DMG:
709abe3e30SAlexei Avshalom Lazar 	case WIL_RX_CB_MODE_EDMG:
719abe3e30SAlexei Avshalom Lazar 		return 1;
729abe3e30SAlexei Avshalom Lazar 	case WIL_RX_CB_MODE_WIDE:
739abe3e30SAlexei Avshalom Lazar 		return 2;
749abe3e30SAlexei Avshalom Lazar 	default:
759abe3e30SAlexei Avshalom Lazar 		return 1;
769abe3e30SAlexei Avshalom Lazar 	}
779abe3e30SAlexei Avshalom Lazar }
789abe3e30SAlexei Avshalom Lazar 
wil_tx_cb_mode_to_n_bonded(u8 cb_mode)799abe3e30SAlexei Avshalom Lazar static int wil_tx_cb_mode_to_n_bonded(u8 cb_mode)
809abe3e30SAlexei Avshalom Lazar {
819abe3e30SAlexei Avshalom Lazar 	switch (cb_mode) {
829abe3e30SAlexei Avshalom Lazar 	case WMI_TX_MODE_DMG:
839abe3e30SAlexei Avshalom Lazar 	case WMI_TX_MODE_EDMG_CB1:
849abe3e30SAlexei Avshalom Lazar 		return 1;
859abe3e30SAlexei Avshalom Lazar 	case WMI_TX_MODE_EDMG_CB2:
869abe3e30SAlexei Avshalom Lazar 		return 2;
879abe3e30SAlexei Avshalom Lazar 	default:
889abe3e30SAlexei Avshalom Lazar 		return 1;
899abe3e30SAlexei Avshalom Lazar 	}
909abe3e30SAlexei Avshalom Lazar }
919abe3e30SAlexei Avshalom Lazar 
92e41ab937SDedy Lansky static void
wil_memdup_ie(u8 ** pdst,size_t * pdst_len,const u8 * src,size_t src_len)93e41ab937SDedy Lansky wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
94e41ab937SDedy Lansky {
95e41ab937SDedy Lansky 	kfree(*pdst);
96e41ab937SDedy Lansky 	*pdst = NULL;
97e41ab937SDedy Lansky 	*pdst_len = 0;
98e41ab937SDedy Lansky 	if (src_len > 0) {
99e41ab937SDedy Lansky 		*pdst = kmemdup(src, src_len, GFP_KERNEL);
100e41ab937SDedy Lansky 		if (*pdst)
101e41ab937SDedy Lansky 			*pdst_len = src_len;
102e41ab937SDedy Lansky 	}
103e41ab937SDedy Lansky }
104e41ab937SDedy Lansky 
wil_num_supported_channels(struct wil6210_priv * wil)10522b9610eSAlexei Avshalom Lazar static int wil_num_supported_channels(struct wil6210_priv *wil)
10622b9610eSAlexei Avshalom Lazar {
10722b9610eSAlexei Avshalom Lazar 	int num_channels = ARRAY_SIZE(wil_60ghz_channels);
10822b9610eSAlexei Avshalom Lazar 
10922b9610eSAlexei Avshalom Lazar 	if (!test_bit(WMI_FW_CAPABILITY_CHANNEL_4, wil->fw_capabilities))
11022b9610eSAlexei Avshalom Lazar 		num_channels--;
11122b9610eSAlexei Avshalom Lazar 
11222b9610eSAlexei Avshalom Lazar 	return num_channels;
11322b9610eSAlexei Avshalom Lazar }
11422b9610eSAlexei Avshalom Lazar 
update_supported_bands(struct wil6210_priv * wil)11522b9610eSAlexei Avshalom Lazar void update_supported_bands(struct wil6210_priv *wil)
11622b9610eSAlexei Avshalom Lazar {
11722b9610eSAlexei Avshalom Lazar 	struct wiphy *wiphy = wil_to_wiphy(wil);
11822b9610eSAlexei Avshalom Lazar 
11922b9610eSAlexei Avshalom Lazar 	wil_dbg_misc(wil, "update supported bands");
12022b9610eSAlexei Avshalom Lazar 
12122b9610eSAlexei Avshalom Lazar 	wiphy->bands[NL80211_BAND_60GHZ]->n_channels =
12222b9610eSAlexei Avshalom Lazar 						wil_num_supported_channels(wil);
1239abe3e30SAlexei Avshalom Lazar 
1249abe3e30SAlexei Avshalom Lazar 	if (test_bit(WMI_FW_CAPABILITY_CHANNEL_BONDING, wil->fw_capabilities)) {
1259abe3e30SAlexei Avshalom Lazar 		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.channels =
1269abe3e30SAlexei Avshalom Lazar 							WIL_EDMG_CHANNELS;
1279abe3e30SAlexei Avshalom Lazar 		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.bw_config =
1289abe3e30SAlexei Avshalom Lazar 						      WIL_EDMG_BW_CONFIGURATION;
1299abe3e30SAlexei Avshalom Lazar 	}
13022b9610eSAlexei Avshalom Lazar }
13122b9610eSAlexei Avshalom Lazar 
1320216a895SLior David /* Vendor id to be used in vendor specific command and events
1330216a895SLior David  * to user space.
1340216a895SLior David  * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID,
1350216a895SLior David  * vendor subcmd definitions prefixed with QCA_NL80211_VENDOR_SUBCMD, and
1360216a895SLior David  * qca_wlan_vendor_attr is open source file src/common/qca-vendor.h in
1370216a895SLior David  * git://w1.fi/srv/git/hostap.git; the values here are just a copy of that
1380216a895SLior David  */
1390216a895SLior David 
1400216a895SLior David #define QCA_NL80211_VENDOR_ID	0x001374
1410216a895SLior David 
1420216a895SLior David #define WIL_MAX_RF_SECTORS (128)
1430216a895SLior David #define WIL_CID_ALL (0xff)
1440216a895SLior David 
1450216a895SLior David enum qca_wlan_vendor_attr_rf_sector {
1460216a895SLior David 	QCA_ATTR_MAC_ADDR = 6,
1470216a895SLior David 	QCA_ATTR_PAD = 13,
1480216a895SLior David 	QCA_ATTR_TSF = 29,
1490216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_INDEX = 30,
1500216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE = 31,
1510216a895SLior David 	QCA_ATTR_DMG_RF_MODULE_MASK = 32,
1520216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG = 33,
1530216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_MAX,
1540216a895SLior David };
1550216a895SLior David 
1560216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_type {
1570216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE_RX,
1580216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE_TX,
1590216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX
1600216a895SLior David };
1610216a895SLior David 
1620216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
1630216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
1640216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
1650216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
1660216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
1670216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
1680216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
1690216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
1700216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
1710216a895SLior David 
1720216a895SLior David 	/* keep last */
1730216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
1740216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_MAX =
1750216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
1760216a895SLior David };
1770216a895SLior David 
1780216a895SLior David static const struct
1790216a895SLior David nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = {
1800216a895SLior David 	[QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN },
1810216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 },
1820216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 },
1830216a895SLior David 	[QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 },
1840216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED },
1850216a895SLior David };
1860216a895SLior David 
1870216a895SLior David static const struct
1880216a895SLior David nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = {
1890216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 },
1900216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 },
1910216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 },
1920216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 },
1930216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 },
1940216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 },
1950216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 },
1960216a895SLior David };
1970216a895SLior David 
1980216a895SLior David enum qca_nl80211_vendor_subcmds {
1990216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
2000216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
2010216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
2020216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
2030216a895SLior David };
2040216a895SLior David 
2050216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
2060216a895SLior David 				 struct wireless_dev *wdev,
2070216a895SLior David 				 const void *data, int data_len);
2080216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
2090216a895SLior David 				 struct wireless_dev *wdev,
2100216a895SLior David 				 const void *data, int data_len);
2110216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy,
2120216a895SLior David 				      struct wireless_dev *wdev,
2130216a895SLior David 				      const void *data, int data_len);
2140216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy,
2150216a895SLior David 				      struct wireless_dev *wdev,
2160216a895SLior David 				      const void *data, int data_len);
2170216a895SLior David 
2180216a895SLior David /* vendor specific commands */
2190216a895SLior David static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
2200216a895SLior David 	{
2210216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2220216a895SLior David 		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG,
2230216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2240216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2251667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2260216a895SLior David 		.doit = wil_rf_sector_get_cfg
2270216a895SLior David 	},
2280216a895SLior David 	{
2290216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2300216a895SLior David 		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG,
2310216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2320216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2331667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2340216a895SLior David 		.doit = wil_rf_sector_set_cfg
2350216a895SLior David 	},
2360216a895SLior David 	{
2370216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2380216a895SLior David 		.info.subcmd =
2390216a895SLior David 			QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR,
2400216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2410216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2421667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2430216a895SLior David 		.doit = wil_rf_sector_get_selected
2440216a895SLior David 	},
2450216a895SLior David 	{
2460216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2470216a895SLior David 		.info.subcmd =
2480216a895SLior David 			QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR,
2490216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2500216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2511667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2520216a895SLior David 		.doit = wil_rf_sector_set_selected
2530216a895SLior David 	},
2540216a895SLior David };
2550216a895SLior David 
2562be7d22fSVladimir Kondratiev static struct ieee80211_supported_band wil_band_60ghz = {
2572be7d22fSVladimir Kondratiev 	.channels = wil_60ghz_channels,
2582be7d22fSVladimir Kondratiev 	.n_channels = ARRAY_SIZE(wil_60ghz_channels),
2592be7d22fSVladimir Kondratiev 	.ht_cap = {
2602be7d22fSVladimir Kondratiev 		.ht_supported = true,
2612be7d22fSVladimir Kondratiev 		.cap = 0, /* TODO */
2622be7d22fSVladimir Kondratiev 		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
2632be7d22fSVladimir Kondratiev 		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
2642be7d22fSVladimir Kondratiev 		.mcs = {
2652be7d22fSVladimir Kondratiev 				/* MCS 1..12 - SC PHY */
2662be7d22fSVladimir Kondratiev 			.rx_mask = {0xfe, 0x1f}, /* 1..12 */
2672be7d22fSVladimir Kondratiev 			.tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
2682be7d22fSVladimir Kondratiev 		},
2692be7d22fSVladimir Kondratiev 	},
2702be7d22fSVladimir Kondratiev };
2712be7d22fSVladimir Kondratiev 
2722be7d22fSVladimir Kondratiev static const struct ieee80211_txrx_stypes
2732be7d22fSVladimir Kondratiev wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2742be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_STATION] = {
2752be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2762be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2772be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2782be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2792be7d22fSVladimir Kondratiev 	},
2802be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_AP] = {
2812be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
282849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4) |
283849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
284b9010f10SAhmad Masri 		BIT(IEEE80211_STYPE_DISASSOC >> 4) |
285b9010f10SAhmad Masri 		BIT(IEEE80211_STYPE_AUTH >> 4) |
286b9010f10SAhmad Masri 		BIT(IEEE80211_STYPE_REASSOC_RESP >> 4),
2872be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
288849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
289849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
290849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_DISASSOC >> 4) |
291849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_AUTH >> 4) |
292849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_DEAUTH >> 4) |
293849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_REASSOC_REQ >> 4)
2942be7d22fSVladimir Kondratiev 	},
2952be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_P2P_CLIENT] = {
2962be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2972be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2982be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2992be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3002be7d22fSVladimir Kondratiev 	},
3012be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_P2P_GO] = {
3022be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3032be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3042be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3052be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3062be7d22fSVladimir Kondratiev 	},
3074332cac1SLior David 	[NL80211_IFTYPE_P2P_DEVICE] = {
3084332cac1SLior David 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3094332cac1SLior David 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3104332cac1SLior David 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3114332cac1SLior David 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3124332cac1SLior David 	},
3132be7d22fSVladimir Kondratiev };
3142be7d22fSVladimir Kondratiev 
3152be7d22fSVladimir Kondratiev static const u32 wil_cipher_suites[] = {
3162be7d22fSVladimir Kondratiev 	WLAN_CIPHER_SUITE_GCMP,
3172be7d22fSVladimir Kondratiev };
3182be7d22fSVladimir Kondratiev 
31958527421SVladimir Kondratiev static const char * const key_usage_str[] = {
32058527421SVladimir Kondratiev 	[WMI_KEY_USE_PAIRWISE]	= "PTK",
32158527421SVladimir Kondratiev 	[WMI_KEY_USE_RX_GROUP]	= "RX_GTK",
32258527421SVladimir Kondratiev 	[WMI_KEY_USE_TX_GROUP]	= "TX_GTK",
32342fe1e51SAhmad Masri 	[WMI_KEY_USE_STORE_PTK]	= "STORE_PTK",
32442fe1e51SAhmad Masri 	[WMI_KEY_USE_APPLY_PTK]	= "APPLY_PTK",
32558527421SVladimir Kondratiev };
32658527421SVladimir Kondratiev 
wil_iftype_nl2wmi(enum nl80211_iftype type)3272be7d22fSVladimir Kondratiev int wil_iftype_nl2wmi(enum nl80211_iftype type)
3282be7d22fSVladimir Kondratiev {
3292be7d22fSVladimir Kondratiev 	static const struct {
3302be7d22fSVladimir Kondratiev 		enum nl80211_iftype nl;
3312be7d22fSVladimir Kondratiev 		enum wmi_network_type wmi;
3322be7d22fSVladimir Kondratiev 	} __nl2wmi[] = {
3332be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_ADHOC,		WMI_NETTYPE_ADHOC},
3342be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_STATION,	WMI_NETTYPE_INFRA},
3352be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_AP,		WMI_NETTYPE_AP},
3362be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_P2P_CLIENT,	WMI_NETTYPE_P2P},
3372be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_P2P_GO,		WMI_NETTYPE_P2P},
3382be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_MONITOR,	WMI_NETTYPE_ADHOC}, /* FIXME */
3392be7d22fSVladimir Kondratiev 	};
3402be7d22fSVladimir Kondratiev 	uint i;
3412be7d22fSVladimir Kondratiev 
3422be7d22fSVladimir Kondratiev 	for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
3432be7d22fSVladimir Kondratiev 		if (__nl2wmi[i].nl == type)
3442be7d22fSVladimir Kondratiev 			return __nl2wmi[i].wmi;
3452be7d22fSVladimir Kondratiev 	}
3462be7d22fSVladimir Kondratiev 
3472be7d22fSVladimir Kondratiev 	return -EOPNOTSUPP;
3482be7d22fSVladimir Kondratiev }
3492be7d22fSVladimir Kondratiev 
wil_spec2wmi_ch(u8 spec_ch,u8 * wmi_ch)3509abe3e30SAlexei Avshalom Lazar int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch)
3519abe3e30SAlexei Avshalom Lazar {
3529abe3e30SAlexei Avshalom Lazar 	switch (spec_ch) {
3539abe3e30SAlexei Avshalom Lazar 	case 1:
3549abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_1;
3559abe3e30SAlexei Avshalom Lazar 		break;
3569abe3e30SAlexei Avshalom Lazar 	case 2:
3579abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_2;
3589abe3e30SAlexei Avshalom Lazar 		break;
3599abe3e30SAlexei Avshalom Lazar 	case 3:
3609abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_3;
3619abe3e30SAlexei Avshalom Lazar 		break;
3629abe3e30SAlexei Avshalom Lazar 	case 4:
3639abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_4;
3649abe3e30SAlexei Avshalom Lazar 		break;
3659abe3e30SAlexei Avshalom Lazar 	case 5:
3669abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_5;
3679abe3e30SAlexei Avshalom Lazar 		break;
3689abe3e30SAlexei Avshalom Lazar 	case 6:
3699abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_6;
3709abe3e30SAlexei Avshalom Lazar 		break;
3719abe3e30SAlexei Avshalom Lazar 	case 9:
3729abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_9;
3739abe3e30SAlexei Avshalom Lazar 		break;
3749abe3e30SAlexei Avshalom Lazar 	case 10:
3759abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_10;
3769abe3e30SAlexei Avshalom Lazar 		break;
3779abe3e30SAlexei Avshalom Lazar 	case 11:
3789abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_11;
3799abe3e30SAlexei Avshalom Lazar 		break;
3809abe3e30SAlexei Avshalom Lazar 	case 12:
3819abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_12;
3829abe3e30SAlexei Avshalom Lazar 		break;
3839abe3e30SAlexei Avshalom Lazar 	default:
3849abe3e30SAlexei Avshalom Lazar 		return -EINVAL;
3859abe3e30SAlexei Avshalom Lazar 	}
3869abe3e30SAlexei Avshalom Lazar 
3879abe3e30SAlexei Avshalom Lazar 	return 0;
3889abe3e30SAlexei Avshalom Lazar }
3899abe3e30SAlexei Avshalom Lazar 
wil_wmi2spec_ch(u8 wmi_ch,u8 * spec_ch)3909abe3e30SAlexei Avshalom Lazar int wil_wmi2spec_ch(u8 wmi_ch, u8 *spec_ch)
3919abe3e30SAlexei Avshalom Lazar {
3929abe3e30SAlexei Avshalom Lazar 	switch (wmi_ch) {
3939abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_1:
3949abe3e30SAlexei Avshalom Lazar 		*spec_ch = 1;
3959abe3e30SAlexei Avshalom Lazar 		break;
3969abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_2:
3979abe3e30SAlexei Avshalom Lazar 		*spec_ch = 2;
3989abe3e30SAlexei Avshalom Lazar 		break;
3999abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_3:
4009abe3e30SAlexei Avshalom Lazar 		*spec_ch = 3;
4019abe3e30SAlexei Avshalom Lazar 		break;
4029abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_4:
4039abe3e30SAlexei Avshalom Lazar 		*spec_ch = 4;
4049abe3e30SAlexei Avshalom Lazar 		break;
4059abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_5:
4069abe3e30SAlexei Avshalom Lazar 		*spec_ch = 5;
4079abe3e30SAlexei Avshalom Lazar 		break;
4089abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_6:
4099abe3e30SAlexei Avshalom Lazar 		*spec_ch = 6;
4109abe3e30SAlexei Avshalom Lazar 		break;
4119abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_9:
4129abe3e30SAlexei Avshalom Lazar 		*spec_ch = 9;
4139abe3e30SAlexei Avshalom Lazar 		break;
4149abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_10:
4159abe3e30SAlexei Avshalom Lazar 		*spec_ch = 10;
4169abe3e30SAlexei Avshalom Lazar 		break;
4179abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_11:
4189abe3e30SAlexei Avshalom Lazar 		*spec_ch = 11;
4199abe3e30SAlexei Avshalom Lazar 		break;
4209abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_12:
4219abe3e30SAlexei Avshalom Lazar 		*spec_ch = 12;
4229abe3e30SAlexei Avshalom Lazar 		break;
4239abe3e30SAlexei Avshalom Lazar 	default:
4249abe3e30SAlexei Avshalom Lazar 		return -EINVAL;
4259abe3e30SAlexei Avshalom Lazar 	}
4269abe3e30SAlexei Avshalom Lazar 
4279abe3e30SAlexei Avshalom Lazar 	return 0;
4289abe3e30SAlexei Avshalom Lazar }
4299abe3e30SAlexei Avshalom Lazar 
wil_cid_fill_sinfo(struct wil6210_vif * vif,int cid,struct station_info * sinfo)430e00243faSLior David int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
431ef28afdbSVladimir Kondratiev 		       struct station_info *sinfo)
4322be7d22fSVladimir Kondratiev {
433e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
4342be7d22fSVladimir Kondratiev 	struct wmi_notify_req_cmd cmd = {
4353df2cd36SVladimir Kondratiev 		.cid = cid,
4362be7d22fSVladimir Kondratiev 		.interval_usec = 0,
4372be7d22fSVladimir Kondratiev 	};
438ef28afdbSVladimir Kondratiev 	struct {
439b874ddecSLior David 		struct wmi_cmd_hdr wmi;
440ef28afdbSVladimir Kondratiev 		struct wmi_notify_req_done_event evt;
441ef28afdbSVladimir Kondratiev 	} __packed reply;
442c8b78b5fSVladimir Kondratiev 	struct wil_net_stats *stats = &wil->sta[cid].stats;
443ef28afdbSVladimir Kondratiev 	int rc;
4447064e219SMax Chen 	u8 tx_mcs, rx_mcs;
4457064e219SMax Chen 	u8 tx_rate_flag = RATE_INFO_FLAGS_DMG;
4467064e219SMax Chen 	u8 rx_rate_flag = RATE_INFO_FLAGS_DMG;
4472be7d22fSVladimir Kondratiev 
448807b0860SAlexei Avshalom Lazar 	memset(&reply, 0, sizeof(reply));
449807b0860SAlexei Avshalom Lazar 
450e00243faSLior David 	rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd),
4512a32c20bSAhmad Masri 		      WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply),
4522a32c20bSAhmad Masri 		      WIL_WMI_CALL_GENERAL_TO_MS);
4532be7d22fSVladimir Kondratiev 	if (rc)
4542be7d22fSVladimir Kondratiev 		return rc;
4552be7d22fSVladimir Kondratiev 
4567064e219SMax Chen 	tx_mcs = le16_to_cpu(reply.evt.bf_mcs);
4577064e219SMax Chen 
458e00243faSLior David 	wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n"
4597064e219SMax Chen 		    "  MCS %s TSF 0x%016llx\n"
46030868f5dSDedy Lansky 		    "  BF status 0x%08x RSSI %d SQI %d%%\n"
461c8b78b5fSVladimir Kondratiev 		    "  Tx Tpt %d goodput %d Rx goodput %d\n"
4629abe3e30SAlexei Avshalom Lazar 		    "  Sectors(rx:tx) my %d:%d peer %d:%d\n"
4639abe3e30SAlexei Avshalom Lazar 		    "  Tx mode %d}\n",
4647064e219SMax Chen 		    cid, vif->mid, WIL_EXTENDED_MCS_CHECK(tx_mcs),
465c8b78b5fSVladimir Kondratiev 		    le64_to_cpu(reply.evt.tsf), reply.evt.status,
46630868f5dSDedy Lansky 		    reply.evt.rssi,
467b8b33a3aSVladimir Kondratiev 		    reply.evt.sqi,
468c8b78b5fSVladimir Kondratiev 		    le32_to_cpu(reply.evt.tx_tpt),
469c8b78b5fSVladimir Kondratiev 		    le32_to_cpu(reply.evt.tx_goodput),
470c8b78b5fSVladimir Kondratiev 		    le32_to_cpu(reply.evt.rx_goodput),
471c8b78b5fSVladimir Kondratiev 		    le16_to_cpu(reply.evt.my_rx_sector),
472c8b78b5fSVladimir Kondratiev 		    le16_to_cpu(reply.evt.my_tx_sector),
473c8b78b5fSVladimir Kondratiev 		    le16_to_cpu(reply.evt.other_rx_sector),
4749abe3e30SAlexei Avshalom Lazar 		    le16_to_cpu(reply.evt.other_tx_sector),
4759abe3e30SAlexei Avshalom Lazar 		    reply.evt.tx_mode);
476c8b78b5fSVladimir Kondratiev 
4772be7d22fSVladimir Kondratiev 	sinfo->generation = wil->sinfo_gen;
4782be7d22fSVladimir Kondratiev 
47922d0d2faSOmer Efrat 	sinfo->filled = BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
48022d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
48122d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
48222d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
48322d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_RX_BITRATE) |
48422d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_BITRATE) |
48522d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
48622d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_FAILED);
487c8b78b5fSVladimir Kondratiev 
4887064e219SMax Chen 	if (wil->use_enhanced_dma_hw && reply.evt.tx_mode != WMI_TX_MODE_DMG) {
4897064e219SMax Chen 		tx_rate_flag = RATE_INFO_FLAGS_EDMG;
4907064e219SMax Chen 		rx_rate_flag = RATE_INFO_FLAGS_EDMG;
4917064e219SMax Chen 	}
4929abe3e30SAlexei Avshalom Lazar 
4937064e219SMax Chen 	rx_mcs = stats->last_mcs_rx;
4947064e219SMax Chen 
4957064e219SMax Chen 	/* check extended MCS (12.1) and convert it into
4967064e219SMax Chen 	 * base MCS (7) + EXTENDED_SC_DMG flag
4977064e219SMax Chen 	 */
4987064e219SMax Chen 	if (tx_mcs == WIL_EXTENDED_MCS_26) {
4997064e219SMax Chen 		tx_rate_flag = RATE_INFO_FLAGS_EXTENDED_SC_DMG;
5007064e219SMax Chen 		tx_mcs = WIL_BASE_MCS_FOR_EXTENDED_26;
5017064e219SMax Chen 	}
5027064e219SMax Chen 	if (rx_mcs == WIL_EXTENDED_MCS_26) {
5037064e219SMax Chen 		rx_rate_flag = RATE_INFO_FLAGS_EXTENDED_SC_DMG;
5047064e219SMax Chen 		rx_mcs = WIL_BASE_MCS_FOR_EXTENDED_26;
5057064e219SMax Chen 	}
5067064e219SMax Chen 
5077064e219SMax Chen 	sinfo->txrate.flags = tx_rate_flag;
5087064e219SMax Chen 	sinfo->rxrate.flags = rx_rate_flag;
5097064e219SMax Chen 	sinfo->txrate.mcs = tx_mcs;
5107064e219SMax Chen 	sinfo->rxrate.mcs = rx_mcs;
5117064e219SMax Chen 
5129abe3e30SAlexei Avshalom Lazar 	sinfo->txrate.n_bonded_ch =
5139abe3e30SAlexei Avshalom Lazar 				  wil_tx_cb_mode_to_n_bonded(reply.evt.tx_mode);
5149abe3e30SAlexei Avshalom Lazar 	sinfo->rxrate.n_bonded_ch =
5159abe3e30SAlexei Avshalom Lazar 			     wil_rx_cb_mode_to_n_bonded(stats->last_cb_mode_rx);
516c8b78b5fSVladimir Kondratiev 	sinfo->rx_bytes = stats->rx_bytes;
517c8b78b5fSVladimir Kondratiev 	sinfo->rx_packets = stats->rx_packets;
518c8b78b5fSVladimir Kondratiev 	sinfo->rx_dropped_misc = stats->rx_dropped;
519c8b78b5fSVladimir Kondratiev 	sinfo->tx_bytes = stats->tx_bytes;
520c8b78b5fSVladimir Kondratiev 	sinfo->tx_packets = stats->tx_packets;
521c8b78b5fSVladimir Kondratiev 	sinfo->tx_failed = stats->tx_errors;
5222be7d22fSVladimir Kondratiev 
5235bd60982SLior David 	if (test_bit(wil_vif_fwconnected, vif->status)) {
52422d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
52530868f5dSDedy Lansky 		if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING,
52630868f5dSDedy Lansky 			     wil->fw_capabilities))
52730868f5dSDedy Lansky 			sinfo->signal = reply.evt.rssi;
52830868f5dSDedy Lansky 		else
529b8b33a3aSVladimir Kondratiev 			sinfo->signal = reply.evt.sqi;
5302be7d22fSVladimir Kondratiev 	}
5312be7d22fSVladimir Kondratiev 
532ef28afdbSVladimir Kondratiev 	return rc;
533ef28afdbSVladimir Kondratiev }
534ef28afdbSVladimir Kondratiev 
wil_cfg80211_get_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_info * sinfo)535ef28afdbSVladimir Kondratiev static int wil_cfg80211_get_station(struct wiphy *wiphy,
536ef28afdbSVladimir Kondratiev 				    struct net_device *ndev,
5373b3a0162SJohannes Berg 				    const u8 *mac, struct station_info *sinfo)
538ef28afdbSVladimir Kondratiev {
539e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
540ef28afdbSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
541ef28afdbSVladimir Kondratiev 	int rc;
542ef28afdbSVladimir Kondratiev 
543e00243faSLior David 	int cid = wil_find_cid(wil, vif->mid, mac);
544ef28afdbSVladimir Kondratiev 
545e00243faSLior David 	wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid,
546e00243faSLior David 		     vif->mid);
547c478ac9dSAlexei Avshalom Lazar 	if (!wil_cid_valid(wil, cid))
548c478ac9dSAlexei Avshalom Lazar 		return -ENOENT;
549ef28afdbSVladimir Kondratiev 
550e00243faSLior David 	rc = wil_cid_fill_sinfo(vif, cid, sinfo);
551ef28afdbSVladimir Kondratiev 
552ef28afdbSVladimir Kondratiev 	return rc;
553ef28afdbSVladimir Kondratiev }
554ef28afdbSVladimir Kondratiev 
555ef28afdbSVladimir Kondratiev /*
556e00243faSLior David  * Find @idx-th active STA for specific MID for station dump.
557ef28afdbSVladimir Kondratiev  */
wil_find_cid_by_idx(struct wil6210_priv * wil,u8 mid,int idx)55842fe1e51SAhmad Masri int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
559ef28afdbSVladimir Kondratiev {
560ef28afdbSVladimir Kondratiev 	int i;
561ef28afdbSVladimir Kondratiev 
562ddf7afddSAhmad Masri 	for (i = 0; i < wil->max_assoc_sta; i++) {
563ef28afdbSVladimir Kondratiev 		if (wil->sta[i].status == wil_sta_unused)
564ef28afdbSVladimir Kondratiev 			continue;
565e00243faSLior David 		if (wil->sta[i].mid != mid)
566e00243faSLior David 			continue;
567ef28afdbSVladimir Kondratiev 		if (idx == 0)
568ef28afdbSVladimir Kondratiev 			return i;
569ef28afdbSVladimir Kondratiev 		idx--;
570ef28afdbSVladimir Kondratiev 	}
571ef28afdbSVladimir Kondratiev 
572ef28afdbSVladimir Kondratiev 	return -ENOENT;
573ef28afdbSVladimir Kondratiev }
574ef28afdbSVladimir Kondratiev 
wil_cfg80211_dump_station(struct wiphy * wiphy,struct net_device * dev,int idx,u8 * mac,struct station_info * sinfo)575ef28afdbSVladimir Kondratiev static int wil_cfg80211_dump_station(struct wiphy *wiphy,
576ef28afdbSVladimir Kondratiev 				     struct net_device *dev, int idx,
577ef28afdbSVladimir Kondratiev 				     u8 *mac, struct station_info *sinfo)
578ef28afdbSVladimir Kondratiev {
579e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
580ef28afdbSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
581ef28afdbSVladimir Kondratiev 	int rc;
582e00243faSLior David 	int cid = wil_find_cid_by_idx(wil, vif->mid, idx);
583ef28afdbSVladimir Kondratiev 
584c478ac9dSAlexei Avshalom Lazar 	if (!wil_cid_valid(wil, cid))
585ef28afdbSVladimir Kondratiev 		return -ENOENT;
586ef28afdbSVladimir Kondratiev 
587a82553bbSVladimir Kondratiev 	ether_addr_copy(mac, wil->sta[cid].addr);
588e00243faSLior David 	wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid,
589e00243faSLior David 		     vif->mid);
590ef28afdbSVladimir Kondratiev 
591e00243faSLior David 	rc = wil_cid_fill_sinfo(vif, cid, sinfo);
592ef28afdbSVladimir Kondratiev 
593ef28afdbSVladimir Kondratiev 	return rc;
5942be7d22fSVladimir Kondratiev }
5952be7d22fSVladimir Kondratiev 
wil_cfg80211_start_p2p_device(struct wiphy * wiphy,struct wireless_dev * wdev)59669fecf59SLior David static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
59769fecf59SLior David 					 struct wireless_dev *wdev)
59869fecf59SLior David {
59969fecf59SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
60069fecf59SLior David 
60169fecf59SLior David 	wil_dbg_misc(wil, "start_p2p_device: entered\n");
602e00243faSLior David 	wil->p2p_dev_started = 1;
60369fecf59SLior David 	return 0;
60469fecf59SLior David }
60569fecf59SLior David 
wil_cfg80211_stop_p2p_device(struct wiphy * wiphy,struct wireless_dev * wdev)60669fecf59SLior David static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
60769fecf59SLior David 					 struct wireless_dev *wdev)
60869fecf59SLior David {
60969fecf59SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
61069fecf59SLior David 
611e00243faSLior David 	if (!wil->p2p_dev_started)
61269fecf59SLior David 		return;
61369fecf59SLior David 
61469fecf59SLior David 	wil_dbg_misc(wil, "stop_p2p_device: entered\n");
61569fecf59SLior David 	mutex_lock(&wil->mutex);
616404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
61769fecf59SLior David 	wil_p2p_stop_radio_operations(wil);
618e00243faSLior David 	wil->p2p_dev_started = 0;
619404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
62069fecf59SLior David 	mutex_unlock(&wil->mutex);
62169fecf59SLior David }
62269fecf59SLior David 
wil_cfg80211_validate_add_iface(struct wil6210_priv * wil,enum nl80211_iftype new_type)6234aebd3bdSLior David static int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil,
6244aebd3bdSLior David 					   enum nl80211_iftype new_type)
6254aebd3bdSLior David {
6264aebd3bdSLior David 	int i;
6274aebd3bdSLior David 	struct wireless_dev *wdev;
6284aebd3bdSLior David 	struct iface_combination_params params = {
6294aebd3bdSLior David 		.num_different_channels = 1,
6304aebd3bdSLior David 	};
6314aebd3bdSLior David 
632e4a29bddSAhmad Masri 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
6334aebd3bdSLior David 		if (wil->vifs[i]) {
6344aebd3bdSLior David 			wdev = vif_to_wdev(wil->vifs[i]);
6354aebd3bdSLior David 			params.iftype_num[wdev->iftype]++;
6364aebd3bdSLior David 		}
6374aebd3bdSLior David 	}
6384aebd3bdSLior David 	params.iftype_num[new_type]++;
6394aebd3bdSLior David 	return cfg80211_check_combinations(wil->wiphy, &params);
6404aebd3bdSLior David }
6414aebd3bdSLior David 
wil_cfg80211_validate_change_iface(struct wil6210_priv * wil,struct wil6210_vif * vif,enum nl80211_iftype new_type)6424aebd3bdSLior David static int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil,
6434aebd3bdSLior David 					      struct wil6210_vif *vif,
6444aebd3bdSLior David 					      enum nl80211_iftype new_type)
6454aebd3bdSLior David {
6464aebd3bdSLior David 	int i, ret = 0;
6474aebd3bdSLior David 	struct wireless_dev *wdev;
6484aebd3bdSLior David 	struct iface_combination_params params = {
6494aebd3bdSLior David 		.num_different_channels = 1,
6504aebd3bdSLior David 	};
6514aebd3bdSLior David 	bool check_combos = false;
6524aebd3bdSLior David 
653e4a29bddSAhmad Masri 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
6544aebd3bdSLior David 		struct wil6210_vif *vif_pos = wil->vifs[i];
6554aebd3bdSLior David 
6564aebd3bdSLior David 		if (vif_pos && vif != vif_pos) {
6574aebd3bdSLior David 			wdev = vif_to_wdev(vif_pos);
6584aebd3bdSLior David 			params.iftype_num[wdev->iftype]++;
6594aebd3bdSLior David 			check_combos = true;
6604aebd3bdSLior David 		}
6614aebd3bdSLior David 	}
6624aebd3bdSLior David 
6634aebd3bdSLior David 	if (check_combos) {
6644aebd3bdSLior David 		params.iftype_num[new_type]++;
6654aebd3bdSLior David 		ret = cfg80211_check_combinations(wil->wiphy, &params);
6664aebd3bdSLior David 	}
6674aebd3bdSLior David 	return ret;
6684aebd3bdSLior David }
6694aebd3bdSLior David 
6704332cac1SLior David static struct wireless_dev *
wil_cfg80211_add_iface(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,struct vif_params * params)6714332cac1SLior David wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
6724332cac1SLior David 		       unsigned char name_assign_type,
6734332cac1SLior David 		       enum nl80211_iftype type,
674818a986eSJohannes Berg 		       struct vif_params *params)
6754332cac1SLior David {
6764332cac1SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
6774aebd3bdSLior David 	struct net_device *ndev_main = wil->main_ndev, *ndev;
6789f38f286SLior David 	struct wil6210_vif *vif;
6794aebd3bdSLior David 	struct wireless_dev *p2p_wdev, *wdev;
6804aebd3bdSLior David 	int rc;
6814332cac1SLior David 
6824aebd3bdSLior David 	wil_dbg_misc(wil, "add_iface, type %d\n", type);
6834332cac1SLior David 
6844aebd3bdSLior David 	/* P2P device is not a real virtual interface, it is a management-only
6854aebd3bdSLior David 	 * interface that shares the main interface.
6864aebd3bdSLior David 	 * Skip concurrency checks here.
6874aebd3bdSLior David 	 */
6884aebd3bdSLior David 	if (type == NL80211_IFTYPE_P2P_DEVICE) {
6894332cac1SLior David 		if (wil->p2p_wdev) {
690af3db60aSLazar Alexei 			wil_err(wil, "P2P_DEVICE interface already created\n");
6914332cac1SLior David 			return ERR_PTR(-EINVAL);
6924332cac1SLior David 		}
6934332cac1SLior David 
6945bd60982SLior David 		p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
6955bd60982SLior David 		if (!p2p_wdev)
6964332cac1SLior David 			return ERR_PTR(-ENOMEM);
6974332cac1SLior David 
6984332cac1SLior David 		p2p_wdev->iftype = type;
6994332cac1SLior David 		p2p_wdev->wiphy = wiphy;
7004332cac1SLior David 		/* use our primary ethernet address */
7014aebd3bdSLior David 		ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr);
7024332cac1SLior David 
7034332cac1SLior David 		wil->p2p_wdev = p2p_wdev;
7044332cac1SLior David 
7054332cac1SLior David 		return p2p_wdev;
7064332cac1SLior David 	}
7074332cac1SLior David 
7084aebd3bdSLior David 	if (!wil->wiphy->n_iface_combinations) {
7094aebd3bdSLior David 		wil_err(wil, "virtual interfaces not supported\n");
7104aebd3bdSLior David 		return ERR_PTR(-EINVAL);
7114aebd3bdSLior David 	}
7124aebd3bdSLior David 
7134aebd3bdSLior David 	rc = wil_cfg80211_validate_add_iface(wil, type);
7144aebd3bdSLior David 	if (rc) {
7154aebd3bdSLior David 		wil_err(wil, "iface validation failed, err=%d\n", rc);
7164aebd3bdSLior David 		return ERR_PTR(rc);
7174aebd3bdSLior David 	}
7184aebd3bdSLior David 
7194aebd3bdSLior David 	vif = wil_vif_alloc(wil, name, name_assign_type, type);
7204aebd3bdSLior David 	if (IS_ERR(vif))
7214aebd3bdSLior David 		return ERR_CAST(vif);
7224aebd3bdSLior David 
7234aebd3bdSLior David 	ndev = vif_to_ndev(vif);
7244aebd3bdSLior David 	ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr);
7254aebd3bdSLior David 	if (is_valid_ether_addr(params->macaddr)) {
726fcb79f31SJakub Kicinski 		eth_hw_addr_set(ndev, params->macaddr);
7274aebd3bdSLior David 	} else {
728c7b6128aSJakub Kicinski 		u8 addr[ETH_ALEN];
729c7b6128aSJakub Kicinski 
730c7b6128aSJakub Kicinski 		ether_addr_copy(addr, ndev_main->perm_addr);
731c7b6128aSJakub Kicinski 		addr[0] = (addr[0] ^ (1 << vif->mid)) |	0x2; /* locally administered */
732c7b6128aSJakub Kicinski 		eth_hw_addr_set(ndev, addr);
7334aebd3bdSLior David 	}
7344aebd3bdSLior David 	wdev = vif_to_wdev(vif);
7354aebd3bdSLior David 	ether_addr_copy(wdev->address, ndev->dev_addr);
7364aebd3bdSLior David 
7374aebd3bdSLior David 	rc = wil_vif_add(wil, vif);
7384aebd3bdSLior David 	if (rc)
7394aebd3bdSLior David 		goto out;
7404aebd3bdSLior David 
7414aebd3bdSLior David 	wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n",
7424aebd3bdSLior David 		 vif->mid, type, wdev->address);
7434aebd3bdSLior David 	return wdev;
7444aebd3bdSLior David out:
7454aebd3bdSLior David 	wil_vif_free(vif);
7464aebd3bdSLior David 	return ERR_PTR(rc);
7474aebd3bdSLior David }
7484aebd3bdSLior David 
wil_vif_prepare_stop(struct wil6210_vif * vif)7493ada9314SLior David int wil_vif_prepare_stop(struct wil6210_vif *vif)
7504aebd3bdSLior David {
7513ada9314SLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
7524aebd3bdSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
7534aebd3bdSLior David 	struct net_device *ndev;
7544aebd3bdSLior David 	int rc;
7554aebd3bdSLior David 
7564aebd3bdSLior David 	if (wdev->iftype != NL80211_IFTYPE_AP)
7574aebd3bdSLior David 		return 0;
7584aebd3bdSLior David 
7594aebd3bdSLior David 	ndev = vif_to_ndev(vif);
7604aebd3bdSLior David 	if (netif_carrier_ok(ndev)) {
7614aebd3bdSLior David 		rc = wmi_pcp_stop(vif);
7624aebd3bdSLior David 		if (rc) {
7634aebd3bdSLior David 			wil_info(wil, "failed to stop AP, status %d\n",
7644aebd3bdSLior David 				 rc);
7654aebd3bdSLior David 			/* continue */
7664aebd3bdSLior David 		}
7673ada9314SLior David 		wil_bcast_fini(vif);
7684aebd3bdSLior David 		netif_carrier_off(ndev);
7694aebd3bdSLior David 	}
7704aebd3bdSLior David 
7714aebd3bdSLior David 	return 0;
7724aebd3bdSLior David }
7734aebd3bdSLior David 
wil_cfg80211_del_iface(struct wiphy * wiphy,struct wireless_dev * wdev)7744332cac1SLior David static int wil_cfg80211_del_iface(struct wiphy *wiphy,
7754332cac1SLior David 				  struct wireless_dev *wdev)
7764332cac1SLior David {
7774332cac1SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
7784aebd3bdSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
7794aebd3bdSLior David 	int rc;
7804332cac1SLior David 
781af3db60aSLazar Alexei 	wil_dbg_misc(wil, "del_iface\n");
7824332cac1SLior David 
7834aebd3bdSLior David 	if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
7844332cac1SLior David 		if (wdev != wil->p2p_wdev) {
7854aebd3bdSLior David 			wil_err(wil, "delete of incorrect interface 0x%p\n",
7864aebd3bdSLior David 				wdev);
7874332cac1SLior David 			return -EINVAL;
7884332cac1SLior David 		}
7894332cac1SLior David 
79069fecf59SLior David 		wil_cfg80211_stop_p2p_device(wiphy, wdev);
7914332cac1SLior David 		wil_p2p_wdev_free(wil);
7924332cac1SLior David 		return 0;
7934332cac1SLior David 	}
7944332cac1SLior David 
7954aebd3bdSLior David 	if (vif->mid == 0) {
7964aebd3bdSLior David 		wil_err(wil, "cannot remove the main interface\n");
7974aebd3bdSLior David 		return -EINVAL;
7984aebd3bdSLior David 	}
7994aebd3bdSLior David 
8003ada9314SLior David 	rc = wil_vif_prepare_stop(vif);
8014aebd3bdSLior David 	if (rc)
8024aebd3bdSLior David 		goto out;
8034aebd3bdSLior David 
8044aebd3bdSLior David 	wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n",
8054aebd3bdSLior David 		 vif->mid, wdev->iftype, wdev->address);
8064aebd3bdSLior David 
8074aebd3bdSLior David 	wil_vif_remove(wil, vif->mid);
8084aebd3bdSLior David out:
8094aebd3bdSLior David 	return rc;
8104aebd3bdSLior David }
8114aebd3bdSLior David 
wil_is_safe_switch(enum nl80211_iftype from,enum nl80211_iftype to)812b913e330SAlexei Avshalom Lazar static bool wil_is_safe_switch(enum nl80211_iftype from,
813b913e330SAlexei Avshalom Lazar 			       enum nl80211_iftype to)
814b913e330SAlexei Avshalom Lazar {
815b913e330SAlexei Avshalom Lazar 	if (from == NL80211_IFTYPE_STATION &&
816b913e330SAlexei Avshalom Lazar 	    to == NL80211_IFTYPE_P2P_CLIENT)
817b913e330SAlexei Avshalom Lazar 		return true;
818b913e330SAlexei Avshalom Lazar 
819b913e330SAlexei Avshalom Lazar 	return false;
820b913e330SAlexei Avshalom Lazar }
821b913e330SAlexei Avshalom Lazar 
wil_cfg80211_change_iface(struct wiphy * wiphy,struct net_device * ndev,enum nl80211_iftype type,struct vif_params * params)8222be7d22fSVladimir Kondratiev static int wil_cfg80211_change_iface(struct wiphy *wiphy,
8232be7d22fSVladimir Kondratiev 				     struct net_device *ndev,
824818a986eSJohannes Berg 				     enum nl80211_iftype type,
8252be7d22fSVladimir Kondratiev 				     struct vif_params *params)
8262be7d22fSVladimir Kondratiev {
8272be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
828e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
829e00243faSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
830e6d68341SDedy Lansky 	int rc;
8313ada9314SLior David 	bool fw_reset = false;
832e6d68341SDedy Lansky 
833af3db60aSLazar Alexei 	wil_dbg_misc(wil, "change_iface: type=%d\n", type);
834e6d68341SDedy Lansky 
8354aebd3bdSLior David 	if (wiphy->n_iface_combinations) {
8364aebd3bdSLior David 		rc = wil_cfg80211_validate_change_iface(wil, vif, type);
8374aebd3bdSLior David 		if (rc) {
8384aebd3bdSLior David 			wil_err(wil, "iface validation failed, err=%d\n", rc);
8394aebd3bdSLior David 			return rc;
8404aebd3bdSLior David 		}
8414aebd3bdSLior David 	}
8424aebd3bdSLior David 
8434aebd3bdSLior David 	/* do not reset FW when there are active VIFs,
8444aebd3bdSLior David 	 * because it can cause significant disruption
8454aebd3bdSLior David 	 */
8463ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, true, false) &&
847b913e330SAlexei Avshalom Lazar 	    netif_running(ndev) && !wil_is_recovery_blocked(wil) &&
848b913e330SAlexei Avshalom Lazar 	    !wil_is_safe_switch(wdev->iftype, type)) {
849e6d68341SDedy Lansky 		wil_dbg_misc(wil, "interface is up. resetting...\n");
850e6d68341SDedy Lansky 		mutex_lock(&wil->mutex);
851e6d68341SDedy Lansky 		__wil_down(wil);
852e6d68341SDedy Lansky 		rc = __wil_up(wil);
853e6d68341SDedy Lansky 		mutex_unlock(&wil->mutex);
854e6d68341SDedy Lansky 
855e6d68341SDedy Lansky 		if (rc)
856e6d68341SDedy Lansky 			return rc;
8573ada9314SLior David 		fw_reset = true;
858e6d68341SDedy Lansky 	}
8592be7d22fSVladimir Kondratiev 
8602be7d22fSVladimir Kondratiev 	switch (type) {
8612be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
8622be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_AP:
8632be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
8642be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_GO:
8652be7d22fSVladimir Kondratiev 		break;
8662be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_MONITOR:
867818a986eSJohannes Berg 		if (params->flags)
868818a986eSJohannes Berg 			wil->monitor_flags = params->flags;
8692be7d22fSVladimir Kondratiev 		break;
8702be7d22fSVladimir Kondratiev 	default:
8712be7d22fSVladimir Kondratiev 		return -EOPNOTSUPP;
8722be7d22fSVladimir Kondratiev 	}
8732be7d22fSVladimir Kondratiev 
8743ada9314SLior David 	if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) {
8753ada9314SLior David 		if (!fw_reset)
8763ada9314SLior David 			wil_vif_prepare_stop(vif);
8774aebd3bdSLior David 		rc = wmi_port_delete(wil, vif->mid);
8784aebd3bdSLior David 		if (rc)
8794aebd3bdSLior David 			return rc;
8804aebd3bdSLior David 		rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type);
8814aebd3bdSLior David 		if (rc)
8824aebd3bdSLior David 			return rc;
8834aebd3bdSLior David 	}
8842be7d22fSVladimir Kondratiev 
8854aebd3bdSLior David 	wdev->iftype = type;
8862be7d22fSVladimir Kondratiev 	return 0;
8872be7d22fSVladimir Kondratiev }
8882be7d22fSVladimir Kondratiev 
wil_cfg80211_scan(struct wiphy * wiphy,struct cfg80211_scan_request * request)8892be7d22fSVladimir Kondratiev static int wil_cfg80211_scan(struct wiphy *wiphy,
8902be7d22fSVladimir Kondratiev 			     struct cfg80211_scan_request *request)
8912be7d22fSVladimir Kondratiev {
8922be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
8934332cac1SLior David 	struct wireless_dev *wdev = request->wdev;
894e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
8952be7d22fSVladimir Kondratiev 	struct {
8962be7d22fSVladimir Kondratiev 		struct wmi_start_scan_cmd cmd;
8972be7d22fSVladimir Kondratiev 		u16 chnl[4];
8982be7d22fSVladimir Kondratiev 	} __packed cmd;
8992be7d22fSVladimir Kondratiev 	uint i, n;
900ed6f9dc6SVladimir Kondratiev 	int rc;
9012be7d22fSVladimir Kondratiev 
902af3db60aSLazar Alexei 	wil_dbg_misc(wil, "scan: wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
903e6d68341SDedy Lansky 
904af2cd85eSAhmad Masri 	/* scan is supported on client interfaces and on AP interface */
9052be7d22fSVladimir Kondratiev 	switch (wdev->iftype) {
9062be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
9072be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
9084332cac1SLior David 	case NL80211_IFTYPE_P2P_DEVICE:
909af2cd85eSAhmad Masri 	case NL80211_IFTYPE_AP:
9102be7d22fSVladimir Kondratiev 		break;
9112be7d22fSVladimir Kondratiev 	default:
9122be7d22fSVladimir Kondratiev 		return -EOPNOTSUPP;
9132be7d22fSVladimir Kondratiev 	}
9142be7d22fSVladimir Kondratiev 
9152be7d22fSVladimir Kondratiev 	/* FW don't support scan after connection attempt */
9169419b6a2SVladimir Kondratiev 	if (test_bit(wil_status_dontscan, wil->status)) {
917e83eb2fcSVladimir Kondratiev 		wil_err(wil, "Can't scan now\n");
9182be7d22fSVladimir Kondratiev 		return -EBUSY;
9192be7d22fSVladimir Kondratiev 	}
9202be7d22fSVladimir Kondratiev 
921bb6743f7SLior David 	mutex_lock(&wil->mutex);
922bb6743f7SLior David 
923404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
924e00243faSLior David 	if (vif->scan_request || vif->p2p.discovery_started) {
925bb6743f7SLior David 		wil_err(wil, "Already scanning\n");
926404bbb3cSLior David 		mutex_unlock(&wil->vif_mutex);
927bb6743f7SLior David 		rc = -EAGAIN;
928bb6743f7SLior David 		goto out;
929bb6743f7SLior David 	}
930404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
931bb6743f7SLior David 
9324a0e45a7SLior David 	if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
933e00243faSLior David 		if (!wil->p2p_dev_started) {
934eb57a5b3SLior David 			wil_err(wil, "P2P search requested on stopped P2P device\n");
935bb6743f7SLior David 			rc = -EIO;
936bb6743f7SLior David 			goto out;
937eb57a5b3SLior David 		}
9384a0e45a7SLior David 		/* social scan on P2P_DEVICE is handled as p2p search */
9394a0e45a7SLior David 		if (wil_p2p_is_social_scan(request)) {
940e00243faSLior David 			vif->scan_request = request;
941e00243faSLior David 			if (vif->mid == 0)
9424332cac1SLior David 				wil->radio_wdev = wdev;
943e00243faSLior David 			rc = wil_p2p_search(vif, request);
9444332cac1SLior David 			if (rc) {
945e00243faSLior David 				if (vif->mid == 0)
946e00243faSLior David 					wil->radio_wdev =
947e00243faSLior David 						wil->main_ndev->ieee80211_ptr;
948e00243faSLior David 				vif->scan_request = NULL;
9494332cac1SLior David 			}
950bb6743f7SLior David 			goto out;
951e6d68341SDedy Lansky 		}
9524a0e45a7SLior David 	}
953e6d68341SDedy Lansky 
954e00243faSLior David 	(void)wil_p2p_stop_discovery(vif);
955e6d68341SDedy Lansky 
9562a91d7d0SVladimir Kondratiev 	wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
9578e52fe30SHamad Kadmany 	wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
9588e52fe30SHamad Kadmany 
9598e52fe30SHamad Kadmany 	for (i = 0; i < request->n_ssids; i++) {
9608e52fe30SHamad Kadmany 		wil_dbg_misc(wil, "SSID[%d]", i);
9615eb443e9SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
9628e52fe30SHamad Kadmany 				  request->ssids[i].ssid,
9635eb443e9SDedy Lansky 				  request->ssids[i].ssid_len, true);
9648e52fe30SHamad Kadmany 	}
9658e52fe30SHamad Kadmany 
9668e52fe30SHamad Kadmany 	if (request->n_ssids)
967e00243faSLior David 		rc = wmi_set_ssid(vif, request->ssids[0].ssid_len,
9688e52fe30SHamad Kadmany 				  request->ssids[0].ssid);
9698e52fe30SHamad Kadmany 	else
970e00243faSLior David 		rc = wmi_set_ssid(vif, 0, NULL);
9718e52fe30SHamad Kadmany 
9728e52fe30SHamad Kadmany 	if (rc) {
9738e52fe30SHamad Kadmany 		wil_err(wil, "set SSID for scan request failed: %d\n", rc);
974bb6743f7SLior David 		goto out;
9758e52fe30SHamad Kadmany 	}
9768e52fe30SHamad Kadmany 
977e00243faSLior David 	vif->scan_request = request;
978e00243faSLior David 	mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO);
9792be7d22fSVladimir Kondratiev 
9802be7d22fSVladimir Kondratiev 	memset(&cmd, 0, sizeof(cmd));
98174997a53SLior David 	cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
9822be7d22fSVladimir Kondratiev 	cmd.cmd.num_channels = 0;
9832be7d22fSVladimir Kondratiev 	n = min(request->n_channels, 4U);
9842be7d22fSVladimir Kondratiev 	for (i = 0; i < n; i++) {
9852be7d22fSVladimir Kondratiev 		int ch = request->channels[i]->hw_value;
9868fe59627SVladimir Kondratiev 
9872be7d22fSVladimir Kondratiev 		if (ch == 0) {
9882be7d22fSVladimir Kondratiev 			wil_err(wil,
9892be7d22fSVladimir Kondratiev 				"Scan requested for unknown frequency %dMhz\n",
9902be7d22fSVladimir Kondratiev 				request->channels[i]->center_freq);
9912be7d22fSVladimir Kondratiev 			continue;
9922be7d22fSVladimir Kondratiev 		}
9932be7d22fSVladimir Kondratiev 		/* 0-based channel indexes */
9942be7d22fSVladimir Kondratiev 		cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
9957743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "Scan for ch %d  : %d MHz\n", ch,
9962be7d22fSVladimir Kondratiev 			     request->channels[i]->center_freq);
9972be7d22fSVladimir Kondratiev 	}
9982be7d22fSVladimir Kondratiev 
99977c91295SVladimir Kondratiev 	if (request->ie_len)
10005eb443e9SDedy Lansky 		wil_hex_dump_misc("Scan IE ", DUMP_PREFIX_OFFSET, 16, 1,
10015eb443e9SDedy Lansky 				  request->ie, request->ie_len, true);
100277c91295SVladimir Kondratiev 	else
100377c91295SVladimir Kondratiev 		wil_dbg_misc(wil, "Scan has no IE's\n");
100477c91295SVladimir Kondratiev 
1005e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
1006e00243faSLior David 			request->ie_len, request->ie);
10075421bf0cSVladimir Kondratiev 	if (rc)
1008bb6743f7SLior David 		goto out_restore;
100977c91295SVladimir Kondratiev 
101074997a53SLior David 	if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) {
101174997a53SLior David 		cmd.cmd.discovery_mode = 1;
101274997a53SLior David 		wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
101374997a53SLior David 	}
101474997a53SLior David 
1015e00243faSLior David 	if (vif->mid == 0)
10164332cac1SLior David 		wil->radio_wdev = wdev;
1017e00243faSLior David 	rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid,
1018e00243faSLior David 		      &cmd, sizeof(cmd.cmd) +
10192be7d22fSVladimir Kondratiev 		      cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
1020ed6f9dc6SVladimir Kondratiev 
1021bb6743f7SLior David out_restore:
1022a2142086SVladimir Kondratiev 	if (rc) {
1023e00243faSLior David 		del_timer_sync(&vif->scan_timer);
1024e00243faSLior David 		if (vif->mid == 0)
1025e00243faSLior David 			wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
1026e00243faSLior David 		vif->scan_request = NULL;
1027a2142086SVladimir Kondratiev 	}
1028bb6743f7SLior David out:
1029bb6743f7SLior David 	mutex_unlock(&wil->mutex);
1030ed6f9dc6SVladimir Kondratiev 	return rc;
10312be7d22fSVladimir Kondratiev }
10322be7d22fSVladimir Kondratiev 
wil_cfg80211_abort_scan(struct wiphy * wiphy,struct wireless_dev * wdev)1033035859a5SMaya Erez static void wil_cfg80211_abort_scan(struct wiphy *wiphy,
1034035859a5SMaya Erez 				    struct wireless_dev *wdev)
1035035859a5SMaya Erez {
1036035859a5SMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1037e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
1038035859a5SMaya Erez 
1039035859a5SMaya Erez 	wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
1040035859a5SMaya Erez 
1041035859a5SMaya Erez 	mutex_lock(&wil->mutex);
1042404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
1043035859a5SMaya Erez 
1044e00243faSLior David 	if (!vif->scan_request)
1045035859a5SMaya Erez 		goto out;
1046035859a5SMaya Erez 
1047e00243faSLior David 	if (wdev != vif->scan_request->wdev) {
1048035859a5SMaya Erez 		wil_dbg_misc(wil, "abort scan was called on the wrong iface\n");
1049035859a5SMaya Erez 		goto out;
1050035859a5SMaya Erez 	}
1051035859a5SMaya Erez 
1052e00243faSLior David 	if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev)
1053035859a5SMaya Erez 		wil_p2p_stop_radio_operations(wil);
1054035859a5SMaya Erez 	else
1055e00243faSLior David 		wil_abort_scan(vif, true);
1056035859a5SMaya Erez 
1057035859a5SMaya Erez out:
1058404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
1059035859a5SMaya Erez 	mutex_unlock(&wil->mutex);
1060035859a5SMaya Erez }
1061035859a5SMaya Erez 
wil_print_crypto(struct wil6210_priv * wil,struct cfg80211_crypto_settings * c)1062feeac225SVladimir Kondratiev static void wil_print_crypto(struct wil6210_priv *wil,
1063feeac225SVladimir Kondratiev 			     struct cfg80211_crypto_settings *c)
1064feeac225SVladimir Kondratiev {
1065feeac225SVladimir Kondratiev 	int i, n;
1066feeac225SVladimir Kondratiev 
1067feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
1068feeac225SVladimir Kondratiev 		     c->wpa_versions, c->cipher_group);
1069feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise);
1070feeac225SVladimir Kondratiev 	n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise));
1071feeac225SVladimir Kondratiev 	for (i = 0; i < n; i++)
1072feeac225SVladimir Kondratiev 		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
1073feeac225SVladimir Kondratiev 			     c->ciphers_pairwise[i]);
1074feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "}\n");
1075feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites);
1076feeac225SVladimir Kondratiev 	n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites));
1077feeac225SVladimir Kondratiev 	for (i = 0; i < n; i++)
1078feeac225SVladimir Kondratiev 		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
1079feeac225SVladimir Kondratiev 			     c->akm_suites[i]);
1080feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "}\n");
1081feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
1082feeac225SVladimir Kondratiev 		     c->control_port, be16_to_cpu(c->control_port_ethertype),
1083feeac225SVladimir Kondratiev 		     c->control_port_no_encrypt);
1084feeac225SVladimir Kondratiev }
1085feeac225SVladimir Kondratiev 
1086b9010f10SAhmad Masri static const char *
wil_get_auth_type_name(enum nl80211_auth_type auth_type)1087b9010f10SAhmad Masri wil_get_auth_type_name(enum nl80211_auth_type auth_type)
1088b9010f10SAhmad Masri {
1089b9010f10SAhmad Masri 	switch (auth_type) {
1090b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_OPEN_SYSTEM:
1091b9010f10SAhmad Masri 		return "OPEN_SYSTEM";
1092b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_SHARED_KEY:
1093b9010f10SAhmad Masri 		return "SHARED_KEY";
1094b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_FT:
1095b9010f10SAhmad Masri 		return "FT";
1096b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_NETWORK_EAP:
1097b9010f10SAhmad Masri 		return "NETWORK_EAP";
1098b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_SAE:
1099b9010f10SAhmad Masri 		return "SAE";
1100b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_AUTOMATIC:
1101b9010f10SAhmad Masri 		return "AUTOMATIC";
1102b9010f10SAhmad Masri 	default:
1103b9010f10SAhmad Masri 		return "unknown";
1104b9010f10SAhmad Masri 	}
1105b9010f10SAhmad Masri }
wil_print_connect_params(struct wil6210_priv * wil,struct cfg80211_connect_params * sme)11068ca26163SVladimir Kondratiev static void wil_print_connect_params(struct wil6210_priv *wil,
11078ca26163SVladimir Kondratiev 				     struct cfg80211_connect_params *sme)
11088ca26163SVladimir Kondratiev {
11098ca26163SVladimir Kondratiev 	wil_info(wil, "Connecting to:\n");
11108ca26163SVladimir Kondratiev 	if (sme->channel) {
11118ca26163SVladimir Kondratiev 		wil_info(wil, "  Channel: %d freq %d\n",
11128ca26163SVladimir Kondratiev 			 sme->channel->hw_value, sme->channel->center_freq);
11138ca26163SVladimir Kondratiev 	}
11148ca26163SVladimir Kondratiev 	if (sme->bssid)
11158ca26163SVladimir Kondratiev 		wil_info(wil, "  BSSID: %pM\n", sme->bssid);
11168ca26163SVladimir Kondratiev 	if (sme->ssid)
11178ca26163SVladimir Kondratiev 		print_hex_dump(KERN_INFO, "  SSID: ", DUMP_PREFIX_OFFSET,
11188ca26163SVladimir Kondratiev 			       16, 1, sme->ssid, sme->ssid_len, true);
1119b9010f10SAhmad Masri 	if (sme->prev_bssid)
1120b9010f10SAhmad Masri 		wil_info(wil, "  Previous BSSID=%pM\n", sme->prev_bssid);
1121b9010f10SAhmad Masri 	wil_info(wil, "  Auth Type: %s\n",
1122b9010f10SAhmad Masri 		 wil_get_auth_type_name(sme->auth_type));
11238ca26163SVladimir Kondratiev 	wil_info(wil, "  Privacy: %s\n", sme->privacy ? "secure" : "open");
1124eabb03b4SLior David 	wil_info(wil, "  PBSS: %d\n", sme->pbss);
1125feeac225SVladimir Kondratiev 	wil_print_crypto(wil, &sme->crypto);
11268ca26163SVladimir Kondratiev }
11278ca26163SVladimir Kondratiev 
wil_ft_connect(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme)1128b9010f10SAhmad Masri static int wil_ft_connect(struct wiphy *wiphy,
1129b9010f10SAhmad Masri 			  struct net_device *ndev,
1130b9010f10SAhmad Masri 			  struct cfg80211_connect_params *sme)
1131b9010f10SAhmad Masri {
1132b9010f10SAhmad Masri 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1133b9010f10SAhmad Masri 	struct wil6210_vif *vif = ndev_to_vif(ndev);
1134b9010f10SAhmad Masri 	struct wmi_ft_auth_cmd auth_cmd;
1135b9010f10SAhmad Masri 	int rc;
1136b9010f10SAhmad Masri 
1137b9010f10SAhmad Masri 	if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
1138b9010f10SAhmad Masri 		wil_err(wil, "FT: FW does not support FT roaming\n");
1139b9010f10SAhmad Masri 		return -EOPNOTSUPP;
1140b9010f10SAhmad Masri 	}
1141b9010f10SAhmad Masri 
1142b9010f10SAhmad Masri 	if (!sme->prev_bssid) {
1143b9010f10SAhmad Masri 		wil_err(wil, "FT: prev_bssid was not set\n");
1144b9010f10SAhmad Masri 		return -EINVAL;
1145b9010f10SAhmad Masri 	}
1146b9010f10SAhmad Masri 
1147b9010f10SAhmad Masri 	if (ether_addr_equal(sme->prev_bssid, sme->bssid)) {
1148b9010f10SAhmad Masri 		wil_err(wil, "FT: can not roam to same AP\n");
1149b9010f10SAhmad Masri 		return -EINVAL;
1150b9010f10SAhmad Masri 	}
1151b9010f10SAhmad Masri 
1152b9010f10SAhmad Masri 	if (!test_bit(wil_vif_fwconnected, vif->status)) {
1153b9010f10SAhmad Masri 		wil_err(wil, "FT: roam while not connected\n");
1154b9010f10SAhmad Masri 		return -EINVAL;
1155b9010f10SAhmad Masri 	}
1156b9010f10SAhmad Masri 
1157b9010f10SAhmad Masri 	if (vif->privacy != sme->privacy) {
1158b9010f10SAhmad Masri 		wil_err(wil, "FT: privacy mismatch, current (%d) roam (%d)\n",
1159b9010f10SAhmad Masri 			vif->privacy, sme->privacy);
1160b9010f10SAhmad Masri 		return -EINVAL;
1161b9010f10SAhmad Masri 	}
1162b9010f10SAhmad Masri 
1163b9010f10SAhmad Masri 	if (sme->pbss) {
1164b9010f10SAhmad Masri 		wil_err(wil, "FT: roam is not valid for PBSS\n");
1165b9010f10SAhmad Masri 		return -EINVAL;
1166b9010f10SAhmad Masri 	}
1167b9010f10SAhmad Masri 
1168b9010f10SAhmad Masri 	memset(&auth_cmd, 0, sizeof(auth_cmd));
1169b9010f10SAhmad Masri 	auth_cmd.channel = sme->channel->hw_value - 1;
1170b9010f10SAhmad Masri 	ether_addr_copy(auth_cmd.bssid, sme->bssid);
1171b9010f10SAhmad Masri 
1172b9010f10SAhmad Masri 	wil_info(wil, "FT: roaming\n");
1173b9010f10SAhmad Masri 
1174b9010f10SAhmad Masri 	set_bit(wil_vif_ft_roam, vif->status);
1175b9010f10SAhmad Masri 	rc = wmi_send(wil, WMI_FT_AUTH_CMDID, vif->mid,
1176b9010f10SAhmad Masri 		      &auth_cmd, sizeof(auth_cmd));
1177b9010f10SAhmad Masri 	if (rc == 0)
1178b9010f10SAhmad Masri 		mod_timer(&vif->connect_timer,
1179b9010f10SAhmad Masri 			  jiffies + msecs_to_jiffies(5000));
1180b9010f10SAhmad Masri 	else
1181b9010f10SAhmad Masri 		clear_bit(wil_vif_ft_roam, vif->status);
1182b9010f10SAhmad Masri 
1183b9010f10SAhmad Masri 	return rc;
1184b9010f10SAhmad Masri }
1185b9010f10SAhmad Masri 
wil_get_wmi_edmg_channel(struct wil6210_priv * wil,u8 edmg_bw_config,u8 edmg_channels,u8 * wmi_ch)11869abe3e30SAlexei Avshalom Lazar static int wil_get_wmi_edmg_channel(struct wil6210_priv *wil, u8 edmg_bw_config,
11879abe3e30SAlexei Avshalom Lazar 				    u8 edmg_channels, u8 *wmi_ch)
11889abe3e30SAlexei Avshalom Lazar {
11899abe3e30SAlexei Avshalom Lazar 	if (!edmg_bw_config) {
11909abe3e30SAlexei Avshalom Lazar 		*wmi_ch = 0;
11919abe3e30SAlexei Avshalom Lazar 		return 0;
11929abe3e30SAlexei Avshalom Lazar 	} else if (edmg_bw_config == WIL_EDMG_BW_CONFIGURATION) {
11939abe3e30SAlexei Avshalom Lazar 		/* convert from edmg channel bitmap into edmg channel number */
11949abe3e30SAlexei Avshalom Lazar 		switch (edmg_channels) {
11959abe3e30SAlexei Avshalom Lazar 		case WIL_EDMG_CHANNEL_9_SUBCHANNELS:
11969abe3e30SAlexei Avshalom Lazar 			return wil_spec2wmi_ch(9, wmi_ch);
11979abe3e30SAlexei Avshalom Lazar 		case WIL_EDMG_CHANNEL_10_SUBCHANNELS:
11989abe3e30SAlexei Avshalom Lazar 			return wil_spec2wmi_ch(10, wmi_ch);
11999abe3e30SAlexei Avshalom Lazar 		case WIL_EDMG_CHANNEL_11_SUBCHANNELS:
12009abe3e30SAlexei Avshalom Lazar 			return wil_spec2wmi_ch(11, wmi_ch);
12019abe3e30SAlexei Avshalom Lazar 		default:
12029abe3e30SAlexei Avshalom Lazar 			wil_err(wil, "Unsupported edmg channel bitmap 0x%x\n",
12039abe3e30SAlexei Avshalom Lazar 				edmg_channels);
12049abe3e30SAlexei Avshalom Lazar 			return -EINVAL;
12059abe3e30SAlexei Avshalom Lazar 		}
12069abe3e30SAlexei Avshalom Lazar 	} else {
12079abe3e30SAlexei Avshalom Lazar 		wil_err(wil, "Unsupported EDMG BW configuration %d\n",
12089abe3e30SAlexei Avshalom Lazar 			edmg_bw_config);
12099abe3e30SAlexei Avshalom Lazar 		return -EINVAL;
12109abe3e30SAlexei Avshalom Lazar 	}
12119abe3e30SAlexei Avshalom Lazar }
12129abe3e30SAlexei Avshalom Lazar 
wil_cfg80211_connect(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme)12132be7d22fSVladimir Kondratiev static int wil_cfg80211_connect(struct wiphy *wiphy,
12142be7d22fSVladimir Kondratiev 				struct net_device *ndev,
12152be7d22fSVladimir Kondratiev 				struct cfg80211_connect_params *sme)
12162be7d22fSVladimir Kondratiev {
12172be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1218e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
12192be7d22fSVladimir Kondratiev 	struct cfg80211_bss *bss;
12202be7d22fSVladimir Kondratiev 	struct wmi_connect_cmd conn;
12212be7d22fSVladimir Kondratiev 	const u8 *ssid_eid;
12222be7d22fSVladimir Kondratiev 	const u8 *rsn_eid;
12232be7d22fSVladimir Kondratiev 	int ch;
12242be7d22fSVladimir Kondratiev 	int rc = 0;
1225b9010f10SAhmad Masri 	bool is_ft_roam = false;
1226b9010f10SAhmad Masri 	u8 network_type;
1227eabb03b4SLior David 	enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
12282be7d22fSVladimir Kondratiev 
1229e00243faSLior David 	wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
1230344a7024SVladimir Kondratiev 	wil_print_connect_params(wil, sme);
1231344a7024SVladimir Kondratiev 
1232b9010f10SAhmad Masri 	if (sme->auth_type == NL80211_AUTHTYPE_FT)
1233b9010f10SAhmad Masri 		is_ft_roam = true;
1234b9010f10SAhmad Masri 	if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC &&
1235b9010f10SAhmad Masri 	    test_bit(wil_vif_fwconnected, vif->status))
1236b9010f10SAhmad Masri 		is_ft_roam = true;
1237b9010f10SAhmad Masri 
1238b9010f10SAhmad Masri 	if (!is_ft_roam)
12395bd60982SLior David 		if (test_bit(wil_vif_fwconnecting, vif->status) ||
12405bd60982SLior David 		    test_bit(wil_vif_fwconnected, vif->status))
12414cd9e837SVladimir Kondratiev 			return -EALREADY;
12424cd9e837SVladimir Kondratiev 
1243344a7024SVladimir Kondratiev 	if (sme->ie_len > WMI_MAX_IE_LEN) {
1244344a7024SVladimir Kondratiev 		wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len);
1245344a7024SVladimir Kondratiev 		return -ERANGE;
1246344a7024SVladimir Kondratiev 	}
1247344a7024SVladimir Kondratiev 
1248344a7024SVladimir Kondratiev 	rsn_eid = sme->ie ?
1249344a7024SVladimir Kondratiev 			cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
1250344a7024SVladimir Kondratiev 			NULL;
1251b9010f10SAhmad Masri 	if (sme->privacy && !rsn_eid) {
125227aa6b71SVladimir Kondratiev 		wil_info(wil, "WSC connection\n");
1253b9010f10SAhmad Masri 		if (is_ft_roam) {
1254b9010f10SAhmad Masri 			wil_err(wil, "No WSC with FT roam\n");
1255b9010f10SAhmad Masri 			return -EINVAL;
1256b9010f10SAhmad Masri 		}
1257b9010f10SAhmad Masri 	}
12588ca26163SVladimir Kondratiev 
1259eabb03b4SLior David 	if (sme->pbss)
1260eabb03b4SLior David 		bss_type = IEEE80211_BSS_TYPE_PBSS;
126134d50519SLior David 
12622be7d22fSVladimir Kondratiev 	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
12632be7d22fSVladimir Kondratiev 			       sme->ssid, sme->ssid_len,
1264eabb03b4SLior David 			       bss_type, IEEE80211_PRIVACY_ANY);
12652be7d22fSVladimir Kondratiev 	if (!bss) {
12662be7d22fSVladimir Kondratiev 		wil_err(wil, "Unable to find BSS\n");
12672be7d22fSVladimir Kondratiev 		return -ENOENT;
12682be7d22fSVladimir Kondratiev 	}
12692be7d22fSVladimir Kondratiev 
12702be7d22fSVladimir Kondratiev 	ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
12712be7d22fSVladimir Kondratiev 	if (!ssid_eid) {
12722be7d22fSVladimir Kondratiev 		wil_err(wil, "No SSID\n");
12732be7d22fSVladimir Kondratiev 		rc = -ENOENT;
12742be7d22fSVladimir Kondratiev 		goto out;
12752be7d22fSVladimir Kondratiev 	}
1276e00243faSLior David 	vif->privacy = sme->privacy;
1277e00243faSLior David 	vif->pbss = sme->pbss;
12782be7d22fSVladimir Kondratiev 
1279b9010f10SAhmad Masri 	rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
1280b9010f10SAhmad Masri 	if (rc)
1281b9010f10SAhmad Masri 		goto out;
1282b9010f10SAhmad Masri 
1283b9010f10SAhmad Masri 	switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
1284b9010f10SAhmad Masri 	case WLAN_CAPABILITY_DMG_TYPE_AP:
1285b9010f10SAhmad Masri 		network_type = WMI_NETTYPE_INFRA;
1286b9010f10SAhmad Masri 		break;
1287b9010f10SAhmad Masri 	case WLAN_CAPABILITY_DMG_TYPE_PBSS:
1288b9010f10SAhmad Masri 		network_type = WMI_NETTYPE_P2P;
1289b9010f10SAhmad Masri 		break;
1290b9010f10SAhmad Masri 	default:
1291b9010f10SAhmad Masri 		wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
1292b9010f10SAhmad Masri 			bss->capability);
1293b9010f10SAhmad Masri 		rc = -EINVAL;
1294b9010f10SAhmad Masri 		goto out;
1295b9010f10SAhmad Masri 	}
1296b9010f10SAhmad Masri 
1297b9010f10SAhmad Masri 	ch = bss->channel->hw_value;
1298b9010f10SAhmad Masri 	if (ch == 0) {
1299b9010f10SAhmad Masri 		wil_err(wil, "BSS at unknown frequency %dMhz\n",
1300b9010f10SAhmad Masri 			bss->channel->center_freq);
1301b9010f10SAhmad Masri 		rc = -EOPNOTSUPP;
1302b9010f10SAhmad Masri 		goto out;
1303b9010f10SAhmad Masri 	}
1304b9010f10SAhmad Masri 
1305b9010f10SAhmad Masri 	if (is_ft_roam) {
1306b9010f10SAhmad Masri 		if (network_type != WMI_NETTYPE_INFRA) {
1307b9010f10SAhmad Masri 			wil_err(wil, "FT: Unsupported BSS type, capability= 0x%04x\n",
1308b9010f10SAhmad Masri 				bss->capability);
1309b9010f10SAhmad Masri 			rc = -EINVAL;
1310b9010f10SAhmad Masri 			goto out;
1311b9010f10SAhmad Masri 		}
1312b9010f10SAhmad Masri 		rc = wil_ft_connect(wiphy, ndev, sme);
1313b9010f10SAhmad Masri 		if (rc == 0)
1314b9010f10SAhmad Masri 			vif->bss = bss;
1315b9010f10SAhmad Masri 		goto out;
1316b9010f10SAhmad Masri 	}
1317b9010f10SAhmad Masri 
1318e00243faSLior David 	if (vif->privacy) {
1319230d8442SVladimir Kondratiev 		/* For secure assoc, remove old keys */
1320e00243faSLior David 		rc = wmi_del_cipher_key(vif, 0, bss->bssid,
1321230d8442SVladimir Kondratiev 					WMI_KEY_USE_PAIRWISE);
13222be7d22fSVladimir Kondratiev 		if (rc) {
1323230d8442SVladimir Kondratiev 			wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
1324230d8442SVladimir Kondratiev 			goto out;
1325230d8442SVladimir Kondratiev 		}
1326e00243faSLior David 		rc = wmi_del_cipher_key(vif, 0, bss->bssid,
1327230d8442SVladimir Kondratiev 					WMI_KEY_USE_RX_GROUP);
1328230d8442SVladimir Kondratiev 		if (rc) {
1329230d8442SVladimir Kondratiev 			wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
13302be7d22fSVladimir Kondratiev 			goto out;
13312be7d22fSVladimir Kondratiev 		}
1332ac4acdb7SVladimir Kondratiev 	}
1333ac4acdb7SVladimir Kondratiev 
13342be7d22fSVladimir Kondratiev 	/* WMI_CONNECT_CMD */
13352be7d22fSVladimir Kondratiev 	memset(&conn, 0, sizeof(conn));
1336b9010f10SAhmad Masri 	conn.network_type = network_type;
1337e00243faSLior David 	if (vif->privacy) {
133827aa6b71SVladimir Kondratiev 		if (rsn_eid) { /* regular secure connection */
13392be7d22fSVladimir Kondratiev 			conn.dot11_auth_mode = WMI_AUTH11_SHARED;
13402be7d22fSVladimir Kondratiev 			conn.auth_mode = WMI_AUTH_WPA2_PSK;
13412be7d22fSVladimir Kondratiev 			conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
13422be7d22fSVladimir Kondratiev 			conn.pairwise_crypto_len = 16;
1343230d8442SVladimir Kondratiev 			conn.group_crypto_type = WMI_CRYPT_AES_GCMP;
1344230d8442SVladimir Kondratiev 			conn.group_crypto_len = 16;
134527aa6b71SVladimir Kondratiev 		} else { /* WSC */
134627aa6b71SVladimir Kondratiev 			conn.dot11_auth_mode = WMI_AUTH11_WSC;
134727aa6b71SVladimir Kondratiev 			conn.auth_mode = WMI_AUTH_NONE;
134827aa6b71SVladimir Kondratiev 		}
134927aa6b71SVladimir Kondratiev 	} else { /* insecure connection */
13502be7d22fSVladimir Kondratiev 		conn.dot11_auth_mode = WMI_AUTH11_OPEN;
13512be7d22fSVladimir Kondratiev 		conn.auth_mode = WMI_AUTH_NONE;
13522be7d22fSVladimir Kondratiev 	}
13532be7d22fSVladimir Kondratiev 
13542be7d22fSVladimir Kondratiev 	conn.ssid_len = min_t(u8, ssid_eid[1], 32);
13552be7d22fSVladimir Kondratiev 	memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
13562be7d22fSVladimir Kondratiev 	conn.channel = ch - 1;
13572be7d22fSVladimir Kondratiev 
13589abe3e30SAlexei Avshalom Lazar 	rc = wil_get_wmi_edmg_channel(wil, sme->edmg.bw_config,
13599abe3e30SAlexei Avshalom Lazar 				      sme->edmg.channels, &conn.edmg_channel);
13609abe3e30SAlexei Avshalom Lazar 	if (rc < 0)
13619abe3e30SAlexei Avshalom Lazar 		return rc;
13629abe3e30SAlexei Avshalom Lazar 
1363a82553bbSVladimir Kondratiev 	ether_addr_copy(conn.bssid, bss->bssid);
1364a82553bbSVladimir Kondratiev 	ether_addr_copy(conn.dst_mac, bss->bssid);
1365e83eb2fcSVladimir Kondratiev 
13665bd60982SLior David 	set_bit(wil_vif_fwconnecting, vif->status);
13672be7d22fSVladimir Kondratiev 
1368e00243faSLior David 	rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn));
13692be7d22fSVladimir Kondratiev 	if (rc == 0) {
1370c5e96c91SDedy Lansky 		netif_carrier_on(ndev);
13715bd60982SLior David 		if (!wil_has_other_active_ifaces(wil, ndev, false, true))
13729953a782SLior David 			wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
1373e00243faSLior David 		vif->bss = bss;
13742be7d22fSVladimir Kondratiev 		/* Connect can take lots of time */
1375e00243faSLior David 		mod_timer(&vif->connect_timer,
1376d83ad4c4SLior David 			  jiffies + msecs_to_jiffies(5000));
1377b338f74eSVladimir Kondratiev 	} else {
13785bd60982SLior David 		clear_bit(wil_vif_fwconnecting, vif->status);
13792be7d22fSVladimir Kondratiev 	}
13802be7d22fSVladimir Kondratiev 
13812be7d22fSVladimir Kondratiev  out:
13825b112d3dSJohannes Berg 	cfg80211_put_bss(wiphy, bss);
13832be7d22fSVladimir Kondratiev 
13842be7d22fSVladimir Kondratiev 	return rc;
13852be7d22fSVladimir Kondratiev }
13862be7d22fSVladimir Kondratiev 
wil_cfg80211_disconnect(struct wiphy * wiphy,struct net_device * ndev,u16 reason_code)13872be7d22fSVladimir Kondratiev static int wil_cfg80211_disconnect(struct wiphy *wiphy,
13882be7d22fSVladimir Kondratiev 				   struct net_device *ndev,
13892be7d22fSVladimir Kondratiev 				   u16 reason_code)
13902be7d22fSVladimir Kondratiev {
13912be7d22fSVladimir Kondratiev 	int rc;
13922be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1393e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
13942be7d22fSVladimir Kondratiev 
1395e00243faSLior David 	wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n",
1396e00243faSLior David 		     reason_code, vif->mid);
1397de9084efSVladimir Kondratiev 
13985bd60982SLior David 	if (!(test_bit(wil_vif_fwconnecting, vif->status) ||
13995bd60982SLior David 	      test_bit(wil_vif_fwconnected, vif->status))) {
1400af3db60aSLazar Alexei 		wil_err(wil, "Disconnect was called while disconnected\n");
140178771d76SVladimir Kondratiev 		return 0;
140278771d76SVladimir Kondratiev 	}
140378771d76SVladimir Kondratiev 
1404e00243faSLior David 	vif->locally_generated_disc = true;
1405e00243faSLior David 	rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0,
140678771d76SVladimir Kondratiev 		      WMI_DISCONNECT_EVENTID, NULL, 0,
140778771d76SVladimir Kondratiev 		      WIL6210_DISCONNECT_TO_MS);
140878771d76SVladimir Kondratiev 	if (rc)
1409af3db60aSLazar Alexei 		wil_err(wil, "disconnect error %d\n", rc);
14102be7d22fSVladimir Kondratiev 
14112be7d22fSVladimir Kondratiev 	return rc;
14122be7d22fSVladimir Kondratiev }
14132be7d22fSVladimir Kondratiev 
wil_cfg80211_set_wiphy_params(struct wiphy * wiphy,u32 changed)14143fea18d0SLior David static int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
14153fea18d0SLior David {
14163fea18d0SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
14173fea18d0SLior David 	int rc;
14183fea18d0SLior David 
14193fea18d0SLior David 	/* these parameters are explicitly not supported */
14203fea18d0SLior David 	if (changed & (WIPHY_PARAM_RETRY_LONG |
14213fea18d0SLior David 		       WIPHY_PARAM_FRAG_THRESHOLD |
14223fea18d0SLior David 		       WIPHY_PARAM_RTS_THRESHOLD))
14233fea18d0SLior David 		return -ENOTSUPP;
14243fea18d0SLior David 
14253fea18d0SLior David 	if (changed & WIPHY_PARAM_RETRY_SHORT) {
14263fea18d0SLior David 		rc = wmi_set_mgmt_retry(wil, wiphy->retry_short);
14273fea18d0SLior David 		if (rc)
14283fea18d0SLior David 			return rc;
14293fea18d0SLior David 	}
14303fea18d0SLior David 
14313fea18d0SLior David 	return 0;
14323fea18d0SLior David }
14333fea18d0SLior David 
wil_cfg80211_mgmt_tx(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_mgmt_tx_params * params,u64 * cookie)14340b39aaf2SVladimir Kondratiev int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
14351647f12fSVladimir Kondratiev 			 struct cfg80211_mgmt_tx_params *params,
14361647f12fSVladimir Kondratiev 			 u64 *cookie)
14371647f12fSVladimir Kondratiev {
14381647f12fSVladimir Kondratiev 	const u8 *buf = params->buf;
14391c21cc5fSDedy Lansky 	size_t len = params->len;
14401647f12fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1441e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
14421647f12fSVladimir Kondratiev 	int rc;
14431c21cc5fSDedy Lansky 	bool tx_status;
14441647f12fSVladimir Kondratiev 
1445b698e2dfSAhmad Masri 	wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
1446b698e2dfSAhmad Masri 		     params->chan ? params->chan->hw_value : -1,
1447b698e2dfSAhmad Masri 		     params->offchan,
1448b698e2dfSAhmad Masri 		     params->wait);
1449b698e2dfSAhmad Masri 
1450b698e2dfSAhmad Masri 	/* Note, currently we support the "wait" parameter only on AP mode.
1451b698e2dfSAhmad Masri 	 * In other modes, user-space must call remain_on_channel before
1452b698e2dfSAhmad Masri 	 * mgmt_tx or listen on a channel other than active one.
1453e6d68341SDedy Lansky 	 */
1454e6d68341SDedy Lansky 
1455b698e2dfSAhmad Masri 	if (params->chan && params->chan->hw_value == 0) {
1456b698e2dfSAhmad Masri 		wil_err(wil, "invalid channel\n");
1457b698e2dfSAhmad Masri 		return -EINVAL;
1458b698e2dfSAhmad Masri 	}
1459e6d68341SDedy Lansky 
1460b698e2dfSAhmad Masri 	if (wdev->iftype != NL80211_IFTYPE_AP) {
1461b698e2dfSAhmad Masri 		wil_dbg_misc(wil,
1462b698e2dfSAhmad Masri 			     "send WMI_SW_TX_REQ_CMDID on non-AP interfaces\n");
1463b698e2dfSAhmad Masri 		rc = wmi_mgmt_tx(vif, buf, len);
1464b698e2dfSAhmad Masri 		goto out;
1465b698e2dfSAhmad Masri 	}
1466b698e2dfSAhmad Masri 
1467b698e2dfSAhmad Masri 	if (!params->chan || params->chan->hw_value == vif->channel) {
1468b698e2dfSAhmad Masri 		wil_dbg_misc(wil,
1469b698e2dfSAhmad Masri 			     "send WMI_SW_TX_REQ_CMDID for on-channel\n");
1470b698e2dfSAhmad Masri 		rc = wmi_mgmt_tx(vif, buf, len);
1471b698e2dfSAhmad Masri 		goto out;
1472b698e2dfSAhmad Masri 	}
1473b698e2dfSAhmad Masri 
1474b698e2dfSAhmad Masri 	if (params->offchan == 0) {
1475b698e2dfSAhmad Masri 		wil_err(wil,
1476b698e2dfSAhmad Masri 			"invalid channel params: current %d requested %d, off-channel not allowed\n",
1477b698e2dfSAhmad Masri 			vif->channel, params->chan->hw_value);
1478b698e2dfSAhmad Masri 		return -EBUSY;
1479b698e2dfSAhmad Masri 	}
1480b698e2dfSAhmad Masri 
1481b698e2dfSAhmad Masri 	/* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
1482b698e2dfSAhmad Masri 	rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
1483b698e2dfSAhmad Masri 			     params->wait);
1484b698e2dfSAhmad Masri 
1485b698e2dfSAhmad Masri out:
148649122ec4SLior David 	/* when the sent packet was not acked by receiver(ACK=0), rc will
148749122ec4SLior David 	 * be -EAGAIN. In this case this function needs to return success,
148849122ec4SLior David 	 * the ACK=0 will be reflected in tx_status.
148949122ec4SLior David 	 */
1490b698e2dfSAhmad Masri 	tx_status = (rc == 0);
149149122ec4SLior David 	rc = (rc == -EAGAIN) ? 0 : rc;
1492304464f4SVladimir Kondratiev 	cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
1493304464f4SVladimir Kondratiev 				tx_status, GFP_KERNEL);
1494b698e2dfSAhmad Masri 
14951647f12fSVladimir Kondratiev 	return rc;
14961647f12fSVladimir Kondratiev }
14971647f12fSVladimir Kondratiev 
wil_cfg80211_set_channel(struct wiphy * wiphy,struct cfg80211_chan_def * chandef)14982be7d22fSVladimir Kondratiev static int wil_cfg80211_set_channel(struct wiphy *wiphy,
14992be7d22fSVladimir Kondratiev 				    struct cfg80211_chan_def *chandef)
15002be7d22fSVladimir Kondratiev {
15012be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
15022be7d22fSVladimir Kondratiev 
15037d3e4dbeSLior David 	wil->monitor_chandef = *chandef;
15042be7d22fSVladimir Kondratiev 
15052be7d22fSVladimir Kondratiev 	return 0;
15062be7d22fSVladimir Kondratiev }
15072be7d22fSVladimir Kondratiev 
wil_detect_key_usage(struct wireless_dev * wdev,bool pairwise)1508e00243faSLior David static enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev,
1509230d8442SVladimir Kondratiev 					       bool pairwise)
1510230d8442SVladimir Kondratiev {
1511e00243faSLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
1512230d8442SVladimir Kondratiev 	enum wmi_key_usage rc;
1513230d8442SVladimir Kondratiev 
1514230d8442SVladimir Kondratiev 	if (pairwise) {
1515230d8442SVladimir Kondratiev 		rc = WMI_KEY_USE_PAIRWISE;
1516230d8442SVladimir Kondratiev 	} else {
1517230d8442SVladimir Kondratiev 		switch (wdev->iftype) {
1518230d8442SVladimir Kondratiev 		case NL80211_IFTYPE_STATION:
1519e6d68341SDedy Lansky 		case NL80211_IFTYPE_P2P_CLIENT:
1520230d8442SVladimir Kondratiev 			rc = WMI_KEY_USE_RX_GROUP;
1521230d8442SVladimir Kondratiev 			break;
1522230d8442SVladimir Kondratiev 		case NL80211_IFTYPE_AP:
1523e6d68341SDedy Lansky 		case NL80211_IFTYPE_P2P_GO:
1524230d8442SVladimir Kondratiev 			rc = WMI_KEY_USE_TX_GROUP;
1525230d8442SVladimir Kondratiev 			break;
1526230d8442SVladimir Kondratiev 		default:
1527230d8442SVladimir Kondratiev 			/* TODO: Rx GTK or Tx GTK? */
1528230d8442SVladimir Kondratiev 			wil_err(wil, "Can't determine GTK type\n");
1529230d8442SVladimir Kondratiev 			rc = WMI_KEY_USE_RX_GROUP;
1530230d8442SVladimir Kondratiev 			break;
1531230d8442SVladimir Kondratiev 		}
1532230d8442SVladimir Kondratiev 	}
1533af3db60aSLazar Alexei 	wil_dbg_misc(wil, "detect_key_usage: -> %s\n", key_usage_str[rc]);
1534230d8442SVladimir Kondratiev 
1535230d8442SVladimir Kondratiev 	return rc;
1536230d8442SVladimir Kondratiev }
1537230d8442SVladimir Kondratiev 
153874b6ac58SMaya Erez static struct wil_sta_info *
wil_find_sta_by_key_usage(struct wil6210_priv * wil,u8 mid,enum wmi_key_usage key_usage,const u8 * mac_addr)1539e00243faSLior David wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid,
154058527421SVladimir Kondratiev 			  enum wmi_key_usage key_usage, const u8 *mac_addr)
154158527421SVladimir Kondratiev {
154258527421SVladimir Kondratiev 	int cid = -EINVAL;
154358527421SVladimir Kondratiev 
154458527421SVladimir Kondratiev 	if (key_usage == WMI_KEY_USE_TX_GROUP)
154558527421SVladimir Kondratiev 		return NULL; /* not needed */
154658527421SVladimir Kondratiev 
154758527421SVladimir Kondratiev 	/* supplicant provides Rx group key in STA mode with NULL MAC address */
154858527421SVladimir Kondratiev 	if (mac_addr)
1549e00243faSLior David 		cid = wil_find_cid(wil, mid, mac_addr);
155058527421SVladimir Kondratiev 	else if (key_usage == WMI_KEY_USE_RX_GROUP)
1551e00243faSLior David 		cid = wil_find_cid_by_idx(wil, mid, 0);
155258527421SVladimir Kondratiev 	if (cid < 0) {
155374b6ac58SMaya Erez 		wil_err(wil, "No CID for %pM %s\n", mac_addr,
155474b6ac58SMaya Erez 			key_usage_str[key_usage]);
155558527421SVladimir Kondratiev 		return ERR_PTR(cid);
155658527421SVladimir Kondratiev 	}
155758527421SVladimir Kondratiev 
155874b6ac58SMaya Erez 	return &wil->sta[cid];
155974b6ac58SMaya Erez }
156058527421SVladimir Kondratiev 
wil_set_crypto_rx(u8 key_index,enum wmi_key_usage key_usage,struct wil_sta_info * cs,struct key_params * params)1561b9010f10SAhmad Masri void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
156274b6ac58SMaya Erez 		       struct wil_sta_info *cs,
156374b6ac58SMaya Erez 		       struct key_params *params)
156474b6ac58SMaya Erez {
156574b6ac58SMaya Erez 	struct wil_tid_crypto_rx_single *cc;
156674b6ac58SMaya Erez 	int tid;
156774b6ac58SMaya Erez 
156874b6ac58SMaya Erez 	if (!cs)
156974b6ac58SMaya Erez 		return;
157074b6ac58SMaya Erez 
157174b6ac58SMaya Erez 	switch (key_usage) {
157242fe1e51SAhmad Masri 	case WMI_KEY_USE_STORE_PTK:
157374b6ac58SMaya Erez 	case WMI_KEY_USE_PAIRWISE:
157474b6ac58SMaya Erez 		for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
157574b6ac58SMaya Erez 			cc = &cs->tid_crypto_rx[tid].key_id[key_index];
157674b6ac58SMaya Erez 			if (params->seq)
157774b6ac58SMaya Erez 				memcpy(cc->pn, params->seq,
157874b6ac58SMaya Erez 				       IEEE80211_GCMP_PN_LEN);
157974b6ac58SMaya Erez 			else
158074b6ac58SMaya Erez 				memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
158174b6ac58SMaya Erez 			cc->key_set = true;
158274b6ac58SMaya Erez 		}
158374b6ac58SMaya Erez 		break;
158474b6ac58SMaya Erez 	case WMI_KEY_USE_RX_GROUP:
158574b6ac58SMaya Erez 		cc = &cs->group_crypto_rx.key_id[key_index];
158674b6ac58SMaya Erez 		if (params->seq)
158774b6ac58SMaya Erez 			memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
158874b6ac58SMaya Erez 		else
158974b6ac58SMaya Erez 			memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
159074b6ac58SMaya Erez 		cc->key_set = true;
159174b6ac58SMaya Erez 		break;
159274b6ac58SMaya Erez 	default:
159374b6ac58SMaya Erez 		break;
159474b6ac58SMaya Erez 	}
159574b6ac58SMaya Erez }
159674b6ac58SMaya Erez 
wil_del_rx_key(u8 key_index,enum wmi_key_usage key_usage,struct wil_sta_info * cs)159774b6ac58SMaya Erez static void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage,
159874b6ac58SMaya Erez 			   struct wil_sta_info *cs)
159974b6ac58SMaya Erez {
160074b6ac58SMaya Erez 	struct wil_tid_crypto_rx_single *cc;
160174b6ac58SMaya Erez 	int tid;
160274b6ac58SMaya Erez 
160374b6ac58SMaya Erez 	if (!cs)
160474b6ac58SMaya Erez 		return;
160574b6ac58SMaya Erez 
160674b6ac58SMaya Erez 	switch (key_usage) {
160774b6ac58SMaya Erez 	case WMI_KEY_USE_PAIRWISE:
160874b6ac58SMaya Erez 		for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
160974b6ac58SMaya Erez 			cc = &cs->tid_crypto_rx[tid].key_id[key_index];
161074b6ac58SMaya Erez 			cc->key_set = false;
161174b6ac58SMaya Erez 		}
161274b6ac58SMaya Erez 		break;
161374b6ac58SMaya Erez 	case WMI_KEY_USE_RX_GROUP:
161474b6ac58SMaya Erez 		cc = &cs->group_crypto_rx.key_id[key_index];
161574b6ac58SMaya Erez 		cc->key_set = false;
161674b6ac58SMaya Erez 		break;
161774b6ac58SMaya Erez 	default:
161874b6ac58SMaya Erez 		break;
161974b6ac58SMaya Erez 	}
162058527421SVladimir Kondratiev }
162158527421SVladimir Kondratiev 
wil_cfg80211_add_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_index,bool pairwise,const u8 * mac_addr,struct key_params * params)16222be7d22fSVladimir Kondratiev static int wil_cfg80211_add_key(struct wiphy *wiphy,
1623*e7a7b84eSVeerendranath Jakkam 				struct net_device *ndev, int link_id,
16242be7d22fSVladimir Kondratiev 				u8 key_index, bool pairwise,
16252be7d22fSVladimir Kondratiev 				const u8 *mac_addr,
16262be7d22fSVladimir Kondratiev 				struct key_params *params)
16272be7d22fSVladimir Kondratiev {
162858527421SVladimir Kondratiev 	int rc;
1629e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
16302be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1631e00243faSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
1632e00243faSLior David 	enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
1633e00243faSLior David 	struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
1634e00243faSLior David 							    key_usage,
163558527421SVladimir Kondratiev 							    mac_addr);
16362be7d22fSVladimir Kondratiev 
163774b6ac58SMaya Erez 	if (!params) {
163874b6ac58SMaya Erez 		wil_err(wil, "NULL params\n");
163974b6ac58SMaya Erez 		return -EINVAL;
164074b6ac58SMaya Erez 	}
164174b6ac58SMaya Erez 
1642af3db60aSLazar Alexei 	wil_dbg_misc(wil, "add_key: %pM %s[%d] PN %*phN\n",
164358527421SVladimir Kondratiev 		     mac_addr, key_usage_str[key_usage], key_index,
164458527421SVladimir Kondratiev 		     params->seq_len, params->seq);
1645db8adcbfSVladimir Kondratiev 
164674b6ac58SMaya Erez 	if (IS_ERR(cs)) {
1647b9010f10SAhmad Masri 		/* in FT, sta info may not be available as add_key may be
1648b9010f10SAhmad Masri 		 * sent by host before FW sends WMI_CONNECT_EVENT
1649b9010f10SAhmad Masri 		 */
1650b9010f10SAhmad Masri 		if (!test_bit(wil_vif_ft_roam, vif->status)) {
1651af3db60aSLazar Alexei 			wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n",
1652af3db60aSLazar Alexei 				mac_addr, key_usage_str[key_usage], key_index,
165358527421SVladimir Kondratiev 				params->seq_len, params->seq);
165458527421SVladimir Kondratiev 			return -EINVAL;
165558527421SVladimir Kondratiev 		}
1656a5f3aed5SWan Jiabing 	} else {
165774b6ac58SMaya Erez 		wil_del_rx_key(key_index, key_usage, cs);
1658a5f3aed5SWan Jiabing 	}
165958527421SVladimir Kondratiev 
166058527421SVladimir Kondratiev 	if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
166158527421SVladimir Kondratiev 		wil_err(wil,
1662af3db60aSLazar Alexei 			"Wrong PN len %d, %pM %s[%d] PN %*phN\n",
1663af3db60aSLazar Alexei 			params->seq_len, mac_addr,
166458527421SVladimir Kondratiev 			key_usage_str[key_usage], key_index,
166558527421SVladimir Kondratiev 			params->seq_len, params->seq);
166658527421SVladimir Kondratiev 		return -EINVAL;
166758527421SVladimir Kondratiev 	}
166858527421SVladimir Kondratiev 
166942fe1e51SAhmad Masri 	spin_lock_bh(&wil->eap_lock);
167042fe1e51SAhmad Masri 	if (pairwise && wdev->iftype == NL80211_IFTYPE_STATION &&
167142fe1e51SAhmad Masri 	    (vif->ptk_rekey_state == WIL_REKEY_M3_RECEIVED ||
167242fe1e51SAhmad Masri 	     vif->ptk_rekey_state == WIL_REKEY_WAIT_M4_SENT)) {
167342fe1e51SAhmad Masri 		key_usage = WMI_KEY_USE_STORE_PTK;
167442fe1e51SAhmad Masri 		vif->ptk_rekey_state = WIL_REKEY_WAIT_M4_SENT;
167542fe1e51SAhmad Masri 		wil_dbg_misc(wil, "Store EAPOL key\n");
167642fe1e51SAhmad Masri 	}
167742fe1e51SAhmad Masri 	spin_unlock_bh(&wil->eap_lock);
167842fe1e51SAhmad Masri 
1679e00243faSLior David 	rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
1680230d8442SVladimir Kondratiev 				params->key, key_usage);
1681e41ab937SDedy Lansky 	if (!rc && !IS_ERR(cs)) {
1682e41ab937SDedy Lansky 		/* update local storage used for AP recovery */
1683e41ab937SDedy Lansky 		if (key_usage == WMI_KEY_USE_TX_GROUP && params->key &&
1684e41ab937SDedy Lansky 		    params->key_len <= WMI_MAX_KEY_LEN) {
1685e41ab937SDedy Lansky 			vif->gtk_index = key_index;
1686e41ab937SDedy Lansky 			memcpy(vif->gtk, params->key, params->key_len);
1687e41ab937SDedy Lansky 			vif->gtk_len = params->key_len;
1688e41ab937SDedy Lansky 		}
1689b9010f10SAhmad Masri 		/* in FT set crypto will take place upon receiving
1690b9010f10SAhmad Masri 		 * WMI_RING_EN_EVENTID event
1691b9010f10SAhmad Masri 		 */
169274b6ac58SMaya Erez 		wil_set_crypto_rx(key_index, key_usage, cs, params);
1693e41ab937SDedy Lansky 	}
169458527421SVladimir Kondratiev 
169558527421SVladimir Kondratiev 	return rc;
16962be7d22fSVladimir Kondratiev }
16972be7d22fSVladimir Kondratiev 
wil_cfg80211_del_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_index,bool pairwise,const u8 * mac_addr)16982be7d22fSVladimir Kondratiev static int wil_cfg80211_del_key(struct wiphy *wiphy,
1699*e7a7b84eSVeerendranath Jakkam 				struct net_device *ndev, int link_id,
17002be7d22fSVladimir Kondratiev 				u8 key_index, bool pairwise,
17012be7d22fSVladimir Kondratiev 				const u8 *mac_addr)
17022be7d22fSVladimir Kondratiev {
1703e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
17042be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1705e00243faSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
1706e00243faSLior David 	enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
1707e00243faSLior David 	struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
1708e00243faSLior David 							    key_usage,
170958527421SVladimir Kondratiev 							    mac_addr);
17102be7d22fSVladimir Kondratiev 
1711af3db60aSLazar Alexei 	wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr,
171258527421SVladimir Kondratiev 		     key_usage_str[key_usage], key_index);
171358527421SVladimir Kondratiev 
171474b6ac58SMaya Erez 	if (IS_ERR(cs))
1715af3db60aSLazar Alexei 		wil_info(wil, "Not connected, %pM %s[%d]\n",
171658527421SVladimir Kondratiev 			 mac_addr, key_usage_str[key_usage], key_index);
171758527421SVladimir Kondratiev 
171874b6ac58SMaya Erez 	if (!IS_ERR_OR_NULL(cs))
171974b6ac58SMaya Erez 		wil_del_rx_key(key_index, key_usage, cs);
1720db8adcbfSVladimir Kondratiev 
1721e00243faSLior David 	return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage);
17222be7d22fSVladimir Kondratiev }
17232be7d22fSVladimir Kondratiev 
17242be7d22fSVladimir Kondratiev /* Need to be present or wiphy_new() will WARN */
wil_cfg80211_set_default_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_index,bool unicast,bool multicast)17252be7d22fSVladimir Kondratiev static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
1726*e7a7b84eSVeerendranath Jakkam 					struct net_device *ndev, int link_id,
17272be7d22fSVladimir Kondratiev 					u8 key_index, bool unicast,
17282be7d22fSVladimir Kondratiev 					bool multicast)
17292be7d22fSVladimir Kondratiev {
1730280ab987SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1731280ab987SLior David 
1732af3db60aSLazar Alexei 	wil_dbg_misc(wil, "set_default_key: entered\n");
17332be7d22fSVladimir Kondratiev 	return 0;
17342be7d22fSVladimir Kondratiev }
17352be7d22fSVladimir Kondratiev 
wil_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,struct ieee80211_channel * chan,unsigned int duration,u64 * cookie)17361647f12fSVladimir Kondratiev static int wil_remain_on_channel(struct wiphy *wiphy,
17371647f12fSVladimir Kondratiev 				 struct wireless_dev *wdev,
17381647f12fSVladimir Kondratiev 				 struct ieee80211_channel *chan,
17391647f12fSVladimir Kondratiev 				 unsigned int duration,
17401647f12fSVladimir Kondratiev 				 u64 *cookie)
17411647f12fSVladimir Kondratiev {
17421647f12fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
17431647f12fSVladimir Kondratiev 	int rc;
17441647f12fSVladimir Kondratiev 
1745af3db60aSLazar Alexei 	wil_dbg_misc(wil,
1746af3db60aSLazar Alexei 		     "remain_on_channel: center_freq=%d, duration=%d iftype=%d\n",
1747af3db60aSLazar Alexei 		     chan->center_freq, duration, wdev->iftype);
17481647f12fSVladimir Kondratiev 
1749bb6743f7SLior David 	rc = wil_p2p_listen(wil, wdev, duration, chan, cookie);
17501647f12fSVladimir Kondratiev 	return rc;
17511647f12fSVladimir Kondratiev }
17521647f12fSVladimir Kondratiev 
wil_cancel_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,u64 cookie)17531647f12fSVladimir Kondratiev static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
17541647f12fSVladimir Kondratiev 					struct wireless_dev *wdev,
17551647f12fSVladimir Kondratiev 					u64 cookie)
17561647f12fSVladimir Kondratiev {
17571647f12fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1758e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
17591647f12fSVladimir Kondratiev 
1760af3db60aSLazar Alexei 	wil_dbg_misc(wil, "cancel_remain_on_channel\n");
17611647f12fSVladimir Kondratiev 
1762e00243faSLior David 	return wil_p2p_cancel_listen(vif, cookie);
17631647f12fSVladimir Kondratiev }
17641647f12fSVladimir Kondratiev 
1765691c7a4dSLee Jones /*
1766c100c883SLior David  * find a specific IE in a list of IEs
1767c100c883SLior David  * return a pointer to the beginning of IE in the list
1768c100c883SLior David  * or NULL if not found
1769c100c883SLior David  */
_wil_cfg80211_find_ie(const u8 * ies,u16 ies_len,const u8 * ie,u16 ie_len)1770c100c883SLior David static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie,
1771c100c883SLior David 				       u16 ie_len)
1772c100c883SLior David {
1773c100c883SLior David 	struct ieee80211_vendor_ie *vie;
1774c100c883SLior David 	u32 oui;
1775c100c883SLior David 
1776c100c883SLior David 	/* IE tag at offset 0, length at offset 1 */
1777c100c883SLior David 	if (ie_len < 2 || 2 + ie[1] > ie_len)
1778c100c883SLior David 		return NULL;
1779c100c883SLior David 
1780c100c883SLior David 	if (ie[0] != WLAN_EID_VENDOR_SPECIFIC)
1781c100c883SLior David 		return cfg80211_find_ie(ie[0], ies, ies_len);
1782c100c883SLior David 
1783c100c883SLior David 	/* make sure there is room for 3 bytes OUI + 1 byte OUI type */
1784c100c883SLior David 	if (ie[1] < 4)
1785c100c883SLior David 		return NULL;
1786c100c883SLior David 	vie = (struct ieee80211_vendor_ie *)ie;
1787c100c883SLior David 	oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2];
1788c100c883SLior David 	return cfg80211_find_vendor_ie(oui, vie->oui_type, ies,
1789c100c883SLior David 				       ies_len);
1790c100c883SLior David }
1791c100c883SLior David 
1792691c7a4dSLee Jones /*
1793c100c883SLior David  * merge the IEs in two lists into a single list.
1794c100c883SLior David  * do not include IEs from the second list which exist in the first list.
1795c100c883SLior David  * add only vendor specific IEs from second list to keep
1796c100c883SLior David  * the merged list sorted (since vendor-specific IE has the
1797c100c883SLior David  * highest tag number)
1798c100c883SLior David  * caller must free the allocated memory for merged IEs
1799c100c883SLior David  */
_wil_cfg80211_merge_extra_ies(const u8 * ies1,u16 ies1_len,const u8 * ies2,u16 ies2_len,u8 ** merged_ies,u16 * merged_len)1800c100c883SLior David static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len,
1801c100c883SLior David 					 const u8 *ies2, u16 ies2_len,
1802c100c883SLior David 					 u8 **merged_ies, u16 *merged_len)
1803c100c883SLior David {
1804c100c883SLior David 	u8 *buf, *dpos;
1805c100c883SLior David 	const u8 *spos;
1806c100c883SLior David 
1807de77a53cSAlexei Avshalom Lazar 	if (!ies1)
1808de77a53cSAlexei Avshalom Lazar 		ies1_len = 0;
1809de77a53cSAlexei Avshalom Lazar 
1810de77a53cSAlexei Avshalom Lazar 	if (!ies2)
1811de77a53cSAlexei Avshalom Lazar 		ies2_len = 0;
1812de77a53cSAlexei Avshalom Lazar 
1813c100c883SLior David 	if (ies1_len == 0 && ies2_len == 0) {
1814c100c883SLior David 		*merged_ies = NULL;
1815c100c883SLior David 		*merged_len = 0;
1816c100c883SLior David 		return 0;
1817c100c883SLior David 	}
1818c100c883SLior David 
1819c100c883SLior David 	buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL);
1820c100c883SLior David 	if (!buf)
1821c100c883SLior David 		return -ENOMEM;
1822de77a53cSAlexei Avshalom Lazar 	if (ies1)
1823c100c883SLior David 		memcpy(buf, ies1, ies1_len);
1824c100c883SLior David 	dpos = buf + ies1_len;
1825c100c883SLior David 	spos = ies2;
1826de77a53cSAlexei Avshalom Lazar 	while (spos && (spos + 1 < ies2 + ies2_len)) {
1827c100c883SLior David 		/* IE tag at offset 0, length at offset 1 */
1828c100c883SLior David 		u16 ielen = 2 + spos[1];
1829c100c883SLior David 
1830c100c883SLior David 		if (spos + ielen > ies2 + ies2_len)
1831c100c883SLior David 			break;
1832c100c883SLior David 		if (spos[0] == WLAN_EID_VENDOR_SPECIFIC &&
1833de77a53cSAlexei Avshalom Lazar 		    (!ies1 || !_wil_cfg80211_find_ie(ies1, ies1_len,
1834de77a53cSAlexei Avshalom Lazar 						     spos, ielen))) {
1835c100c883SLior David 			memcpy(dpos, spos, ielen);
1836c100c883SLior David 			dpos += ielen;
1837c100c883SLior David 		}
1838c100c883SLior David 		spos += ielen;
1839c100c883SLior David 	}
1840c100c883SLior David 
1841c100c883SLior David 	*merged_ies = buf;
1842c100c883SLior David 	*merged_len = dpos - buf;
1843c100c883SLior David 	return 0;
1844c100c883SLior David }
1845c100c883SLior David 
wil_print_bcon_data(struct cfg80211_beacon_data * b)1846ca959773SVladimir Kondratiev static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
1847ca959773SVladimir Kondratiev {
18485eb443e9SDedy Lansky 	wil_hex_dump_misc("head     ", DUMP_PREFIX_OFFSET, 16, 1,
18495eb443e9SDedy Lansky 			  b->head, b->head_len, true);
18505eb443e9SDedy Lansky 	wil_hex_dump_misc("tail     ", DUMP_PREFIX_OFFSET, 16, 1,
18515eb443e9SDedy Lansky 			  b->tail, b->tail_len, true);
18525eb443e9SDedy Lansky 	wil_hex_dump_misc("BCON IE  ", DUMP_PREFIX_OFFSET, 16, 1,
18535eb443e9SDedy Lansky 			  b->beacon_ies, b->beacon_ies_len, true);
18545eb443e9SDedy Lansky 	wil_hex_dump_misc("PROBE    ", DUMP_PREFIX_OFFSET, 16, 1,
18555eb443e9SDedy Lansky 			  b->probe_resp, b->probe_resp_len, true);
18565eb443e9SDedy Lansky 	wil_hex_dump_misc("PROBE IE ", DUMP_PREFIX_OFFSET, 16, 1,
18575eb443e9SDedy Lansky 			  b->proberesp_ies, b->proberesp_ies_len, true);
18585eb443e9SDedy Lansky 	wil_hex_dump_misc("ASSOC IE ", DUMP_PREFIX_OFFSET, 16, 1,
18595eb443e9SDedy Lansky 			  b->assocresp_ies, b->assocresp_ies_len, true);
1860ca959773SVladimir Kondratiev }
1861ca959773SVladimir Kondratiev 
186233190ebfSVladimir Kondratiev /* internal functions for device reset and starting AP */
1863b9010f10SAhmad Masri static u8 *
_wil_cfg80211_get_proberesp_ies(const u8 * proberesp,u16 proberesp_len,u16 * ies_len)1864b9010f10SAhmad Masri _wil_cfg80211_get_proberesp_ies(const u8 *proberesp, u16 proberesp_len,
1865b9010f10SAhmad Masri 				u16 *ies_len)
1866b9010f10SAhmad Masri {
1867b9010f10SAhmad Masri 	u8 *ies = NULL;
1868b9010f10SAhmad Masri 
1869b9010f10SAhmad Masri 	if (proberesp) {
1870b9010f10SAhmad Masri 		struct ieee80211_mgmt *f =
1871b9010f10SAhmad Masri 			(struct ieee80211_mgmt *)proberesp;
1872b9010f10SAhmad Masri 		size_t hlen = offsetof(struct ieee80211_mgmt,
1873b9010f10SAhmad Masri 				       u.probe_resp.variable);
1874b9010f10SAhmad Masri 
1875b9010f10SAhmad Masri 		ies = f->u.probe_resp.variable;
1876b9010f10SAhmad Masri 		if (ies_len)
1877b9010f10SAhmad Masri 			*ies_len = proberesp_len - hlen;
1878b9010f10SAhmad Masri 	}
1879b9010f10SAhmad Masri 
1880b9010f10SAhmad Masri 	return ies;
1881b9010f10SAhmad Masri }
1882b9010f10SAhmad Masri 
_wil_cfg80211_set_ies(struct wil6210_vif * vif,struct cfg80211_beacon_data * bcon)1883e00243faSLior David static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
1884cab5abbfSVladimir Kondratiev 				 struct cfg80211_beacon_data *bcon)
188533190ebfSVladimir Kondratiev {
188633190ebfSVladimir Kondratiev 	int rc;
1887c100c883SLior David 	u16 len = 0, proberesp_len = 0;
1888b9010f10SAhmad Masri 	u8 *ies = NULL, *proberesp;
188933190ebfSVladimir Kondratiev 
1890e41ab937SDedy Lansky 	/* update local storage used for AP recovery */
1891e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, bcon->probe_resp,
1892e41ab937SDedy Lansky 		      bcon->probe_resp_len);
1893e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len,
1894e41ab937SDedy Lansky 		      bcon->proberesp_ies, bcon->proberesp_ies_len);
1895e41ab937SDedy Lansky 	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len,
1896e41ab937SDedy Lansky 		      bcon->assocresp_ies, bcon->assocresp_ies_len);
1897e41ab937SDedy Lansky 
1898b9010f10SAhmad Masri 	proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
1899b9010f10SAhmad Masri 						    bcon->probe_resp_len,
1900b9010f10SAhmad Masri 						    &proberesp_len);
1901c100c883SLior David 	rc = _wil_cfg80211_merge_extra_ies(proberesp,
1902c100c883SLior David 					   proberesp_len,
1903c100c883SLior David 					   bcon->proberesp_ies,
1904c100c883SLior David 					   bcon->proberesp_ies_len,
1905c100c883SLior David 					   &ies, &len);
1906c100c883SLior David 
19075421bf0cSVladimir Kondratiev 	if (rc)
1908c100c883SLior David 		goto out;
190933190ebfSVladimir Kondratiev 
1910e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies);
1911c100c883SLior David 	if (rc)
1912c100c883SLior David 		goto out;
1913c100c883SLior David 
1914c100c883SLior David 	if (bcon->assocresp_ies)
1915e00243faSLior David 		rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP,
1916c100c883SLior David 				bcon->assocresp_ies_len, bcon->assocresp_ies);
1917c100c883SLior David 	else
1918e00243faSLior David 		rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies);
1919cab5abbfSVladimir Kondratiev #if 0 /* to use beacon IE's, remove this #if 0 */
19205421bf0cSVladimir Kondratiev 	if (rc)
1921c100c883SLior David 		goto out;
19225421bf0cSVladimir Kondratiev 
1923e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_BEACON,
1924e00243faSLior David 			bcon->tail_len, bcon->tail);
1925cab5abbfSVladimir Kondratiev #endif
1926c100c883SLior David out:
1927c100c883SLior David 	kfree(ies);
19285421bf0cSVladimir Kondratiev 	return rc;
192933190ebfSVladimir Kondratiev }
193033190ebfSVladimir Kondratiev 
_wil_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * ndev,const u8 * ssid,size_t ssid_len,u32 privacy,int bi,u8 chan,u8 wmi_edmg_channel,struct cfg80211_beacon_data * bcon,u8 hidden_ssid,u32 pbss)193133190ebfSVladimir Kondratiev static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
193233190ebfSVladimir Kondratiev 				  struct net_device *ndev,
193333190ebfSVladimir Kondratiev 				  const u8 *ssid, size_t ssid_len, u32 privacy,
19349abe3e30SAlexei Avshalom Lazar 				  int bi, u8 chan, u8 wmi_edmg_channel,
1935cab5abbfSVladimir Kondratiev 				  struct cfg80211_beacon_data *bcon,
1936eabb03b4SLior David 				  u8 hidden_ssid, u32 pbss)
193733190ebfSVladimir Kondratiev {
193833190ebfSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1939e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
194033190ebfSVladimir Kondratiev 	int rc;
194133190ebfSVladimir Kondratiev 	struct wireless_dev *wdev = ndev->ieee80211_ptr;
194233190ebfSVladimir Kondratiev 	u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
1943b4944f2cSLior David 	u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO);
1944b9010f10SAhmad Masri 	u16 proberesp_len = 0;
1945b9010f10SAhmad Masri 	u8 *proberesp;
1946b9010f10SAhmad Masri 	bool ft = false;
194733190ebfSVladimir Kondratiev 
1948eabb03b4SLior David 	if (pbss)
1949eabb03b4SLior David 		wmi_nettype = WMI_NETTYPE_P2P;
1950eabb03b4SLior David 
1951e00243faSLior David 	wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go);
1952b4944f2cSLior David 	if (is_go && !pbss) {
1953af3db60aSLazar Alexei 		wil_err(wil, "P2P GO must be in PBSS\n");
1954b4944f2cSLior David 		return -ENOTSUPP;
1955b4944f2cSLior David 	}
1956b4944f2cSLior David 
195733190ebfSVladimir Kondratiev 	wil_set_recovery_state(wil, fw_recovery_idle);
195833190ebfSVladimir Kondratiev 
1959b9010f10SAhmad Masri 	proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
1960b9010f10SAhmad Masri 						    bcon->probe_resp_len,
1961b9010f10SAhmad Masri 						    &proberesp_len);
1962b9010f10SAhmad Masri 	/* check that the probe response IEs has a MDE */
1963b9010f10SAhmad Masri 	if ((proberesp && proberesp_len > 0 &&
1964b9010f10SAhmad Masri 	     cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN,
1965b9010f10SAhmad Masri 			      proberesp,
1966b9010f10SAhmad Masri 			      proberesp_len)))
1967b9010f10SAhmad Masri 		ft = true;
1968b9010f10SAhmad Masri 
1969b9010f10SAhmad Masri 	if (ft) {
1970b9010f10SAhmad Masri 		if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING,
1971b9010f10SAhmad Masri 			      wil->fw_capabilities)) {
1972b9010f10SAhmad Masri 			wil_err(wil, "FW does not support FT roaming\n");
1973b9010f10SAhmad Masri 			return -ENOTSUPP;
1974b9010f10SAhmad Masri 		}
1975b9010f10SAhmad Masri 		set_bit(wil_vif_ft_roam, vif->status);
1976b9010f10SAhmad Masri 	}
1977b9010f10SAhmad Masri 
197833190ebfSVladimir Kondratiev 	mutex_lock(&wil->mutex);
197933190ebfSVladimir Kondratiev 
19803ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
198133190ebfSVladimir Kondratiev 		__wil_down(wil);
198233190ebfSVladimir Kondratiev 		rc = __wil_up(wil);
198333190ebfSVladimir Kondratiev 		if (rc)
198433190ebfSVladimir Kondratiev 			goto out;
19853ada9314SLior David 	}
198633190ebfSVladimir Kondratiev 
1987e00243faSLior David 	rc = wmi_set_ssid(vif, ssid_len, ssid);
198833190ebfSVladimir Kondratiev 	if (rc)
198933190ebfSVladimir Kondratiev 		goto out;
199033190ebfSVladimir Kondratiev 
1991e00243faSLior David 	rc = _wil_cfg80211_set_ies(vif, bcon);
199233190ebfSVladimir Kondratiev 	if (rc)
199333190ebfSVladimir Kondratiev 		goto out;
199433190ebfSVladimir Kondratiev 
1995e00243faSLior David 	vif->privacy = privacy;
1996e00243faSLior David 	vif->channel = chan;
19979abe3e30SAlexei Avshalom Lazar 	vif->wmi_edmg_channel = wmi_edmg_channel;
1998e00243faSLior David 	vif->hidden_ssid = hidden_ssid;
1999e00243faSLior David 	vif->pbss = pbss;
2000e41ab937SDedy Lansky 	vif->bi = bi;
2001e41ab937SDedy Lansky 	memcpy(vif->ssid, ssid, ssid_len);
2002e41ab937SDedy Lansky 	vif->ssid_len = ssid_len;
200333190ebfSVladimir Kondratiev 
200433190ebfSVladimir Kondratiev 	netif_carrier_on(ndev);
20053ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
20069953a782SLior David 		wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
200733190ebfSVladimir Kondratiev 
20089abe3e30SAlexei Avshalom Lazar 	rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, wmi_edmg_channel,
20099abe3e30SAlexei Avshalom Lazar 			   hidden_ssid, is_go);
201033190ebfSVladimir Kondratiev 	if (rc)
201133190ebfSVladimir Kondratiev 		goto err_pcp_start;
201233190ebfSVladimir Kondratiev 
2013e00243faSLior David 	rc = wil_bcast_init(vif);
201433190ebfSVladimir Kondratiev 	if (rc)
201533190ebfSVladimir Kondratiev 		goto err_bcast;
201633190ebfSVladimir Kondratiev 
201733190ebfSVladimir Kondratiev 	goto out; /* success */
201833190ebfSVladimir Kondratiev 
201933190ebfSVladimir Kondratiev err_bcast:
2020e00243faSLior David 	wmi_pcp_stop(vif);
202133190ebfSVladimir Kondratiev err_pcp_start:
202233190ebfSVladimir Kondratiev 	netif_carrier_off(ndev);
20233ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
20249953a782SLior David 		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
202533190ebfSVladimir Kondratiev out:
202633190ebfSVladimir Kondratiev 	mutex_unlock(&wil->mutex);
202733190ebfSVladimir Kondratiev 	return rc;
202833190ebfSVladimir Kondratiev }
202933190ebfSVladimir Kondratiev 
wil_cfg80211_ap_recovery(struct wil6210_priv * wil)2030e41ab937SDedy Lansky void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
2031e41ab937SDedy Lansky {
2032e41ab937SDedy Lansky 	int rc, i;
2033e41ab937SDedy Lansky 	struct wiphy *wiphy = wil_to_wiphy(wil);
2034e41ab937SDedy Lansky 
2035e4a29bddSAhmad Masri 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
2036e41ab937SDedy Lansky 		struct wil6210_vif *vif = wil->vifs[i];
2037e41ab937SDedy Lansky 		struct net_device *ndev;
2038e41ab937SDedy Lansky 		struct cfg80211_beacon_data bcon = {};
2039e41ab937SDedy Lansky 		struct key_params key_params = {};
2040e41ab937SDedy Lansky 
2041e41ab937SDedy Lansky 		if (!vif || vif->ssid_len == 0)
2042e41ab937SDedy Lansky 			continue;
2043e41ab937SDedy Lansky 
2044e41ab937SDedy Lansky 		ndev = vif_to_ndev(vif);
2045e41ab937SDedy Lansky 		bcon.proberesp_ies = vif->proberesp_ies;
2046e41ab937SDedy Lansky 		bcon.assocresp_ies = vif->assocresp_ies;
2047e41ab937SDedy Lansky 		bcon.probe_resp = vif->proberesp;
2048e41ab937SDedy Lansky 		bcon.proberesp_ies_len = vif->proberesp_ies_len;
2049e41ab937SDedy Lansky 		bcon.assocresp_ies_len = vif->assocresp_ies_len;
2050e41ab937SDedy Lansky 		bcon.probe_resp_len = vif->proberesp_len;
2051e41ab937SDedy Lansky 
2052e41ab937SDedy Lansky 		wil_info(wil,
2053e41ab937SDedy Lansky 			 "AP (vif %d) recovery: privacy %d, bi %d, channel %d, hidden %d, pbss %d\n",
2054e41ab937SDedy Lansky 			 i, vif->privacy, vif->bi, vif->channel,
2055e41ab937SDedy Lansky 			 vif->hidden_ssid, vif->pbss);
2056e41ab937SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2057e41ab937SDedy Lansky 				  vif->ssid, vif->ssid_len, true);
2058e41ab937SDedy Lansky 		rc = _wil_cfg80211_start_ap(wiphy, ndev,
2059e41ab937SDedy Lansky 					    vif->ssid, vif->ssid_len,
2060e41ab937SDedy Lansky 					    vif->privacy, vif->bi,
20619abe3e30SAlexei Avshalom Lazar 					    vif->channel,
20629abe3e30SAlexei Avshalom Lazar 					    vif->wmi_edmg_channel, &bcon,
2063e41ab937SDedy Lansky 					    vif->hidden_ssid, vif->pbss);
2064e41ab937SDedy Lansky 		if (rc) {
2065e41ab937SDedy Lansky 			wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
2066e41ab937SDedy Lansky 			continue;
2067e41ab937SDedy Lansky 		}
2068e41ab937SDedy Lansky 
2069e41ab937SDedy Lansky 		if (!vif->privacy || vif->gtk_len == 0)
2070e41ab937SDedy Lansky 			continue;
2071e41ab937SDedy Lansky 
2072e41ab937SDedy Lansky 		key_params.key = vif->gtk;
2073e41ab937SDedy Lansky 		key_params.key_len = vif->gtk_len;
2074e41ab937SDedy Lansky 		key_params.seq_len = IEEE80211_GCMP_PN_LEN;
2075*e7a7b84eSVeerendranath Jakkam 		rc = wil_cfg80211_add_key(wiphy, ndev, -1, vif->gtk_index,
2076*e7a7b84eSVeerendranath Jakkam 					  false, NULL, &key_params);
2077e41ab937SDedy Lansky 		if (rc)
2078e41ab937SDedy Lansky 			wil_err(wil, "vif %d recovery add key failed (%d)\n",
2079e41ab937SDedy Lansky 				i, rc);
2080e41ab937SDedy Lansky 	}
2081e41ab937SDedy Lansky }
2082e41ab937SDedy Lansky 
wil_cfg80211_change_beacon(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_beacon_data * bcon)20831bd922fcSVladimir Kondratiev static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
20841bd922fcSVladimir Kondratiev 				      struct net_device *ndev,
20851bd922fcSVladimir Kondratiev 				      struct cfg80211_beacon_data *bcon)
20861bd922fcSVladimir Kondratiev {
20871bd922fcSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2088e41ab937SDedy Lansky 	struct wireless_dev *wdev = ndev->ieee80211_ptr;
2089e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
20901bd922fcSVladimir Kondratiev 	int rc;
209133190ebfSVladimir Kondratiev 	u32 privacy = 0;
20921bd922fcSVladimir Kondratiev 
2093e00243faSLior David 	wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid);
20941e7e5a0dSVladimir Kondratiev 	wil_print_bcon_data(bcon);
20951e7e5a0dSVladimir Kondratiev 
2096c5a157e4SLior David 	if (bcon->tail &&
2097c5a157e4SLior David 	    cfg80211_find_ie(WLAN_EID_RSN, bcon->tail,
2098c5a157e4SLior David 			     bcon->tail_len))
209933190ebfSVladimir Kondratiev 		privacy = 1;
21001bd922fcSVladimir Kondratiev 
21017b0a0e3cSJohannes Berg 	memcpy(vif->ssid, wdev->u.ap.ssid, wdev->u.ap.ssid_len);
21027b0a0e3cSJohannes Berg 	vif->ssid_len = wdev->u.ap.ssid_len;
2103e41ab937SDedy Lansky 
210433190ebfSVladimir Kondratiev 	/* in case privacy has changed, need to restart the AP */
2105e00243faSLior David 	if (vif->privacy != privacy) {
210633190ebfSVladimir Kondratiev 		wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
2107e00243faSLior David 			     vif->privacy, privacy);
210833190ebfSVladimir Kondratiev 
2109e41ab937SDedy Lansky 		rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
2110e41ab937SDedy Lansky 					    vif->ssid_len, privacy,
21117b0a0e3cSJohannes Berg 					    wdev->links[0].ap.beacon_interval,
21129abe3e30SAlexei Avshalom Lazar 					    vif->channel,
21139abe3e30SAlexei Avshalom Lazar 					    vif->wmi_edmg_channel, bcon,
2114e00243faSLior David 					    vif->hidden_ssid,
2115e00243faSLior David 					    vif->pbss);
211633190ebfSVladimir Kondratiev 	} else {
2117e00243faSLior David 		rc = _wil_cfg80211_set_ies(vif, bcon);
21181bd922fcSVladimir Kondratiev 	}
21191bd922fcSVladimir Kondratiev 
212033190ebfSVladimir Kondratiev 	return rc;
21211bd922fcSVladimir Kondratiev }
21221bd922fcSVladimir Kondratiev 
wil_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ap_settings * info)21232be7d22fSVladimir Kondratiev static int wil_cfg80211_start_ap(struct wiphy *wiphy,
21242be7d22fSVladimir Kondratiev 				 struct net_device *ndev,
21252be7d22fSVladimir Kondratiev 				 struct cfg80211_ap_settings *info)
21262be7d22fSVladimir Kondratiev {
212733190ebfSVladimir Kondratiev 	int rc;
21282be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
21292be7d22fSVladimir Kondratiev 	struct ieee80211_channel *channel = info->chandef.chan;
21302be7d22fSVladimir Kondratiev 	struct cfg80211_beacon_data *bcon = &info->beacon;
2131ca959773SVladimir Kondratiev 	struct cfg80211_crypto_settings *crypto = &info->crypto;
21329abe3e30SAlexei Avshalom Lazar 	u8 wmi_edmg_channel;
21338e52fe30SHamad Kadmany 	u8 hidden_ssid;
21342be7d22fSVladimir Kondratiev 
2135af3db60aSLazar Alexei 	wil_dbg_misc(wil, "start_ap\n");
2136ca959773SVladimir Kondratiev 
21379abe3e30SAlexei Avshalom Lazar 	rc = wil_get_wmi_edmg_channel(wil, info->chandef.edmg.bw_config,
21389abe3e30SAlexei Avshalom Lazar 				      info->chandef.edmg.channels,
21399abe3e30SAlexei Avshalom Lazar 				      &wmi_edmg_channel);
21409abe3e30SAlexei Avshalom Lazar 	if (rc < 0)
21419abe3e30SAlexei Avshalom Lazar 		return rc;
21429abe3e30SAlexei Avshalom Lazar 
21432be7d22fSVladimir Kondratiev 	if (!channel) {
21442be7d22fSVladimir Kondratiev 		wil_err(wil, "AP: No channel???\n");
21452be7d22fSVladimir Kondratiev 		return -EINVAL;
21462be7d22fSVladimir Kondratiev 	}
21472be7d22fSVladimir Kondratiev 
214833190ebfSVladimir Kondratiev 	switch (info->hidden_ssid) {
214933190ebfSVladimir Kondratiev 	case NL80211_HIDDEN_SSID_NOT_IN_USE:
215033190ebfSVladimir Kondratiev 		hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
215133190ebfSVladimir Kondratiev 		break;
215233190ebfSVladimir Kondratiev 
215333190ebfSVladimir Kondratiev 	case NL80211_HIDDEN_SSID_ZERO_LEN:
215433190ebfSVladimir Kondratiev 		hidden_ssid = WMI_HIDDEN_SSID_SEND_EMPTY;
215533190ebfSVladimir Kondratiev 		break;
215633190ebfSVladimir Kondratiev 
215733190ebfSVladimir Kondratiev 	case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
215833190ebfSVladimir Kondratiev 		hidden_ssid = WMI_HIDDEN_SSID_CLEAR;
215933190ebfSVladimir Kondratiev 		break;
216033190ebfSVladimir Kondratiev 
216133190ebfSVladimir Kondratiev 	default:
216233190ebfSVladimir Kondratiev 		wil_err(wil, "AP: Invalid hidden SSID %d\n", info->hidden_ssid);
216333190ebfSVladimir Kondratiev 		return -EOPNOTSUPP;
216433190ebfSVladimir Kondratiev 	}
21657743882dSVladimir Kondratiev 	wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
21662be7d22fSVladimir Kondratiev 		     channel->center_freq, info->privacy ? "secure" : "open");
2167ca959773SVladimir Kondratiev 	wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
2168ca959773SVladimir Kondratiev 		     info->privacy, info->auth_type);
21698e52fe30SHamad Kadmany 	wil_dbg_misc(wil, "Hidden SSID mode: %d\n",
21708e52fe30SHamad Kadmany 		     info->hidden_ssid);
2171ca959773SVladimir Kondratiev 	wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
2172ca959773SVladimir Kondratiev 		     info->dtim_period);
2173eabb03b4SLior David 	wil_dbg_misc(wil, "PBSS %d\n", info->pbss);
21745eb443e9SDedy Lansky 	wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
21755eb443e9SDedy Lansky 			  info->ssid, info->ssid_len, true);
2176ca959773SVladimir Kondratiev 	wil_print_bcon_data(bcon);
2177ca959773SVladimir Kondratiev 	wil_print_crypto(wil, crypto);
21782be7d22fSVladimir Kondratiev 
217933190ebfSVladimir Kondratiev 	rc = _wil_cfg80211_start_ap(wiphy, ndev,
218033190ebfSVladimir Kondratiev 				    info->ssid, info->ssid_len, info->privacy,
218133190ebfSVladimir Kondratiev 				    info->beacon_interval, channel->hw_value,
21829abe3e30SAlexei Avshalom Lazar 				    wmi_edmg_channel, bcon, hidden_ssid,
21839abe3e30SAlexei Avshalom Lazar 				    info->pbss);
2184c33407a8SVladimir Kondratiev 
21852be7d22fSVladimir Kondratiev 	return rc;
21862be7d22fSVladimir Kondratiev }
21872be7d22fSVladimir Kondratiev 
wil_cfg80211_stop_ap(struct wiphy * wiphy,struct net_device * ndev,unsigned int link_id)21882be7d22fSVladimir Kondratiev static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
21897b0a0e3cSJohannes Berg 				struct net_device *ndev,
21907b0a0e3cSJohannes Berg 				unsigned int link_id)
21912be7d22fSVladimir Kondratiev {
21922be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2193e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
21943ada9314SLior David 	bool last;
21952be7d22fSVladimir Kondratiev 
21963ada9314SLior David 	wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid);
2197ca959773SVladimir Kondratiev 
2198c5e96c91SDedy Lansky 	netif_carrier_off(ndev);
21993ada9314SLior David 	last = !wil_has_other_active_ifaces(wil, ndev, false, true);
22003ada9314SLior David 	if (last) {
22019953a782SLior David 		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
2202c33407a8SVladimir Kondratiev 		wil_set_recovery_state(wil, fw_recovery_idle);
2203646d402dSHamad Kadmany 		set_bit(wil_status_resetting, wil->status);
22043ada9314SLior David 	}
2205646d402dSHamad Kadmany 
22069c3bde56SVladimir Kondratiev 	mutex_lock(&wil->mutex);
22079c3bde56SVladimir Kondratiev 
2208e00243faSLior David 	wmi_pcp_stop(vif);
2209b9010f10SAhmad Masri 	clear_bit(wil_vif_ft_roam, vif->status);
2210e41ab937SDedy Lansky 	vif->ssid_len = 0;
2211e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, NULL, 0);
2212e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len, NULL, 0);
2213e41ab937SDedy Lansky 	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len, NULL, 0);
2214e41ab937SDedy Lansky 	memset(vif->gtk, 0, WMI_MAX_KEY_LEN);
2215e41ab937SDedy Lansky 	vif->gtk_len = 0;
22162be7d22fSVladimir Kondratiev 
22173ada9314SLior David 	if (last)
221873d839aeSVladimir Kondratiev 		__wil_down(wil);
22193ada9314SLior David 	else
22203ada9314SLior David 		wil_bcast_fini(vif);
222173d839aeSVladimir Kondratiev 
22229c3bde56SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
222373d839aeSVladimir Kondratiev 
222432a20d46SDedy Lansky 	return 0;
22252be7d22fSVladimir Kondratiev }
22262be7d22fSVladimir Kondratiev 
wil_cfg80211_add_station(struct wiphy * wiphy,struct net_device * dev,const u8 * mac,struct station_parameters * params)2227849a564bSDedy Lansky static int wil_cfg80211_add_station(struct wiphy *wiphy,
2228849a564bSDedy Lansky 				    struct net_device *dev,
2229849a564bSDedy Lansky 				    const u8 *mac,
2230849a564bSDedy Lansky 				    struct station_parameters *params)
2231849a564bSDedy Lansky {
2232e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2233849a564bSDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2234849a564bSDedy Lansky 
2235b9010f10SAhmad Masri 	wil_dbg_misc(wil, "add station %pM aid %d mid %d mask 0x%x set 0x%x\n",
2236b9010f10SAhmad Masri 		     mac, params->aid, vif->mid,
2237b9010f10SAhmad Masri 		     params->sta_flags_mask, params->sta_flags_set);
2238849a564bSDedy Lansky 
2239849a564bSDedy Lansky 	if (!disable_ap_sme) {
2240849a564bSDedy Lansky 		wil_err(wil, "not supported with AP SME enabled\n");
2241849a564bSDedy Lansky 		return -EOPNOTSUPP;
2242849a564bSDedy Lansky 	}
2243849a564bSDedy Lansky 
2244849a564bSDedy Lansky 	if (params->aid > WIL_MAX_DMG_AID) {
2245849a564bSDedy Lansky 		wil_err(wil, "invalid aid\n");
2246849a564bSDedy Lansky 		return -EINVAL;
2247849a564bSDedy Lansky 	}
2248849a564bSDedy Lansky 
2249e00243faSLior David 	return wmi_new_sta(vif, mac, params->aid);
2250849a564bSDedy Lansky }
2251849a564bSDedy Lansky 
wil_cfg80211_del_station(struct wiphy * wiphy,struct net_device * dev,struct station_del_parameters * params)22524d55a0a1SVladimir Kondratiev static int wil_cfg80211_del_station(struct wiphy *wiphy,
225389c771e5SJouni Malinen 				    struct net_device *dev,
225489c771e5SJouni Malinen 				    struct station_del_parameters *params)
22554d55a0a1SVladimir Kondratiev {
2256e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
22574d55a0a1SVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2258097638a0SVladimir Kondratiev 
2259e00243faSLior David 	wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n",
2260e00243faSLior David 		     params->mac, params->reason_code, vif->mid);
2261de9084efSVladimir Kondratiev 
2262097638a0SVladimir Kondratiev 	mutex_lock(&wil->mutex);
2263e1b43407SAhmad Masri 	wil6210_disconnect(vif, params->mac, params->reason_code);
2264097638a0SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
2265097638a0SVladimir Kondratiev 
22664d55a0a1SVladimir Kondratiev 	return 0;
22674d55a0a1SVladimir Kondratiev }
22684d55a0a1SVladimir Kondratiev 
wil_cfg80211_change_station(struct wiphy * wiphy,struct net_device * dev,const u8 * mac,struct station_parameters * params)2269849a564bSDedy Lansky static int wil_cfg80211_change_station(struct wiphy *wiphy,
2270849a564bSDedy Lansky 				       struct net_device *dev,
2271849a564bSDedy Lansky 				       const u8 *mac,
2272849a564bSDedy Lansky 				       struct station_parameters *params)
2273849a564bSDedy Lansky {
2274e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2275849a564bSDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2276849a564bSDedy Lansky 	int authorize;
2277849a564bSDedy Lansky 	int cid, i;
227810590c6aSGidon Studinski 	struct wil_ring_tx_data *txdata = NULL;
2279849a564bSDedy Lansky 
2280e00243faSLior David 	wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n",
2281e00243faSLior David 		     mac, params->sta_flags_mask, params->sta_flags_set,
2282e00243faSLior David 		     vif->mid);
2283849a564bSDedy Lansky 
2284849a564bSDedy Lansky 	if (!disable_ap_sme) {
2285849a564bSDedy Lansky 		wil_dbg_misc(wil, "not supported with AP SME enabled\n");
2286849a564bSDedy Lansky 		return -EOPNOTSUPP;
2287849a564bSDedy Lansky 	}
2288849a564bSDedy Lansky 
2289849a564bSDedy Lansky 	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2290849a564bSDedy Lansky 		return 0;
2291849a564bSDedy Lansky 
2292e00243faSLior David 	cid = wil_find_cid(wil, vif->mid, mac);
2293849a564bSDedy Lansky 	if (cid < 0) {
2294849a564bSDedy Lansky 		wil_err(wil, "station not found\n");
2295849a564bSDedy Lansky 		return -ENOLINK;
2296849a564bSDedy Lansky 	}
2297849a564bSDedy Lansky 
229810590c6aSGidon Studinski 	for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++)
229910590c6aSGidon Studinski 		if (wil->ring2cid_tid[i][0] == cid) {
230010590c6aSGidon Studinski 			txdata = &wil->ring_tx_data[i];
2301849a564bSDedy Lansky 			break;
2302849a564bSDedy Lansky 		}
2303849a564bSDedy Lansky 
2304849a564bSDedy Lansky 	if (!txdata) {
230510590c6aSGidon Studinski 		wil_err(wil, "ring data not found\n");
2306849a564bSDedy Lansky 		return -ENOLINK;
2307849a564bSDedy Lansky 	}
2308849a564bSDedy Lansky 
2309849a564bSDedy Lansky 	authorize = params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED);
2310849a564bSDedy Lansky 	txdata->dot1x_open = authorize ? 1 : 0;
231110590c6aSGidon Studinski 	wil_dbg_misc(wil, "cid %d ring %d authorize %d\n", cid, i,
2312849a564bSDedy Lansky 		     txdata->dot1x_open);
2313849a564bSDedy Lansky 
2314849a564bSDedy Lansky 	return 0;
2315849a564bSDedy Lansky }
2316849a564bSDedy Lansky 
231740822a90SVladimir Kondratiev /* probe_client handling */
wil_probe_client_handle(struct wil6210_priv * wil,struct wil6210_vif * vif,struct wil_probe_client_req * req)231840822a90SVladimir Kondratiev static void wil_probe_client_handle(struct wil6210_priv *wil,
2319e00243faSLior David 				    struct wil6210_vif *vif,
232040822a90SVladimir Kondratiev 				    struct wil_probe_client_req *req)
232140822a90SVladimir Kondratiev {
2322e00243faSLior David 	struct net_device *ndev = vif_to_ndev(vif);
232340822a90SVladimir Kondratiev 	struct wil_sta_info *sta = &wil->sta[req->cid];
232440822a90SVladimir Kondratiev 	/* assume STA is alive if it is still connected,
232540822a90SVladimir Kondratiev 	 * else FW will disconnect it
232640822a90SVladimir Kondratiev 	 */
232740822a90SVladimir Kondratiev 	bool alive = (sta->status == wil_sta_connected);
232840822a90SVladimir Kondratiev 
2329c4b50cd3SVenkateswara Naralasetty 	cfg80211_probe_status(ndev, sta->addr, req->cookie, alive,
2330c4b50cd3SVenkateswara Naralasetty 			      0, false, GFP_KERNEL);
233140822a90SVladimir Kondratiev }
233240822a90SVladimir Kondratiev 
next_probe_client(struct wil6210_vif * vif)2333e00243faSLior David static struct list_head *next_probe_client(struct wil6210_vif *vif)
233440822a90SVladimir Kondratiev {
233540822a90SVladimir Kondratiev 	struct list_head *ret = NULL;
233640822a90SVladimir Kondratiev 
2337e00243faSLior David 	mutex_lock(&vif->probe_client_mutex);
233840822a90SVladimir Kondratiev 
2339e00243faSLior David 	if (!list_empty(&vif->probe_client_pending)) {
2340e00243faSLior David 		ret = vif->probe_client_pending.next;
234140822a90SVladimir Kondratiev 		list_del(ret);
234240822a90SVladimir Kondratiev 	}
234340822a90SVladimir Kondratiev 
2344e00243faSLior David 	mutex_unlock(&vif->probe_client_mutex);
234540822a90SVladimir Kondratiev 
234640822a90SVladimir Kondratiev 	return ret;
234740822a90SVladimir Kondratiev }
234840822a90SVladimir Kondratiev 
wil_probe_client_worker(struct work_struct * work)234940822a90SVladimir Kondratiev void wil_probe_client_worker(struct work_struct *work)
235040822a90SVladimir Kondratiev {
2351e00243faSLior David 	struct wil6210_vif *vif = container_of(work, struct wil6210_vif,
235240822a90SVladimir Kondratiev 					       probe_client_worker);
2353e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
235440822a90SVladimir Kondratiev 	struct wil_probe_client_req *req;
235540822a90SVladimir Kondratiev 	struct list_head *lh;
235640822a90SVladimir Kondratiev 
2357e00243faSLior David 	while ((lh = next_probe_client(vif)) != NULL) {
235840822a90SVladimir Kondratiev 		req = list_entry(lh, struct wil_probe_client_req, list);
235940822a90SVladimir Kondratiev 
2360e00243faSLior David 		wil_probe_client_handle(wil, vif, req);
236140822a90SVladimir Kondratiev 		kfree(req);
236240822a90SVladimir Kondratiev 	}
236340822a90SVladimir Kondratiev }
236440822a90SVladimir Kondratiev 
wil_probe_client_flush(struct wil6210_vif * vif)2365e00243faSLior David void wil_probe_client_flush(struct wil6210_vif *vif)
236640822a90SVladimir Kondratiev {
236740822a90SVladimir Kondratiev 	struct wil_probe_client_req *req, *t;
2368e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
236940822a90SVladimir Kondratiev 
2370af3db60aSLazar Alexei 	wil_dbg_misc(wil, "probe_client_flush\n");
237140822a90SVladimir Kondratiev 
2372e00243faSLior David 	mutex_lock(&vif->probe_client_mutex);
237340822a90SVladimir Kondratiev 
2374e00243faSLior David 	list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) {
237540822a90SVladimir Kondratiev 		list_del(&req->list);
237640822a90SVladimir Kondratiev 		kfree(req);
237740822a90SVladimir Kondratiev 	}
237840822a90SVladimir Kondratiev 
2379e00243faSLior David 	mutex_unlock(&vif->probe_client_mutex);
238040822a90SVladimir Kondratiev }
238140822a90SVladimir Kondratiev 
wil_cfg80211_probe_client(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,u64 * cookie)238240822a90SVladimir Kondratiev static int wil_cfg80211_probe_client(struct wiphy *wiphy,
238340822a90SVladimir Kondratiev 				     struct net_device *dev,
238440822a90SVladimir Kondratiev 				     const u8 *peer, u64 *cookie)
238540822a90SVladimir Kondratiev {
238640822a90SVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2387e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
238840822a90SVladimir Kondratiev 	struct wil_probe_client_req *req;
2389e00243faSLior David 	int cid = wil_find_cid(wil, vif->mid, peer);
239040822a90SVladimir Kondratiev 
2391e00243faSLior David 	wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n",
2392e00243faSLior David 		     peer, cid, vif->mid);
239340822a90SVladimir Kondratiev 
239440822a90SVladimir Kondratiev 	if (cid < 0)
239540822a90SVladimir Kondratiev 		return -ENOLINK;
239640822a90SVladimir Kondratiev 
239740822a90SVladimir Kondratiev 	req = kzalloc(sizeof(*req), GFP_KERNEL);
239840822a90SVladimir Kondratiev 	if (!req)
239940822a90SVladimir Kondratiev 		return -ENOMEM;
240040822a90SVladimir Kondratiev 
240140822a90SVladimir Kondratiev 	req->cid = cid;
240240822a90SVladimir Kondratiev 	req->cookie = cid;
240340822a90SVladimir Kondratiev 
2404e00243faSLior David 	mutex_lock(&vif->probe_client_mutex);
2405e00243faSLior David 	list_add_tail(&req->list, &vif->probe_client_pending);
2406e00243faSLior David 	mutex_unlock(&vif->probe_client_mutex);
240740822a90SVladimir Kondratiev 
240840822a90SVladimir Kondratiev 	*cookie = req->cookie;
2409e00243faSLior David 	queue_work(wil->wq_service, &vif->probe_client_worker);
241040822a90SVladimir Kondratiev 	return 0;
241140822a90SVladimir Kondratiev }
241240822a90SVladimir Kondratiev 
wil_cfg80211_change_bss(struct wiphy * wiphy,struct net_device * dev,struct bss_parameters * params)241302beaf1aSVladimir Kondratiev static int wil_cfg80211_change_bss(struct wiphy *wiphy,
241402beaf1aSVladimir Kondratiev 				   struct net_device *dev,
241502beaf1aSVladimir Kondratiev 				   struct bss_parameters *params)
241602beaf1aSVladimir Kondratiev {
241702beaf1aSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2418e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
241902beaf1aSVladimir Kondratiev 
242002beaf1aSVladimir Kondratiev 	if (params->ap_isolate >= 0) {
2421e00243faSLior David 		wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n",
2422e00243faSLior David 			     vif->mid, vif->ap_isolate, params->ap_isolate);
2423e00243faSLior David 		vif->ap_isolate = params->ap_isolate;
242402beaf1aSVladimir Kondratiev 	}
242502beaf1aSVladimir Kondratiev 
242602beaf1aSVladimir Kondratiev 	return 0;
242702beaf1aSVladimir Kondratiev }
242802beaf1aSVladimir Kondratiev 
wil_cfg80211_set_power_mgmt(struct wiphy * wiphy,struct net_device * dev,bool enabled,int timeout)24292c207eb8SMaya Erez static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
24302c207eb8SMaya Erez 				       struct net_device *dev,
24312c207eb8SMaya Erez 				       bool enabled, int timeout)
24322c207eb8SMaya Erez {
24332c207eb8SMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
24342c207eb8SMaya Erez 	enum wmi_ps_profile_type ps_profile;
24352c207eb8SMaya Erez 
24362c207eb8SMaya Erez 	wil_dbg_misc(wil, "enabled=%d, timeout=%d\n",
24372c207eb8SMaya Erez 		     enabled, timeout);
24382c207eb8SMaya Erez 
24392c207eb8SMaya Erez 	if (enabled)
24402c207eb8SMaya Erez 		ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT;
24412c207eb8SMaya Erez 	else
24422c207eb8SMaya Erez 		ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED;
24432c207eb8SMaya Erez 
24448b068c03SLazar Alexei 	return wil_ps_update(wil, ps_profile);
24452c207eb8SMaya Erez }
24462c207eb8SMaya Erez 
wil_cfg80211_suspend(struct wiphy * wiphy,struct cfg80211_wowlan * wow)2447fe9ee51eSMaya Erez static int wil_cfg80211_suspend(struct wiphy *wiphy,
2448fe9ee51eSMaya Erez 				struct cfg80211_wowlan *wow)
2449fe9ee51eSMaya Erez {
2450fe9ee51eSMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2451fe9ee51eSMaya Erez 	int rc;
2452fe9ee51eSMaya Erez 
2453fe9ee51eSMaya Erez 	/* Setting the wakeup trigger based on wow is TBD */
2454fe9ee51eSMaya Erez 
2455fe9ee51eSMaya Erez 	if (test_bit(wil_status_suspended, wil->status)) {
2456fe9ee51eSMaya Erez 		wil_dbg_pm(wil, "trying to suspend while suspended\n");
2457fe9ee51eSMaya Erez 		return 0;
2458fe9ee51eSMaya Erez 	}
2459fe9ee51eSMaya Erez 
2460fe9ee51eSMaya Erez 	rc = wil_can_suspend(wil, false);
2461fe9ee51eSMaya Erez 	if (rc)
2462fe9ee51eSMaya Erez 		goto out;
2463fe9ee51eSMaya Erez 
2464fe9ee51eSMaya Erez 	wil_dbg_pm(wil, "suspending\n");
2465fe9ee51eSMaya Erez 
2466144a12a6SHamad Kadmany 	mutex_lock(&wil->mutex);
2467404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
2468144a12a6SHamad Kadmany 	wil_p2p_stop_radio_operations(wil);
24695bd60982SLior David 	wil_abort_scan_all_vifs(wil, true);
2470404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
2471144a12a6SHamad Kadmany 	mutex_unlock(&wil->mutex);
2472fe9ee51eSMaya Erez 
2473fe9ee51eSMaya Erez out:
2474fe9ee51eSMaya Erez 	return rc;
2475fe9ee51eSMaya Erez }
2476fe9ee51eSMaya Erez 
wil_cfg80211_resume(struct wiphy * wiphy)2477fe9ee51eSMaya Erez static int wil_cfg80211_resume(struct wiphy *wiphy)
2478fe9ee51eSMaya Erez {
2479fe9ee51eSMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2480fe9ee51eSMaya Erez 
2481fe9ee51eSMaya Erez 	wil_dbg_pm(wil, "resuming\n");
2482fe9ee51eSMaya Erez 
2483fe9ee51eSMaya Erez 	return 0;
2484fe9ee51eSMaya Erez }
2485fe9ee51eSMaya Erez 
2486a5dc6883SDedy Lansky static int
wil_cfg80211_sched_scan_start(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_sched_scan_request * request)2487a5dc6883SDedy Lansky wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
2488a5dc6883SDedy Lansky 			      struct net_device *dev,
2489a5dc6883SDedy Lansky 			      struct cfg80211_sched_scan_request *request)
2490a5dc6883SDedy Lansky {
2491a5dc6883SDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2492e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2493a5dc6883SDedy Lansky 	int i, rc;
2494a5dc6883SDedy Lansky 
2495e00243faSLior David 	if (vif->mid != 0)
2496e00243faSLior David 		return -EOPNOTSUPP;
2497e00243faSLior David 
2498a5dc6883SDedy Lansky 	wil_dbg_misc(wil,
2499a5dc6883SDedy Lansky 		     "sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
2500a5dc6883SDedy Lansky 		     request->n_ssids, request->ie_len, request->flags);
2501a5dc6883SDedy Lansky 	for (i = 0; i < request->n_ssids; i++) {
2502a5dc6883SDedy Lansky 		wil_dbg_misc(wil, "SSID[%d]:", i);
2503a5dc6883SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2504a5dc6883SDedy Lansky 				  request->ssids[i].ssid,
2505a5dc6883SDedy Lansky 				  request->ssids[i].ssid_len, true);
2506a5dc6883SDedy Lansky 	}
2507a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "channels:");
2508a5dc6883SDedy Lansky 	for (i = 0; i < request->n_channels; i++)
2509a5dc6883SDedy Lansky 		wil_dbg_misc(wil, " %d%s", request->channels[i]->hw_value,
2510a5dc6883SDedy Lansky 			     i == request->n_channels - 1 ? "\n" : "");
2511a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "n_match_sets %d, min_rssi_thold %d, delay %d\n",
2512a5dc6883SDedy Lansky 		     request->n_match_sets, request->min_rssi_thold,
2513a5dc6883SDedy Lansky 		     request->delay);
2514a5dc6883SDedy Lansky 	for (i = 0; i < request->n_match_sets; i++) {
2515a5dc6883SDedy Lansky 		struct cfg80211_match_set *ms = &request->match_sets[i];
2516a5dc6883SDedy Lansky 
2517a5dc6883SDedy Lansky 		wil_dbg_misc(wil, "MATCHSET[%d]: rssi_thold %d\n",
2518a5dc6883SDedy Lansky 			     i, ms->rssi_thold);
2519a5dc6883SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2520a5dc6883SDedy Lansky 				  ms->ssid.ssid,
2521a5dc6883SDedy Lansky 				  ms->ssid.ssid_len, true);
2522a5dc6883SDedy Lansky 	}
2523a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "n_scan_plans %d\n", request->n_scan_plans);
2524a5dc6883SDedy Lansky 	for (i = 0; i < request->n_scan_plans; i++) {
2525a5dc6883SDedy Lansky 		struct cfg80211_sched_scan_plan *sp = &request->scan_plans[i];
2526a5dc6883SDedy Lansky 
2527a5dc6883SDedy Lansky 		wil_dbg_misc(wil, "SCAN PLAN[%d]: interval %d iterations %d\n",
2528a5dc6883SDedy Lansky 			     i, sp->interval, sp->iterations);
2529a5dc6883SDedy Lansky 	}
2530a5dc6883SDedy Lansky 
2531e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
2532e00243faSLior David 			request->ie_len, request->ie);
2533a5dc6883SDedy Lansky 	if (rc)
2534a5dc6883SDedy Lansky 		return rc;
2535a5dc6883SDedy Lansky 	return wmi_start_sched_scan(wil, request);
2536a5dc6883SDedy Lansky }
2537a5dc6883SDedy Lansky 
2538a5dc6883SDedy Lansky static int
wil_cfg80211_sched_scan_stop(struct wiphy * wiphy,struct net_device * dev,u64 reqid)2539a5dc6883SDedy Lansky wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
2540a5dc6883SDedy Lansky 			     u64 reqid)
2541a5dc6883SDedy Lansky {
2542a5dc6883SDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2543e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2544a5dc6883SDedy Lansky 	int rc;
2545a5dc6883SDedy Lansky 
2546e00243faSLior David 	if (vif->mid != 0)
2547e00243faSLior David 		return -EOPNOTSUPP;
2548e00243faSLior David 
2549a5dc6883SDedy Lansky 	rc = wmi_stop_sched_scan(wil);
2550a5dc6883SDedy Lansky 	/* device would return error if it thinks PNO is already stopped.
2551a5dc6883SDedy Lansky 	 * ignore the return code so user space and driver gets back in-sync
2552a5dc6883SDedy Lansky 	 */
2553a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "sched scan stopped (%d)\n", rc);
2554a5dc6883SDedy Lansky 
2555a5dc6883SDedy Lansky 	return 0;
2556a5dc6883SDedy Lansky }
2557a5dc6883SDedy Lansky 
2558b9010f10SAhmad Masri static int
wil_cfg80211_update_ft_ies(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_update_ft_ies_params * ftie)2559b9010f10SAhmad Masri wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
2560b9010f10SAhmad Masri 			   struct cfg80211_update_ft_ies_params *ftie)
2561b9010f10SAhmad Masri {
2562b9010f10SAhmad Masri 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2563b9010f10SAhmad Masri 	struct wil6210_vif *vif = ndev_to_vif(dev);
2564b9010f10SAhmad Masri 	struct cfg80211_bss *bss;
2565b9010f10SAhmad Masri 	struct wmi_ft_reassoc_cmd reassoc;
2566b9010f10SAhmad Masri 	int rc = 0;
2567b9010f10SAhmad Masri 
2568b9010f10SAhmad Masri 	wil_dbg_misc(wil, "update ft ies, mid=%d\n", vif->mid);
2569b9010f10SAhmad Masri 	wil_hex_dump_misc("FT IE ", DUMP_PREFIX_OFFSET, 16, 1,
2570b9010f10SAhmad Masri 			  ftie->ie, ftie->ie_len, true);
2571b9010f10SAhmad Masri 
2572b9010f10SAhmad Masri 	if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
2573b9010f10SAhmad Masri 		wil_err(wil, "FW does not support FT roaming\n");
2574b9010f10SAhmad Masri 		return -EOPNOTSUPP;
2575b9010f10SAhmad Masri 	}
2576b9010f10SAhmad Masri 
2577b9010f10SAhmad Masri 	rc = wmi_update_ft_ies(vif, ftie->ie_len, ftie->ie);
2578b9010f10SAhmad Masri 	if (rc)
2579b9010f10SAhmad Masri 		return rc;
2580b9010f10SAhmad Masri 
2581b9010f10SAhmad Masri 	if (!test_bit(wil_vif_ft_roam, vif->status))
2582b9010f10SAhmad Masri 		/* vif is not roaming */
2583b9010f10SAhmad Masri 		return 0;
2584b9010f10SAhmad Masri 
2585b9010f10SAhmad Masri 	/* wil_vif_ft_roam is set. wil_cfg80211_update_ft_ies is used as
2586b9010f10SAhmad Masri 	 * a trigger for reassoc
2587b9010f10SAhmad Masri 	 */
2588b9010f10SAhmad Masri 
2589b9010f10SAhmad Masri 	bss = vif->bss;
2590b9010f10SAhmad Masri 	if (!bss) {
2591b9010f10SAhmad Masri 		wil_err(wil, "FT: bss is NULL\n");
2592b9010f10SAhmad Masri 		return -EINVAL;
2593b9010f10SAhmad Masri 	}
2594b9010f10SAhmad Masri 
2595b9010f10SAhmad Masri 	memset(&reassoc, 0, sizeof(reassoc));
2596b9010f10SAhmad Masri 	ether_addr_copy(reassoc.bssid, bss->bssid);
2597b9010f10SAhmad Masri 
2598b9010f10SAhmad Masri 	rc = wmi_send(wil, WMI_FT_REASSOC_CMDID, vif->mid,
2599b9010f10SAhmad Masri 		      &reassoc, sizeof(reassoc));
2600b9010f10SAhmad Masri 	if (rc)
2601b9010f10SAhmad Masri 		wil_err(wil, "FT: reassoc failed (%d)\n", rc);
2602b9010f10SAhmad Masri 
2603b9010f10SAhmad Masri 	return rc;
2604b9010f10SAhmad Masri }
2605b9010f10SAhmad Masri 
wil_cfg80211_set_multicast_to_unicast(struct wiphy * wiphy,struct net_device * dev,const bool enabled)26065e5f069cSAhmad Masri static int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy,
26075e5f069cSAhmad Masri 						 struct net_device *dev,
26085e5f069cSAhmad Masri 						 const bool enabled)
26095e5f069cSAhmad Masri {
26105e5f069cSAhmad Masri 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
26115e5f069cSAhmad Masri 
26125e5f069cSAhmad Masri 	if (wil->multicast_to_unicast == enabled)
26135e5f069cSAhmad Masri 		return 0;
26145e5f069cSAhmad Masri 
26155e5f069cSAhmad Masri 	wil_info(wil, "set multicast to unicast, enabled=%d\n", enabled);
26165e5f069cSAhmad Masri 	wil->multicast_to_unicast = enabled;
26175e5f069cSAhmad Masri 
26185e5f069cSAhmad Masri 	return 0;
26195e5f069cSAhmad Masri }
26205e5f069cSAhmad Masri 
wil_cfg80211_set_cqm_rssi_config(struct wiphy * wiphy,struct net_device * dev,s32 rssi_thold,u32 rssi_hyst)26214315a74aSDedy Lansky static int wil_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
26224315a74aSDedy Lansky 					    struct net_device *dev,
26234315a74aSDedy Lansky 					    s32 rssi_thold, u32 rssi_hyst)
26244315a74aSDedy Lansky {
26254315a74aSDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
26264315a74aSDedy Lansky 	int rc;
26274315a74aSDedy Lansky 
26284315a74aSDedy Lansky 	wil->cqm_rssi_thold = rssi_thold;
26294315a74aSDedy Lansky 
26304315a74aSDedy Lansky 	rc = wmi_set_cqm_rssi_config(wil, rssi_thold, rssi_hyst);
26314315a74aSDedy Lansky 	if (rc)
26324315a74aSDedy Lansky 		/* reset stored value upon failure */
26334315a74aSDedy Lansky 		wil->cqm_rssi_thold = 0;
26344315a74aSDedy Lansky 
26354315a74aSDedy Lansky 	return rc;
26364315a74aSDedy Lansky }
26374315a74aSDedy Lansky 
2638b59eb961SBhumika Goyal static const struct cfg80211_ops wil_cfg80211_ops = {
26394332cac1SLior David 	.add_virtual_intf = wil_cfg80211_add_iface,
26404332cac1SLior David 	.del_virtual_intf = wil_cfg80211_del_iface,
26412be7d22fSVladimir Kondratiev 	.scan = wil_cfg80211_scan,
2642035859a5SMaya Erez 	.abort_scan = wil_cfg80211_abort_scan,
26432be7d22fSVladimir Kondratiev 	.connect = wil_cfg80211_connect,
26442be7d22fSVladimir Kondratiev 	.disconnect = wil_cfg80211_disconnect,
26453fea18d0SLior David 	.set_wiphy_params = wil_cfg80211_set_wiphy_params,
26462be7d22fSVladimir Kondratiev 	.change_virtual_intf = wil_cfg80211_change_iface,
26472be7d22fSVladimir Kondratiev 	.get_station = wil_cfg80211_get_station,
2648ef28afdbSVladimir Kondratiev 	.dump_station = wil_cfg80211_dump_station,
26491647f12fSVladimir Kondratiev 	.remain_on_channel = wil_remain_on_channel,
26501647f12fSVladimir Kondratiev 	.cancel_remain_on_channel = wil_cancel_remain_on_channel,
26511647f12fSVladimir Kondratiev 	.mgmt_tx = wil_cfg80211_mgmt_tx,
26522be7d22fSVladimir Kondratiev 	.set_monitor_channel = wil_cfg80211_set_channel,
26532be7d22fSVladimir Kondratiev 	.add_key = wil_cfg80211_add_key,
26542be7d22fSVladimir Kondratiev 	.del_key = wil_cfg80211_del_key,
26552be7d22fSVladimir Kondratiev 	.set_default_key = wil_cfg80211_set_default_key,
26562be7d22fSVladimir Kondratiev 	/* AP mode */
26571bd922fcSVladimir Kondratiev 	.change_beacon = wil_cfg80211_change_beacon,
26582be7d22fSVladimir Kondratiev 	.start_ap = wil_cfg80211_start_ap,
26592be7d22fSVladimir Kondratiev 	.stop_ap = wil_cfg80211_stop_ap,
2660849a564bSDedy Lansky 	.add_station = wil_cfg80211_add_station,
26614d55a0a1SVladimir Kondratiev 	.del_station = wil_cfg80211_del_station,
2662849a564bSDedy Lansky 	.change_station = wil_cfg80211_change_station,
266340822a90SVladimir Kondratiev 	.probe_client = wil_cfg80211_probe_client,
266402beaf1aSVladimir Kondratiev 	.change_bss = wil_cfg80211_change_bss,
26654332cac1SLior David 	/* P2P device */
26664332cac1SLior David 	.start_p2p_device = wil_cfg80211_start_p2p_device,
26674332cac1SLior David 	.stop_p2p_device = wil_cfg80211_stop_p2p_device,
26682c207eb8SMaya Erez 	.set_power_mgmt = wil_cfg80211_set_power_mgmt,
26694315a74aSDedy Lansky 	.set_cqm_rssi_config = wil_cfg80211_set_cqm_rssi_config,
2670fe9ee51eSMaya Erez 	.suspend = wil_cfg80211_suspend,
2671fe9ee51eSMaya Erez 	.resume = wil_cfg80211_resume,
2672a5dc6883SDedy Lansky 	.sched_scan_start = wil_cfg80211_sched_scan_start,
2673a5dc6883SDedy Lansky 	.sched_scan_stop = wil_cfg80211_sched_scan_stop,
2674b9010f10SAhmad Masri 	.update_ft_ies = wil_cfg80211_update_ft_ies,
26755e5f069cSAhmad Masri 	.set_multicast_to_unicast = wil_cfg80211_set_multicast_to_unicast,
26762be7d22fSVladimir Kondratiev };
26772be7d22fSVladimir Kondratiev 
wil_wiphy_init(struct wiphy * wiphy)26782be7d22fSVladimir Kondratiev static void wil_wiphy_init(struct wiphy *wiphy)
26792be7d22fSVladimir Kondratiev {
26808e52fe30SHamad Kadmany 	wiphy->max_scan_ssids = 1;
268177c91295SVladimir Kondratiev 	wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
2682e6d68341SDedy Lansky 	wiphy->max_remain_on_channel_duration = WIL_MAX_ROC_DURATION_MS;
26832be7d22fSVladimir Kondratiev 	wiphy->max_num_pmkids = 0 /* TODO: */;
26842be7d22fSVladimir Kondratiev 	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
26852be7d22fSVladimir Kondratiev 				 BIT(NL80211_IFTYPE_AP) |
2686e6d68341SDedy Lansky 				 BIT(NL80211_IFTYPE_P2P_CLIENT) |
2687e6d68341SDedy Lansky 				 BIT(NL80211_IFTYPE_P2P_GO) |
26884332cac1SLior David 				 BIT(NL80211_IFTYPE_P2P_DEVICE) |
2689e6d68341SDedy Lansky 				 BIT(NL80211_IFTYPE_MONITOR);
2690849a564bSDedy Lansky 	wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
26912c207eb8SMaya Erez 			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
26922c207eb8SMaya Erez 			WIPHY_FLAG_PS_ON_BY_DEFAULT;
2693849a564bSDedy Lansky 	if (!disable_ap_sme)
2694849a564bSDedy Lansky 		wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
26959cf10d62SVladimir Kondratiev 	dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
26962be7d22fSVladimir Kondratiev 		__func__, wiphy->flags);
26972be7d22fSVladimir Kondratiev 	wiphy->probe_resp_offload =
26982be7d22fSVladimir Kondratiev 		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
26992be7d22fSVladimir Kondratiev 		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
27002be7d22fSVladimir Kondratiev 		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
27012be7d22fSVladimir Kondratiev 
270257fbcce3SJohannes Berg 	wiphy->bands[NL80211_BAND_60GHZ] = &wil_band_60ghz;
27032be7d22fSVladimir Kondratiev 
270430868f5dSDedy Lansky 	/* may change after reading FW capabilities */
2705b8b33a3aSVladimir Kondratiev 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
27062be7d22fSVladimir Kondratiev 
27072be7d22fSVladimir Kondratiev 	wiphy->cipher_suites = wil_cipher_suites;
27082be7d22fSVladimir Kondratiev 	wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
27092be7d22fSVladimir Kondratiev 	wiphy->mgmt_stypes = wil_mgmt_stypes;
2710713c8a29SVladimir Kondratiev 	wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
27110216a895SLior David 
27120216a895SLior David 	wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
27130216a895SLior David 	wiphy->vendor_commands = wil_nl80211_vendor_commands;
2714d1fbf075SMaya Erez 
2715d1fbf075SMaya Erez #ifdef CONFIG_PM
2716d1fbf075SMaya Erez 	wiphy->wowlan = &wil_wowlan_support;
2717d1fbf075SMaya Erez #endif
27182be7d22fSVladimir Kondratiev }
27192be7d22fSVladimir Kondratiev 
wil_cfg80211_iface_combinations_from_fw(struct wil6210_priv * wil,const struct wil_fw_record_concurrency * conc)27207bfe9e22SLior David int wil_cfg80211_iface_combinations_from_fw(
27217bfe9e22SLior David 	struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc)
27227bfe9e22SLior David {
27237bfe9e22SLior David 	struct wiphy *wiphy = wil_to_wiphy(wil);
27247bfe9e22SLior David 	u32 total_limits = 0;
27257bfe9e22SLior David 	u16 n_combos;
27267bfe9e22SLior David 	const struct wil_fw_concurrency_combo *combo;
27277bfe9e22SLior David 	const struct wil_fw_concurrency_limit *limit;
27287bfe9e22SLior David 	struct ieee80211_iface_combination *iface_combinations;
27297bfe9e22SLior David 	struct ieee80211_iface_limit *iface_limit;
27307bfe9e22SLior David 	int i, j;
27317bfe9e22SLior David 
27327bfe9e22SLior David 	if (wiphy->iface_combinations) {
27337bfe9e22SLior David 		wil_dbg_misc(wil, "iface_combinations already set, skipping\n");
27347bfe9e22SLior David 		return 0;
27357bfe9e22SLior David 	}
27367bfe9e22SLior David 
27377bfe9e22SLior David 	combo = conc->combos;
27387bfe9e22SLior David 	n_combos = le16_to_cpu(conc->n_combos);
27397bfe9e22SLior David 	for (i = 0; i < n_combos; i++) {
27407bfe9e22SLior David 		total_limits += combo->n_limits;
27417bfe9e22SLior David 		limit = combo->limits + combo->n_limits;
27427bfe9e22SLior David 		combo = (struct wil_fw_concurrency_combo *)limit;
27437bfe9e22SLior David 	}
27447bfe9e22SLior David 
27457bfe9e22SLior David 	iface_combinations =
27467bfe9e22SLior David 		kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) +
27477bfe9e22SLior David 			total_limits * sizeof(struct ieee80211_iface_limit),
27487bfe9e22SLior David 			GFP_KERNEL);
27497bfe9e22SLior David 	if (!iface_combinations)
27507bfe9e22SLior David 		return -ENOMEM;
27517bfe9e22SLior David 	iface_limit = (struct ieee80211_iface_limit *)(iface_combinations +
27527bfe9e22SLior David 						       n_combos);
27537bfe9e22SLior David 	combo = conc->combos;
27547bfe9e22SLior David 	for (i = 0; i < n_combos; i++) {
27557bfe9e22SLior David 		iface_combinations[i].max_interfaces = combo->max_interfaces;
27567bfe9e22SLior David 		iface_combinations[i].num_different_channels =
27577bfe9e22SLior David 			combo->n_diff_channels;
27587bfe9e22SLior David 		iface_combinations[i].beacon_int_infra_match =
27597bfe9e22SLior David 			combo->same_bi;
27607bfe9e22SLior David 		iface_combinations[i].n_limits = combo->n_limits;
27617bfe9e22SLior David 		wil_dbg_misc(wil,
27627bfe9e22SLior David 			     "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n",
27637bfe9e22SLior David 			     i, iface_combinations[i].max_interfaces,
27647bfe9e22SLior David 			     iface_combinations[i].num_different_channels,
27657bfe9e22SLior David 			     iface_combinations[i].beacon_int_infra_match);
27667bfe9e22SLior David 		limit = combo->limits;
27677bfe9e22SLior David 		for (j = 0; j < combo->n_limits; j++) {
27687bfe9e22SLior David 			iface_limit[j].max = le16_to_cpu(limit[j].max);
27697bfe9e22SLior David 			iface_limit[j].types = le16_to_cpu(limit[j].types);
27707bfe9e22SLior David 			wil_dbg_misc(wil,
27717bfe9e22SLior David 				     "limit %d: max %d types 0x%x\n", j,
27727bfe9e22SLior David 				     iface_limit[j].max, iface_limit[j].types);
27737bfe9e22SLior David 		}
27747bfe9e22SLior David 		iface_combinations[i].limits = iface_limit;
27757bfe9e22SLior David 		iface_limit += combo->n_limits;
27767bfe9e22SLior David 		limit += combo->n_limits;
27777bfe9e22SLior David 		combo = (struct wil_fw_concurrency_combo *)limit;
27787bfe9e22SLior David 	}
27797bfe9e22SLior David 
27804aebd3bdSLior David 	wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids);
27814aebd3bdSLior David 	wil->max_vifs = conc->n_mids + 1; /* including main interface */
27824aebd3bdSLior David 	if (wil->max_vifs > WIL_MAX_VIFS) {
27834aebd3bdSLior David 		wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n",
27844aebd3bdSLior David 			 WIL_MAX_VIFS, wil->max_vifs);
27854aebd3bdSLior David 		wil->max_vifs = WIL_MAX_VIFS;
27864aebd3bdSLior David 	}
27877bfe9e22SLior David 	wiphy->n_iface_combinations = n_combos;
27887bfe9e22SLior David 	wiphy->iface_combinations = iface_combinations;
27897bfe9e22SLior David 	return 0;
27907bfe9e22SLior David }
27917bfe9e22SLior David 
wil_cfg80211_init(struct device * dev)27929f38f286SLior David struct wil6210_priv *wil_cfg80211_init(struct device *dev)
27932be7d22fSVladimir Kondratiev {
27949f38f286SLior David 	struct wiphy *wiphy;
27959f38f286SLior David 	struct wil6210_priv *wil;
27969f38f286SLior David 	struct ieee80211_channel *ch;
27972be7d22fSVladimir Kondratiev 
27989cf10d62SVladimir Kondratiev 	dev_dbg(dev, "%s()\n", __func__);
27999cf10d62SVladimir Kondratiev 
28009f38f286SLior David 	/* Note: the wireless_dev structure is no longer allocated here.
28019f38f286SLior David 	 * Instead, it is allocated as part of the net_device structure
28029f38f286SLior David 	 * for main interface and each VIF.
28039f38f286SLior David 	 */
28049f38f286SLior David 	wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv));
28059f38f286SLior David 	if (!wiphy)
28062be7d22fSVladimir Kondratiev 		return ERR_PTR(-ENOMEM);
28072be7d22fSVladimir Kondratiev 
28089f38f286SLior David 	set_wiphy_dev(wiphy, dev);
28099f38f286SLior David 	wil_wiphy_init(wiphy);
28109f38f286SLior David 
28119f38f286SLior David 	wil = wiphy_to_wil(wiphy);
28129f38f286SLior David 	wil->wiphy = wiphy;
28139f38f286SLior David 
28149f38f286SLior David 	/* default monitor channel */
28159f38f286SLior David 	ch = wiphy->bands[NL80211_BAND_60GHZ]->channels;
28169f38f286SLior David 	cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT);
28179f38f286SLior David 
28189f38f286SLior David 	return wil;
28192be7d22fSVladimir Kondratiev }
28202be7d22fSVladimir Kondratiev 
wil_cfg80211_deinit(struct wil6210_priv * wil)28219f38f286SLior David void wil_cfg80211_deinit(struct wil6210_priv *wil)
28222be7d22fSVladimir Kondratiev {
28239f38f286SLior David 	struct wiphy *wiphy = wil_to_wiphy(wil);
28242be7d22fSVladimir Kondratiev 
28259cf10d62SVladimir Kondratiev 	dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
28269cf10d62SVladimir Kondratiev 
28279f38f286SLior David 	if (!wiphy)
28282be7d22fSVladimir Kondratiev 		return;
28292be7d22fSVladimir Kondratiev 
28307bfe9e22SLior David 	kfree(wiphy->iface_combinations);
28317bfe9e22SLior David 	wiphy->iface_combinations = NULL;
28327bfe9e22SLior David 
28339f38f286SLior David 	wiphy_free(wiphy);
28349f38f286SLior David 	/* do not access wil6210_priv after returning from here */
28352be7d22fSVladimir Kondratiev }
28364332cac1SLior David 
wil_p2p_wdev_free(struct wil6210_priv * wil)28374332cac1SLior David void wil_p2p_wdev_free(struct wil6210_priv *wil)
28384332cac1SLior David {
28394332cac1SLior David 	struct wireless_dev *p2p_wdev;
28404332cac1SLior David 
2841404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
28424332cac1SLior David 	p2p_wdev = wil->p2p_wdev;
28434332cac1SLior David 	wil->p2p_wdev = NULL;
2844e00243faSLior David 	wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
2845404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
2846d35c2b6fSMaya Erez 	if (p2p_wdev) {
28474332cac1SLior David 		cfg80211_unregister_wdev(p2p_wdev);
28485bd60982SLior David 		kfree(p2p_wdev);
28494332cac1SLior David 	}
28504332cac1SLior David }
28510216a895SLior David 
wil_rf_sector_status_to_rc(u8 status)28520216a895SLior David static int wil_rf_sector_status_to_rc(u8 status)
28530216a895SLior David {
28540216a895SLior David 	switch (status) {
28550216a895SLior David 	case WMI_RF_SECTOR_STATUS_SUCCESS:
28560216a895SLior David 		return 0;
28570216a895SLior David 	case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR:
28580216a895SLior David 		return -EINVAL;
28590216a895SLior David 	case WMI_RF_SECTOR_STATUS_BUSY_ERROR:
28600216a895SLior David 		return -EAGAIN;
28610216a895SLior David 	case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR:
28620216a895SLior David 		return -EOPNOTSUPP;
28630216a895SLior David 	default:
28640216a895SLior David 		return -EINVAL;
28650216a895SLior David 	}
28660216a895SLior David }
28670216a895SLior David 
wil_rf_sector_get_cfg(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)28680216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
28690216a895SLior David 				 struct wireless_dev *wdev,
28700216a895SLior David 				 const void *data, int data_len)
28710216a895SLior David {
28720216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
2873e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
28740216a895SLior David 	int rc;
28750216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
28760216a895SLior David 	u16 sector_index;
28770216a895SLior David 	u8 sector_type;
28780216a895SLior David 	u32 rf_modules_vec;
28790216a895SLior David 	struct wmi_get_rf_sector_params_cmd cmd;
28800216a895SLior David 	struct {
28810216a895SLior David 		struct wmi_cmd_hdr wmi;
28820216a895SLior David 		struct wmi_get_rf_sector_params_done_event evt;
2883807b0860SAlexei Avshalom Lazar 	} __packed reply = {
2884807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
2885807b0860SAlexei Avshalom Lazar 	};
28860216a895SLior David 	struct sk_buff *msg;
28870216a895SLior David 	struct nlattr *nl_cfgs, *nl_cfg;
28880216a895SLior David 	u32 i;
28890216a895SLior David 	struct wmi_rf_sector_info *si;
28900216a895SLior David 
28910216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
28920216a895SLior David 		return -EOPNOTSUPP;
28930216a895SLior David 
28948cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
28958cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
28960216a895SLior David 	if (rc) {
28970216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
28980216a895SLior David 		return rc;
28990216a895SLior David 	}
29000216a895SLior David 
29010216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
29020216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
29030216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) {
29040216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
29050216a895SLior David 		return -EINVAL;
29060216a895SLior David 	}
29070216a895SLior David 
29080216a895SLior David 	sector_index = nla_get_u16(
29090216a895SLior David 		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
29100216a895SLior David 	if (sector_index >= WIL_MAX_RF_SECTORS) {
29110216a895SLior David 		wil_err(wil, "Invalid sector index %d\n", sector_index);
29120216a895SLior David 		return -EINVAL;
29130216a895SLior David 	}
29140216a895SLior David 
29150216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
29160216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
29170216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
29180216a895SLior David 		return -EINVAL;
29190216a895SLior David 	}
29200216a895SLior David 
29210216a895SLior David 	rf_modules_vec = nla_get_u32(
29220216a895SLior David 		tb[QCA_ATTR_DMG_RF_MODULE_MASK]);
29230216a895SLior David 	if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) {
29240216a895SLior David 		wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec);
29250216a895SLior David 		return -EINVAL;
29260216a895SLior David 	}
29270216a895SLior David 
29280216a895SLior David 	cmd.sector_idx = cpu_to_le16(sector_index);
29290216a895SLior David 	cmd.sector_type = sector_type;
29300216a895SLior David 	cmd.rf_modules_vec = rf_modules_vec & 0xFF;
2931e00243faSLior David 	rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid,
2932e00243faSLior David 		      &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
29330216a895SLior David 		      &reply, sizeof(reply),
29340216a895SLior David 		      500);
29350216a895SLior David 	if (rc)
29360216a895SLior David 		return rc;
29370216a895SLior David 	if (reply.evt.status) {
29380216a895SLior David 		wil_err(wil, "get rf sector cfg failed with status %d\n",
29390216a895SLior David 			reply.evt.status);
29400216a895SLior David 		return wil_rf_sector_status_to_rc(reply.evt.status);
29410216a895SLior David 	}
29420216a895SLior David 
29430216a895SLior David 	msg = cfg80211_vendor_cmd_alloc_reply_skb(
29440216a895SLior David 		wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
29450216a895SLior David 	if (!msg)
29460216a895SLior David 		return -ENOMEM;
29470216a895SLior David 
29480216a895SLior David 	if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
29490216a895SLior David 			      le64_to_cpu(reply.evt.tsf),
29500216a895SLior David 			      QCA_ATTR_PAD))
29510216a895SLior David 		goto nla_put_failure;
29520216a895SLior David 
2953ae0be8deSMichal Kubecek 	nl_cfgs = nla_nest_start_noflag(msg, QCA_ATTR_DMG_RF_SECTOR_CFG);
29540216a895SLior David 	if (!nl_cfgs)
29550216a895SLior David 		goto nla_put_failure;
29560216a895SLior David 	for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) {
29570216a895SLior David 		if (!(rf_modules_vec & BIT(i)))
29580216a895SLior David 			continue;
2959ae0be8deSMichal Kubecek 		nl_cfg = nla_nest_start_noflag(msg, i);
29600216a895SLior David 		if (!nl_cfg)
29610216a895SLior David 			goto nla_put_failure;
29620216a895SLior David 		si = &reply.evt.sectors_info[i];
29630216a895SLior David 		if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
29640216a895SLior David 			       i) ||
29650216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
29660216a895SLior David 				le32_to_cpu(si->etype0)) ||
29670216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
29680216a895SLior David 				le32_to_cpu(si->etype1)) ||
29690216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
29700216a895SLior David 				le32_to_cpu(si->etype2)) ||
29710216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
29720216a895SLior David 				le32_to_cpu(si->psh_hi)) ||
29730216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
29740216a895SLior David 				le32_to_cpu(si->psh_lo)) ||
29750216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
29760216a895SLior David 				le32_to_cpu(si->dtype_swch_off)))
29770216a895SLior David 			goto nla_put_failure;
29780216a895SLior David 		nla_nest_end(msg, nl_cfg);
29790216a895SLior David 	}
29800216a895SLior David 
29810216a895SLior David 	nla_nest_end(msg, nl_cfgs);
29820216a895SLior David 	rc = cfg80211_vendor_cmd_reply(msg);
29830216a895SLior David 	return rc;
29840216a895SLior David nla_put_failure:
29850216a895SLior David 	kfree_skb(msg);
29860216a895SLior David 	return -ENOBUFS;
29870216a895SLior David }
29880216a895SLior David 
wil_rf_sector_set_cfg(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)29890216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
29900216a895SLior David 				 struct wireless_dev *wdev,
29910216a895SLior David 				 const void *data, int data_len)
29920216a895SLior David {
29930216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
2994e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
29950216a895SLior David 	int rc, tmp;
29960216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
29970216a895SLior David 	struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
29980216a895SLior David 	u16 sector_index, rf_module_index;
29990216a895SLior David 	u8 sector_type;
30000216a895SLior David 	u32 rf_modules_vec = 0;
30010216a895SLior David 	struct wmi_set_rf_sector_params_cmd cmd;
30020216a895SLior David 	struct {
30030216a895SLior David 		struct wmi_cmd_hdr wmi;
30040216a895SLior David 		struct wmi_set_rf_sector_params_done_event evt;
3005807b0860SAlexei Avshalom Lazar 	} __packed reply = {
3006807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3007807b0860SAlexei Avshalom Lazar 	};
30080216a895SLior David 	struct nlattr *nl_cfg;
30090216a895SLior David 	struct wmi_rf_sector_info *si;
30100216a895SLior David 
30110216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
30120216a895SLior David 		return -EOPNOTSUPP;
30130216a895SLior David 
30148cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
30158cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
30160216a895SLior David 	if (rc) {
30170216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
30180216a895SLior David 		return rc;
30190216a895SLior David 	}
30200216a895SLior David 
30210216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
30220216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
30230216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) {
30240216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
30250216a895SLior David 		return -EINVAL;
30260216a895SLior David 	}
30270216a895SLior David 
30280216a895SLior David 	sector_index = nla_get_u16(
30290216a895SLior David 		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
30300216a895SLior David 	if (sector_index >= WIL_MAX_RF_SECTORS) {
30310216a895SLior David 		wil_err(wil, "Invalid sector index %d\n", sector_index);
30320216a895SLior David 		return -EINVAL;
30330216a895SLior David 	}
30340216a895SLior David 
30350216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
30360216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
30370216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
30380216a895SLior David 		return -EINVAL;
30390216a895SLior David 	}
30400216a895SLior David 
30410216a895SLior David 	memset(&cmd, 0, sizeof(cmd));
30420216a895SLior David 
30430216a895SLior David 	cmd.sector_idx = cpu_to_le16(sector_index);
30440216a895SLior David 	cmd.sector_type = sector_type;
30450216a895SLior David 	nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG],
30460216a895SLior David 			    tmp) {
30478cb08174SJohannes Berg 		rc = nla_parse_nested_deprecated(tb2,
30488cb08174SJohannes Berg 						 QCA_ATTR_DMG_RF_SECTOR_CFG_MAX,
30498cb08174SJohannes Berg 						 nl_cfg,
30508cb08174SJohannes Berg 						 wil_rf_sector_cfg_policy,
30510216a895SLior David 						 NULL);
30520216a895SLior David 		if (rc) {
30530216a895SLior David 			wil_err(wil, "invalid sector cfg\n");
30540216a895SLior David 			return -EINVAL;
30550216a895SLior David 		}
30560216a895SLior David 
30570216a895SLior David 		if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] ||
30580216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] ||
30590216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] ||
30600216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] ||
30610216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] ||
30620216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] ||
30630216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) {
30640216a895SLior David 			wil_err(wil, "missing cfg params\n");
30650216a895SLior David 			return -EINVAL;
30660216a895SLior David 		}
30670216a895SLior David 
30680216a895SLior David 		rf_module_index = nla_get_u8(
30690216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]);
30700216a895SLior David 		if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) {
30710216a895SLior David 			wil_err(wil, "invalid RF module index %d\n",
30720216a895SLior David 				rf_module_index);
30730216a895SLior David 			return -EINVAL;
30740216a895SLior David 		}
30750216a895SLior David 		rf_modules_vec |= BIT(rf_module_index);
30760216a895SLior David 		si = &cmd.sectors_info[rf_module_index];
30770216a895SLior David 		si->etype0 = cpu_to_le32(nla_get_u32(
30780216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0]));
30790216a895SLior David 		si->etype1 = cpu_to_le32(nla_get_u32(
30800216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1]));
30810216a895SLior David 		si->etype2 = cpu_to_le32(nla_get_u32(
30820216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2]));
30830216a895SLior David 		si->psh_hi = cpu_to_le32(nla_get_u32(
30840216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI]));
30850216a895SLior David 		si->psh_lo = cpu_to_le32(nla_get_u32(
30860216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO]));
30870216a895SLior David 		si->dtype_swch_off = cpu_to_le32(nla_get_u32(
30880216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]));
30890216a895SLior David 	}
30900216a895SLior David 
30910216a895SLior David 	cmd.rf_modules_vec = rf_modules_vec & 0xFF;
3092e00243faSLior David 	rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid,
3093e00243faSLior David 		      &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
30940216a895SLior David 		      &reply, sizeof(reply),
30950216a895SLior David 		      500);
30960216a895SLior David 	if (rc)
30970216a895SLior David 		return rc;
30980216a895SLior David 	return wil_rf_sector_status_to_rc(reply.evt.status);
30990216a895SLior David }
31000216a895SLior David 
wil_rf_sector_get_selected(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)31010216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy,
31020216a895SLior David 				      struct wireless_dev *wdev,
31030216a895SLior David 				      const void *data, int data_len)
31040216a895SLior David {
31050216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
3106e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
31070216a895SLior David 	int rc;
31080216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
31090216a895SLior David 	u8 sector_type, mac_addr[ETH_ALEN];
31100216a895SLior David 	int cid = 0;
31110216a895SLior David 	struct wmi_get_selected_rf_sector_index_cmd cmd;
31120216a895SLior David 	struct {
31130216a895SLior David 		struct wmi_cmd_hdr wmi;
31140216a895SLior David 		struct wmi_get_selected_rf_sector_index_done_event evt;
3115807b0860SAlexei Avshalom Lazar 	} __packed reply = {
3116807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3117807b0860SAlexei Avshalom Lazar 	};
31180216a895SLior David 	struct sk_buff *msg;
31190216a895SLior David 
31200216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
31210216a895SLior David 		return -EOPNOTSUPP;
31220216a895SLior David 
31238cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
31248cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
31250216a895SLior David 	if (rc) {
31260216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
31270216a895SLior David 		return rc;
31280216a895SLior David 	}
31290216a895SLior David 
31300216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
31310216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
31320216a895SLior David 		return -EINVAL;
31330216a895SLior David 	}
31340216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
31350216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
31360216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
31370216a895SLior David 		return -EINVAL;
31380216a895SLior David 	}
31390216a895SLior David 
31400216a895SLior David 	if (tb[QCA_ATTR_MAC_ADDR]) {
31410216a895SLior David 		ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
3142e00243faSLior David 		cid = wil_find_cid(wil, vif->mid, mac_addr);
31430216a895SLior David 		if (cid < 0) {
31440216a895SLior David 			wil_err(wil, "invalid MAC address %pM\n", mac_addr);
31450216a895SLior David 			return -ENOENT;
31460216a895SLior David 		}
31470216a895SLior David 	} else {
31485bd60982SLior David 		if (test_bit(wil_vif_fwconnected, vif->status)) {
31490216a895SLior David 			wil_err(wil, "must specify MAC address when connected\n");
31500216a895SLior David 			return -EINVAL;
31510216a895SLior David 		}
31520216a895SLior David 	}
31530216a895SLior David 
31540216a895SLior David 	memset(&cmd, 0, sizeof(cmd));
31550216a895SLior David 	cmd.cid = (u8)cid;
31560216a895SLior David 	cmd.sector_type = sector_type;
3157e00243faSLior David 	rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid,
31580216a895SLior David 		      &cmd, sizeof(cmd),
31590216a895SLior David 		      WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
31600216a895SLior David 		      &reply, sizeof(reply),
31610216a895SLior David 		      500);
31620216a895SLior David 	if (rc)
31630216a895SLior David 		return rc;
31640216a895SLior David 	if (reply.evt.status) {
31650216a895SLior David 		wil_err(wil, "get rf selected sector cfg failed with status %d\n",
31660216a895SLior David 			reply.evt.status);
31670216a895SLior David 		return wil_rf_sector_status_to_rc(reply.evt.status);
31680216a895SLior David 	}
31690216a895SLior David 
31700216a895SLior David 	msg = cfg80211_vendor_cmd_alloc_reply_skb(
31710216a895SLior David 		wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
31720216a895SLior David 	if (!msg)
31730216a895SLior David 		return -ENOMEM;
31740216a895SLior David 
31750216a895SLior David 	if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
31760216a895SLior David 			      le64_to_cpu(reply.evt.tsf),
31770216a895SLior David 			      QCA_ATTR_PAD) ||
31780216a895SLior David 	    nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX,
31790216a895SLior David 			le16_to_cpu(reply.evt.sector_idx)))
31800216a895SLior David 		goto nla_put_failure;
31810216a895SLior David 
31820216a895SLior David 	rc = cfg80211_vendor_cmd_reply(msg);
31830216a895SLior David 	return rc;
31840216a895SLior David nla_put_failure:
31850216a895SLior David 	kfree_skb(msg);
31860216a895SLior David 	return -ENOBUFS;
31870216a895SLior David }
31880216a895SLior David 
wil_rf_sector_wmi_set_selected(struct wil6210_priv * wil,u8 mid,u16 sector_index,u8 sector_type,u8 cid)31890216a895SLior David static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
3190e00243faSLior David 					  u8 mid, u16 sector_index,
31910216a895SLior David 					  u8 sector_type, u8 cid)
31920216a895SLior David {
31930216a895SLior David 	struct wmi_set_selected_rf_sector_index_cmd cmd;
31940216a895SLior David 	struct {
31950216a895SLior David 		struct wmi_cmd_hdr wmi;
31960216a895SLior David 		struct wmi_set_selected_rf_sector_index_done_event evt;
3197807b0860SAlexei Avshalom Lazar 	} __packed reply = {
3198807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3199807b0860SAlexei Avshalom Lazar 	};
32000216a895SLior David 	int rc;
32010216a895SLior David 
32020216a895SLior David 	memset(&cmd, 0, sizeof(cmd));
32030216a895SLior David 	cmd.sector_idx = cpu_to_le16(sector_index);
32040216a895SLior David 	cmd.sector_type = sector_type;
32050216a895SLior David 	cmd.cid = (u8)cid;
3206e00243faSLior David 	rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid,
32070216a895SLior David 		      &cmd, sizeof(cmd),
32080216a895SLior David 		      WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
32090216a895SLior David 		      &reply, sizeof(reply),
32100216a895SLior David 		      500);
32110216a895SLior David 	if (rc)
32120216a895SLior David 		return rc;
32130216a895SLior David 	return wil_rf_sector_status_to_rc(reply.evt.status);
32140216a895SLior David }
32150216a895SLior David 
wil_rf_sector_set_selected(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)32160216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy,
32170216a895SLior David 				      struct wireless_dev *wdev,
32180216a895SLior David 				      const void *data, int data_len)
32190216a895SLior David {
32200216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
3221e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
32220216a895SLior David 	int rc;
32230216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
32240216a895SLior David 	u16 sector_index;
32250216a895SLior David 	u8 sector_type, mac_addr[ETH_ALEN], i;
32260216a895SLior David 	int cid = 0;
32270216a895SLior David 
32280216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
32290216a895SLior David 		return -EOPNOTSUPP;
32300216a895SLior David 
32318cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
32328cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
32330216a895SLior David 	if (rc) {
32340216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
32350216a895SLior David 		return rc;
32360216a895SLior David 	}
32370216a895SLior David 
32380216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
32390216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
32400216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
32410216a895SLior David 		return -EINVAL;
32420216a895SLior David 	}
32430216a895SLior David 
32440216a895SLior David 	sector_index = nla_get_u16(
32450216a895SLior David 		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
32460216a895SLior David 	if (sector_index >= WIL_MAX_RF_SECTORS &&
32470216a895SLior David 	    sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
32480216a895SLior David 		wil_err(wil, "Invalid sector index %d\n", sector_index);
32490216a895SLior David 		return -EINVAL;
32500216a895SLior David 	}
32510216a895SLior David 
32520216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
32530216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
32540216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
32550216a895SLior David 		return -EINVAL;
32560216a895SLior David 	}
32570216a895SLior David 
32580216a895SLior David 	if (tb[QCA_ATTR_MAC_ADDR]) {
32590216a895SLior David 		ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
32600216a895SLior David 		if (!is_broadcast_ether_addr(mac_addr)) {
3261e00243faSLior David 			cid = wil_find_cid(wil, vif->mid, mac_addr);
32620216a895SLior David 			if (cid < 0) {
32630216a895SLior David 				wil_err(wil, "invalid MAC address %pM\n",
32640216a895SLior David 					mac_addr);
32650216a895SLior David 				return -ENOENT;
32660216a895SLior David 			}
32670216a895SLior David 		} else {
32680216a895SLior David 			if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
32690216a895SLior David 				wil_err(wil, "broadcast MAC valid only with unlocking\n");
32700216a895SLior David 				return -EINVAL;
32710216a895SLior David 			}
32720216a895SLior David 			cid = -1;
32730216a895SLior David 		}
32740216a895SLior David 	} else {
32755bd60982SLior David 		if (test_bit(wil_vif_fwconnected, vif->status)) {
32760216a895SLior David 			wil_err(wil, "must specify MAC address when connected\n");
32770216a895SLior David 			return -EINVAL;
32780216a895SLior David 		}
32790216a895SLior David 		/* otherwise, using cid=0 for unassociated station */
32800216a895SLior David 	}
32810216a895SLior David 
32820216a895SLior David 	if (cid >= 0) {
3283e00243faSLior David 		rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index,
32840216a895SLior David 						    sector_type, cid);
32850216a895SLior David 	} else {
32860216a895SLior David 		/* unlock all cids */
32870216a895SLior David 		rc = wil_rf_sector_wmi_set_selected(
3288e00243faSLior David 			wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX,
3289e00243faSLior David 			sector_type, WIL_CID_ALL);
32900216a895SLior David 		if (rc == -EINVAL) {
3291ddf7afddSAhmad Masri 			for (i = 0; i < wil->max_assoc_sta; i++) {
3292e00243faSLior David 				if (wil->sta[i].mid != vif->mid)
3293e00243faSLior David 					continue;
32940216a895SLior David 				rc = wil_rf_sector_wmi_set_selected(
3295e00243faSLior David 					wil, vif->mid,
3296e00243faSLior David 					WMI_INVALID_RF_SECTOR_INDEX,
32970216a895SLior David 					sector_type, i);
32980216a895SLior David 				/* the FW will silently ignore and return
32990216a895SLior David 				 * success for unused cid, so abort the loop
33000216a895SLior David 				 * on any other error
33010216a895SLior David 				 */
33020216a895SLior David 				if (rc) {
33030216a895SLior David 					wil_err(wil, "unlock cid %d failed with status %d\n",
33040216a895SLior David 						i, rc);
33050216a895SLior David 					break;
33060216a895SLior David 				}
33070216a895SLior David 			}
33080216a895SLior David 		}
33090216a895SLior David 	}
33100216a895SLior David 
33110216a895SLior David 	return rc;
33120216a895SLior David }
3313