12be7d22fSVladimir Kondratiev /*
2849a564bSDedy Lansky  * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
3bf0353a6SAhmad Masri  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
42be7d22fSVladimir Kondratiev  *
52be7d22fSVladimir Kondratiev  * Permission to use, copy, modify, and/or distribute this software for any
62be7d22fSVladimir Kondratiev  * purpose with or without fee is hereby granted, provided that the above
72be7d22fSVladimir Kondratiev  * copyright notice and this permission notice appear in all copies.
82be7d22fSVladimir Kondratiev  *
92be7d22fSVladimir Kondratiev  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
102be7d22fSVladimir Kondratiev  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
112be7d22fSVladimir Kondratiev  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
122be7d22fSVladimir Kondratiev  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
132be7d22fSVladimir Kondratiev  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
142be7d22fSVladimir Kondratiev  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
152be7d22fSVladimir Kondratiev  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
162be7d22fSVladimir Kondratiev  */
172be7d22fSVladimir Kondratiev 
18a82553bbSVladimir Kondratiev #include <linux/etherdevice.h>
19949c2d00SJohannes Berg #include <linux/moduleparam.h>
200216a895SLior David #include <net/netlink.h>
214aebd3bdSLior David #include <net/cfg80211.h>
222be7d22fSVladimir Kondratiev #include "wil6210.h"
232be7d22fSVladimir Kondratiev #include "wmi.h"
247bfe9e22SLior David #include "fw.h"
252be7d22fSVladimir Kondratiev 
26e6d68341SDedy Lansky #define WIL_MAX_ROC_DURATION_MS 5000
27e6d68341SDedy Lansky 
289abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_9_SUBCHANNELS	(BIT(0) | BIT(1))
299abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_10_SUBCHANNELS	(BIT(1) | BIT(2))
309abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_11_SUBCHANNELS	(BIT(2) | BIT(3))
319abe3e30SAlexei Avshalom Lazar 
329abe3e30SAlexei Avshalom Lazar /* WIL_EDMG_BW_CONFIGURATION define the allowed channel bandwidth
339abe3e30SAlexei Avshalom Lazar  * configurations as defined by IEEE 802.11 section 9.4.2.251, Table 13.
349abe3e30SAlexei Avshalom Lazar  * The value 5 allowing CB1 and CB2 of adjacent channels.
359abe3e30SAlexei Avshalom Lazar  */
369abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_BW_CONFIGURATION 5
379abe3e30SAlexei Avshalom Lazar 
389abe3e30SAlexei Avshalom Lazar /* WIL_EDMG_CHANNELS is a bitmap that indicates the 2.16 GHz channel(s) that
399abe3e30SAlexei Avshalom Lazar  * are allowed to be used for EDMG transmissions in the BSS as defined by
409abe3e30SAlexei Avshalom Lazar  * IEEE 802.11 section 9.4.2.251.
419abe3e30SAlexei Avshalom Lazar  */
429abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNELS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
439abe3e30SAlexei Avshalom Lazar 
44849a564bSDedy Lansky bool disable_ap_sme;
4578484c44SMaya Erez module_param(disable_ap_sme, bool, 0444);
46849a564bSDedy Lansky MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
47849a564bSDedy Lansky 
48d1fbf075SMaya Erez #ifdef CONFIG_PM
49d1fbf075SMaya Erez static struct wiphy_wowlan_support wil_wowlan_support = {
50d1fbf075SMaya Erez 	.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
51d1fbf075SMaya Erez };
52d1fbf075SMaya Erez #endif
53d1fbf075SMaya Erez 
542be7d22fSVladimir Kondratiev #define CHAN60G(_channel, _flags) {				\
5557fbcce3SJohannes Berg 	.band			= NL80211_BAND_60GHZ,		\
562be7d22fSVladimir Kondratiev 	.center_freq		= 56160 + (2160 * (_channel)),	\
572be7d22fSVladimir Kondratiev 	.hw_value		= (_channel),			\
582be7d22fSVladimir Kondratiev 	.flags			= (_flags),			\
592be7d22fSVladimir Kondratiev 	.max_antenna_gain	= 0,				\
602be7d22fSVladimir Kondratiev 	.max_power		= 40,				\
612be7d22fSVladimir Kondratiev }
622be7d22fSVladimir Kondratiev 
632be7d22fSVladimir Kondratiev static struct ieee80211_channel wil_60ghz_channels[] = {
642be7d22fSVladimir Kondratiev 	CHAN60G(1, 0),
652be7d22fSVladimir Kondratiev 	CHAN60G(2, 0),
662be7d22fSVladimir Kondratiev 	CHAN60G(3, 0),
6722b9610eSAlexei Avshalom Lazar 	CHAN60G(4, 0),
682be7d22fSVladimir Kondratiev };
692be7d22fSVladimir Kondratiev 
709abe3e30SAlexei Avshalom Lazar /* Rx channel bonding mode */
719abe3e30SAlexei Avshalom Lazar enum wil_rx_cb_mode {
729abe3e30SAlexei Avshalom Lazar 	WIL_RX_CB_MODE_DMG,
739abe3e30SAlexei Avshalom Lazar 	WIL_RX_CB_MODE_EDMG,
749abe3e30SAlexei Avshalom Lazar 	WIL_RX_CB_MODE_WIDE,
759abe3e30SAlexei Avshalom Lazar };
769abe3e30SAlexei Avshalom Lazar 
779abe3e30SAlexei Avshalom Lazar static int wil_rx_cb_mode_to_n_bonded(u8 cb_mode)
789abe3e30SAlexei Avshalom Lazar {
799abe3e30SAlexei Avshalom Lazar 	switch (cb_mode) {
809abe3e30SAlexei Avshalom Lazar 	case WIL_RX_CB_MODE_DMG:
819abe3e30SAlexei Avshalom Lazar 	case WIL_RX_CB_MODE_EDMG:
829abe3e30SAlexei Avshalom Lazar 		return 1;
839abe3e30SAlexei Avshalom Lazar 	case WIL_RX_CB_MODE_WIDE:
849abe3e30SAlexei Avshalom Lazar 		return 2;
859abe3e30SAlexei Avshalom Lazar 	default:
869abe3e30SAlexei Avshalom Lazar 		return 1;
879abe3e30SAlexei Avshalom Lazar 	}
889abe3e30SAlexei Avshalom Lazar }
899abe3e30SAlexei Avshalom Lazar 
909abe3e30SAlexei Avshalom Lazar static int wil_tx_cb_mode_to_n_bonded(u8 cb_mode)
919abe3e30SAlexei Avshalom Lazar {
929abe3e30SAlexei Avshalom Lazar 	switch (cb_mode) {
939abe3e30SAlexei Avshalom Lazar 	case WMI_TX_MODE_DMG:
949abe3e30SAlexei Avshalom Lazar 	case WMI_TX_MODE_EDMG_CB1:
959abe3e30SAlexei Avshalom Lazar 		return 1;
969abe3e30SAlexei Avshalom Lazar 	case WMI_TX_MODE_EDMG_CB2:
979abe3e30SAlexei Avshalom Lazar 		return 2;
989abe3e30SAlexei Avshalom Lazar 	default:
999abe3e30SAlexei Avshalom Lazar 		return 1;
1009abe3e30SAlexei Avshalom Lazar 	}
1019abe3e30SAlexei Avshalom Lazar }
1029abe3e30SAlexei Avshalom Lazar 
103e41ab937SDedy Lansky static void
104e41ab937SDedy Lansky wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
105e41ab937SDedy Lansky {
106e41ab937SDedy Lansky 	kfree(*pdst);
107e41ab937SDedy Lansky 	*pdst = NULL;
108e41ab937SDedy Lansky 	*pdst_len = 0;
109e41ab937SDedy Lansky 	if (src_len > 0) {
110e41ab937SDedy Lansky 		*pdst = kmemdup(src, src_len, GFP_KERNEL);
111e41ab937SDedy Lansky 		if (*pdst)
112e41ab937SDedy Lansky 			*pdst_len = src_len;
113e41ab937SDedy Lansky 	}
114e41ab937SDedy Lansky }
115e41ab937SDedy Lansky 
11622b9610eSAlexei Avshalom Lazar static int wil_num_supported_channels(struct wil6210_priv *wil)
11722b9610eSAlexei Avshalom Lazar {
11822b9610eSAlexei Avshalom Lazar 	int num_channels = ARRAY_SIZE(wil_60ghz_channels);
11922b9610eSAlexei Avshalom Lazar 
12022b9610eSAlexei Avshalom Lazar 	if (!test_bit(WMI_FW_CAPABILITY_CHANNEL_4, wil->fw_capabilities))
12122b9610eSAlexei Avshalom Lazar 		num_channels--;
12222b9610eSAlexei Avshalom Lazar 
12322b9610eSAlexei Avshalom Lazar 	return num_channels;
12422b9610eSAlexei Avshalom Lazar }
12522b9610eSAlexei Avshalom Lazar 
12622b9610eSAlexei Avshalom Lazar void update_supported_bands(struct wil6210_priv *wil)
12722b9610eSAlexei Avshalom Lazar {
12822b9610eSAlexei Avshalom Lazar 	struct wiphy *wiphy = wil_to_wiphy(wil);
12922b9610eSAlexei Avshalom Lazar 
13022b9610eSAlexei Avshalom Lazar 	wil_dbg_misc(wil, "update supported bands");
13122b9610eSAlexei Avshalom Lazar 
13222b9610eSAlexei Avshalom Lazar 	wiphy->bands[NL80211_BAND_60GHZ]->n_channels =
13322b9610eSAlexei Avshalom Lazar 						wil_num_supported_channels(wil);
1349abe3e30SAlexei Avshalom Lazar 
1359abe3e30SAlexei Avshalom Lazar 	if (test_bit(WMI_FW_CAPABILITY_CHANNEL_BONDING, wil->fw_capabilities)) {
1369abe3e30SAlexei Avshalom Lazar 		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.channels =
1379abe3e30SAlexei Avshalom Lazar 							WIL_EDMG_CHANNELS;
1389abe3e30SAlexei Avshalom Lazar 		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.bw_config =
1399abe3e30SAlexei Avshalom Lazar 						      WIL_EDMG_BW_CONFIGURATION;
1409abe3e30SAlexei Avshalom Lazar 	}
14122b9610eSAlexei Avshalom Lazar }
14222b9610eSAlexei Avshalom Lazar 
1430216a895SLior David /* Vendor id to be used in vendor specific command and events
1440216a895SLior David  * to user space.
1450216a895SLior David  * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID,
1460216a895SLior David  * vendor subcmd definitions prefixed with QCA_NL80211_VENDOR_SUBCMD, and
1470216a895SLior David  * qca_wlan_vendor_attr is open source file src/common/qca-vendor.h in
1480216a895SLior David  * git://w1.fi/srv/git/hostap.git; the values here are just a copy of that
1490216a895SLior David  */
1500216a895SLior David 
1510216a895SLior David #define QCA_NL80211_VENDOR_ID	0x001374
1520216a895SLior David 
1530216a895SLior David #define WIL_MAX_RF_SECTORS (128)
1540216a895SLior David #define WIL_CID_ALL (0xff)
1550216a895SLior David 
1560216a895SLior David enum qca_wlan_vendor_attr_rf_sector {
1570216a895SLior David 	QCA_ATTR_MAC_ADDR = 6,
1580216a895SLior David 	QCA_ATTR_PAD = 13,
1590216a895SLior David 	QCA_ATTR_TSF = 29,
1600216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_INDEX = 30,
1610216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE = 31,
1620216a895SLior David 	QCA_ATTR_DMG_RF_MODULE_MASK = 32,
1630216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG = 33,
1640216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_MAX,
1650216a895SLior David };
1660216a895SLior David 
1670216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_type {
1680216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE_RX,
1690216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE_TX,
1700216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX
1710216a895SLior David };
1720216a895SLior David 
1730216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
1740216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
1750216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
1760216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
1770216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
1780216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
1790216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
1800216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
1810216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
1820216a895SLior David 
1830216a895SLior David 	/* keep last */
1840216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
1850216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_MAX =
1860216a895SLior David 	QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
1870216a895SLior David };
1880216a895SLior David 
1890216a895SLior David static const struct
1900216a895SLior David nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = {
1910216a895SLior David 	[QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN },
1920216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 },
1930216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 },
1940216a895SLior David 	[QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 },
1950216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED },
1960216a895SLior David };
1970216a895SLior David 
1980216a895SLior David static const struct
1990216a895SLior David nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = {
2000216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 },
2010216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 },
2020216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 },
2030216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 },
2040216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 },
2050216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 },
2060216a895SLior David 	[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 },
2070216a895SLior David };
2080216a895SLior David 
2090216a895SLior David enum qca_nl80211_vendor_subcmds {
2100216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
2110216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
2120216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
2130216a895SLior David 	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
2140216a895SLior David };
2150216a895SLior David 
2160216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
2170216a895SLior David 				 struct wireless_dev *wdev,
2180216a895SLior David 				 const void *data, int data_len);
2190216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
2200216a895SLior David 				 struct wireless_dev *wdev,
2210216a895SLior David 				 const void *data, int data_len);
2220216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy,
2230216a895SLior David 				      struct wireless_dev *wdev,
2240216a895SLior David 				      const void *data, int data_len);
2250216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy,
2260216a895SLior David 				      struct wireless_dev *wdev,
2270216a895SLior David 				      const void *data, int data_len);
2280216a895SLior David 
2290216a895SLior David /* vendor specific commands */
2300216a895SLior David static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
2310216a895SLior David 	{
2320216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2330216a895SLior David 		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG,
2340216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2350216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2361667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2370216a895SLior David 		.doit = wil_rf_sector_get_cfg
2380216a895SLior David 	},
2390216a895SLior David 	{
2400216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2410216a895SLior David 		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG,
2420216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2430216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2441667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2450216a895SLior David 		.doit = wil_rf_sector_set_cfg
2460216a895SLior David 	},
2470216a895SLior David 	{
2480216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2490216a895SLior David 		.info.subcmd =
2500216a895SLior David 			QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR,
2510216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2520216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2531667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2540216a895SLior David 		.doit = wil_rf_sector_get_selected
2550216a895SLior David 	},
2560216a895SLior David 	{
2570216a895SLior David 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
2580216a895SLior David 		.info.subcmd =
2590216a895SLior David 			QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR,
2600216a895SLior David 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2610216a895SLior David 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
2621667e4f9SJohannes Berg 		.policy = wil_rf_sector_policy,
2630216a895SLior David 		.doit = wil_rf_sector_set_selected
2640216a895SLior David 	},
2650216a895SLior David };
2660216a895SLior David 
2672be7d22fSVladimir Kondratiev static struct ieee80211_supported_band wil_band_60ghz = {
2682be7d22fSVladimir Kondratiev 	.channels = wil_60ghz_channels,
2692be7d22fSVladimir Kondratiev 	.n_channels = ARRAY_SIZE(wil_60ghz_channels),
2702be7d22fSVladimir Kondratiev 	.ht_cap = {
2712be7d22fSVladimir Kondratiev 		.ht_supported = true,
2722be7d22fSVladimir Kondratiev 		.cap = 0, /* TODO */
2732be7d22fSVladimir Kondratiev 		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
2742be7d22fSVladimir Kondratiev 		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
2752be7d22fSVladimir Kondratiev 		.mcs = {
2762be7d22fSVladimir Kondratiev 				/* MCS 1..12 - SC PHY */
2772be7d22fSVladimir Kondratiev 			.rx_mask = {0xfe, 0x1f}, /* 1..12 */
2782be7d22fSVladimir Kondratiev 			.tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
2792be7d22fSVladimir Kondratiev 		},
2802be7d22fSVladimir Kondratiev 	},
2812be7d22fSVladimir Kondratiev };
2822be7d22fSVladimir Kondratiev 
2832be7d22fSVladimir Kondratiev static const struct ieee80211_txrx_stypes
2842be7d22fSVladimir Kondratiev wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2852be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_STATION] = {
2862be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2872be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2882be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2892be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2902be7d22fSVladimir Kondratiev 	},
2912be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_AP] = {
2922be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
293849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4) |
294849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
295b9010f10SAhmad Masri 		BIT(IEEE80211_STYPE_DISASSOC >> 4) |
296b9010f10SAhmad Masri 		BIT(IEEE80211_STYPE_AUTH >> 4) |
297b9010f10SAhmad Masri 		BIT(IEEE80211_STYPE_REASSOC_RESP >> 4),
2982be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
299849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
300849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
301849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_DISASSOC >> 4) |
302849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_AUTH >> 4) |
303849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_DEAUTH >> 4) |
304849a564bSDedy Lansky 		BIT(IEEE80211_STYPE_REASSOC_REQ >> 4)
3052be7d22fSVladimir Kondratiev 	},
3062be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_P2P_CLIENT] = {
3072be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3082be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3092be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3102be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3112be7d22fSVladimir Kondratiev 	},
3122be7d22fSVladimir Kondratiev 	[NL80211_IFTYPE_P2P_GO] = {
3132be7d22fSVladimir Kondratiev 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3142be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3152be7d22fSVladimir Kondratiev 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3162be7d22fSVladimir Kondratiev 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3172be7d22fSVladimir Kondratiev 	},
3184332cac1SLior David 	[NL80211_IFTYPE_P2P_DEVICE] = {
3194332cac1SLior David 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3204332cac1SLior David 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3214332cac1SLior David 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3224332cac1SLior David 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3234332cac1SLior David 	},
3242be7d22fSVladimir Kondratiev };
3252be7d22fSVladimir Kondratiev 
3262be7d22fSVladimir Kondratiev static const u32 wil_cipher_suites[] = {
3272be7d22fSVladimir Kondratiev 	WLAN_CIPHER_SUITE_GCMP,
3282be7d22fSVladimir Kondratiev };
3292be7d22fSVladimir Kondratiev 
33058527421SVladimir Kondratiev static const char * const key_usage_str[] = {
33158527421SVladimir Kondratiev 	[WMI_KEY_USE_PAIRWISE]	= "PTK",
33258527421SVladimir Kondratiev 	[WMI_KEY_USE_RX_GROUP]	= "RX_GTK",
33358527421SVladimir Kondratiev 	[WMI_KEY_USE_TX_GROUP]	= "TX_GTK",
33442fe1e51SAhmad Masri 	[WMI_KEY_USE_STORE_PTK]	= "STORE_PTK",
33542fe1e51SAhmad Masri 	[WMI_KEY_USE_APPLY_PTK]	= "APPLY_PTK",
33658527421SVladimir Kondratiev };
33758527421SVladimir Kondratiev 
3382be7d22fSVladimir Kondratiev int wil_iftype_nl2wmi(enum nl80211_iftype type)
3392be7d22fSVladimir Kondratiev {
3402be7d22fSVladimir Kondratiev 	static const struct {
3412be7d22fSVladimir Kondratiev 		enum nl80211_iftype nl;
3422be7d22fSVladimir Kondratiev 		enum wmi_network_type wmi;
3432be7d22fSVladimir Kondratiev 	} __nl2wmi[] = {
3442be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_ADHOC,		WMI_NETTYPE_ADHOC},
3452be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_STATION,	WMI_NETTYPE_INFRA},
3462be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_AP,		WMI_NETTYPE_AP},
3472be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_P2P_CLIENT,	WMI_NETTYPE_P2P},
3482be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_P2P_GO,		WMI_NETTYPE_P2P},
3492be7d22fSVladimir Kondratiev 		{NL80211_IFTYPE_MONITOR,	WMI_NETTYPE_ADHOC}, /* FIXME */
3502be7d22fSVladimir Kondratiev 	};
3512be7d22fSVladimir Kondratiev 	uint i;
3522be7d22fSVladimir Kondratiev 
3532be7d22fSVladimir Kondratiev 	for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
3542be7d22fSVladimir Kondratiev 		if (__nl2wmi[i].nl == type)
3552be7d22fSVladimir Kondratiev 			return __nl2wmi[i].wmi;
3562be7d22fSVladimir Kondratiev 	}
3572be7d22fSVladimir Kondratiev 
3582be7d22fSVladimir Kondratiev 	return -EOPNOTSUPP;
3592be7d22fSVladimir Kondratiev }
3602be7d22fSVladimir Kondratiev 
3619abe3e30SAlexei Avshalom Lazar int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch)
3629abe3e30SAlexei Avshalom Lazar {
3639abe3e30SAlexei Avshalom Lazar 	switch (spec_ch) {
3649abe3e30SAlexei Avshalom Lazar 	case 1:
3659abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_1;
3669abe3e30SAlexei Avshalom Lazar 		break;
3679abe3e30SAlexei Avshalom Lazar 	case 2:
3689abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_2;
3699abe3e30SAlexei Avshalom Lazar 		break;
3709abe3e30SAlexei Avshalom Lazar 	case 3:
3719abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_3;
3729abe3e30SAlexei Avshalom Lazar 		break;
3739abe3e30SAlexei Avshalom Lazar 	case 4:
3749abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_4;
3759abe3e30SAlexei Avshalom Lazar 		break;
3769abe3e30SAlexei Avshalom Lazar 	case 5:
3779abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_5;
3789abe3e30SAlexei Avshalom Lazar 		break;
3799abe3e30SAlexei Avshalom Lazar 	case 6:
3809abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_6;
3819abe3e30SAlexei Avshalom Lazar 		break;
3829abe3e30SAlexei Avshalom Lazar 	case 9:
3839abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_9;
3849abe3e30SAlexei Avshalom Lazar 		break;
3859abe3e30SAlexei Avshalom Lazar 	case 10:
3869abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_10;
3879abe3e30SAlexei Avshalom Lazar 		break;
3889abe3e30SAlexei Avshalom Lazar 	case 11:
3899abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_11;
3909abe3e30SAlexei Avshalom Lazar 		break;
3919abe3e30SAlexei Avshalom Lazar 	case 12:
3929abe3e30SAlexei Avshalom Lazar 		*wmi_ch = WMI_CHANNEL_12;
3939abe3e30SAlexei Avshalom Lazar 		break;
3949abe3e30SAlexei Avshalom Lazar 	default:
3959abe3e30SAlexei Avshalom Lazar 		return -EINVAL;
3969abe3e30SAlexei Avshalom Lazar 	}
3979abe3e30SAlexei Avshalom Lazar 
3989abe3e30SAlexei Avshalom Lazar 	return 0;
3999abe3e30SAlexei Avshalom Lazar }
4009abe3e30SAlexei Avshalom Lazar 
4019abe3e30SAlexei Avshalom Lazar int wil_wmi2spec_ch(u8 wmi_ch, u8 *spec_ch)
4029abe3e30SAlexei Avshalom Lazar {
4039abe3e30SAlexei Avshalom Lazar 	switch (wmi_ch) {
4049abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_1:
4059abe3e30SAlexei Avshalom Lazar 		*spec_ch = 1;
4069abe3e30SAlexei Avshalom Lazar 		break;
4079abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_2:
4089abe3e30SAlexei Avshalom Lazar 		*spec_ch = 2;
4099abe3e30SAlexei Avshalom Lazar 		break;
4109abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_3:
4119abe3e30SAlexei Avshalom Lazar 		*spec_ch = 3;
4129abe3e30SAlexei Avshalom Lazar 		break;
4139abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_4:
4149abe3e30SAlexei Avshalom Lazar 		*spec_ch = 4;
4159abe3e30SAlexei Avshalom Lazar 		break;
4169abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_5:
4179abe3e30SAlexei Avshalom Lazar 		*spec_ch = 5;
4189abe3e30SAlexei Avshalom Lazar 		break;
4199abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_6:
4209abe3e30SAlexei Avshalom Lazar 		*spec_ch = 6;
4219abe3e30SAlexei Avshalom Lazar 		break;
4229abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_9:
4239abe3e30SAlexei Avshalom Lazar 		*spec_ch = 9;
4249abe3e30SAlexei Avshalom Lazar 		break;
4259abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_10:
4269abe3e30SAlexei Avshalom Lazar 		*spec_ch = 10;
4279abe3e30SAlexei Avshalom Lazar 		break;
4289abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_11:
4299abe3e30SAlexei Avshalom Lazar 		*spec_ch = 11;
4309abe3e30SAlexei Avshalom Lazar 		break;
4319abe3e30SAlexei Avshalom Lazar 	case WMI_CHANNEL_12:
4329abe3e30SAlexei Avshalom Lazar 		*spec_ch = 12;
4339abe3e30SAlexei Avshalom Lazar 		break;
4349abe3e30SAlexei Avshalom Lazar 	default:
4359abe3e30SAlexei Avshalom Lazar 		return -EINVAL;
4369abe3e30SAlexei Avshalom Lazar 	}
4379abe3e30SAlexei Avshalom Lazar 
4389abe3e30SAlexei Avshalom Lazar 	return 0;
4399abe3e30SAlexei Avshalom Lazar }
4409abe3e30SAlexei Avshalom Lazar 
441e00243faSLior David int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
442ef28afdbSVladimir Kondratiev 		       struct station_info *sinfo)
4432be7d22fSVladimir Kondratiev {
444e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
4452be7d22fSVladimir Kondratiev 	struct wmi_notify_req_cmd cmd = {
4463df2cd36SVladimir Kondratiev 		.cid = cid,
4472be7d22fSVladimir Kondratiev 		.interval_usec = 0,
4482be7d22fSVladimir Kondratiev 	};
449ef28afdbSVladimir Kondratiev 	struct {
450b874ddecSLior David 		struct wmi_cmd_hdr wmi;
451ef28afdbSVladimir Kondratiev 		struct wmi_notify_req_done_event evt;
452ef28afdbSVladimir Kondratiev 	} __packed reply;
453c8b78b5fSVladimir Kondratiev 	struct wil_net_stats *stats = &wil->sta[cid].stats;
454ef28afdbSVladimir Kondratiev 	int rc;
4559abe3e30SAlexei Avshalom Lazar 	u8 txflag = RATE_INFO_FLAGS_DMG;
4562be7d22fSVladimir Kondratiev 
457807b0860SAlexei Avshalom Lazar 	memset(&reply, 0, sizeof(reply));
458807b0860SAlexei Avshalom Lazar 
459e00243faSLior David 	rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd),
4602a32c20bSAhmad Masri 		      WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply),
4612a32c20bSAhmad Masri 		      WIL_WMI_CALL_GENERAL_TO_MS);
4622be7d22fSVladimir Kondratiev 	if (rc)
4632be7d22fSVladimir Kondratiev 		return rc;
4642be7d22fSVladimir Kondratiev 
465e00243faSLior David 	wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n"
466c8b78b5fSVladimir Kondratiev 		    "  MCS %d TSF 0x%016llx\n"
46730868f5dSDedy Lansky 		    "  BF status 0x%08x RSSI %d SQI %d%%\n"
468c8b78b5fSVladimir Kondratiev 		    "  Tx Tpt %d goodput %d Rx goodput %d\n"
4699abe3e30SAlexei Avshalom Lazar 		    "  Sectors(rx:tx) my %d:%d peer %d:%d\n"
4709abe3e30SAlexei Avshalom Lazar 		    "  Tx mode %d}\n",
471e00243faSLior David 		    cid, vif->mid, le16_to_cpu(reply.evt.bf_mcs),
472c8b78b5fSVladimir Kondratiev 		    le64_to_cpu(reply.evt.tsf), reply.evt.status,
47330868f5dSDedy Lansky 		    reply.evt.rssi,
474b8b33a3aSVladimir Kondratiev 		    reply.evt.sqi,
475c8b78b5fSVladimir Kondratiev 		    le32_to_cpu(reply.evt.tx_tpt),
476c8b78b5fSVladimir Kondratiev 		    le32_to_cpu(reply.evt.tx_goodput),
477c8b78b5fSVladimir Kondratiev 		    le32_to_cpu(reply.evt.rx_goodput),
478c8b78b5fSVladimir Kondratiev 		    le16_to_cpu(reply.evt.my_rx_sector),
479c8b78b5fSVladimir Kondratiev 		    le16_to_cpu(reply.evt.my_tx_sector),
480c8b78b5fSVladimir Kondratiev 		    le16_to_cpu(reply.evt.other_rx_sector),
4819abe3e30SAlexei Avshalom Lazar 		    le16_to_cpu(reply.evt.other_tx_sector),
4829abe3e30SAlexei Avshalom Lazar 		    reply.evt.tx_mode);
483c8b78b5fSVladimir Kondratiev 
4842be7d22fSVladimir Kondratiev 	sinfo->generation = wil->sinfo_gen;
4852be7d22fSVladimir Kondratiev 
48622d0d2faSOmer Efrat 	sinfo->filled = BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
48722d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
48822d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
48922d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
49022d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_RX_BITRATE) |
49122d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_BITRATE) |
49222d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
49322d0d2faSOmer Efrat 			BIT_ULL(NL80211_STA_INFO_TX_FAILED);
494c8b78b5fSVladimir Kondratiev 
4959abe3e30SAlexei Avshalom Lazar 	if (wil->use_enhanced_dma_hw && reply.evt.tx_mode != WMI_TX_MODE_DMG)
4969abe3e30SAlexei Avshalom Lazar 		txflag = RATE_INFO_FLAGS_EDMG;
4979abe3e30SAlexei Avshalom Lazar 
4989abe3e30SAlexei Avshalom Lazar 	sinfo->txrate.flags = txflag;
499ef28afdbSVladimir Kondratiev 	sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
500c8b78b5fSVladimir Kondratiev 	sinfo->rxrate.mcs = stats->last_mcs_rx;
5019abe3e30SAlexei Avshalom Lazar 	sinfo->txrate.n_bonded_ch =
5029abe3e30SAlexei Avshalom Lazar 				  wil_tx_cb_mode_to_n_bonded(reply.evt.tx_mode);
5039abe3e30SAlexei Avshalom Lazar 	sinfo->rxrate.n_bonded_ch =
5049abe3e30SAlexei Avshalom Lazar 			     wil_rx_cb_mode_to_n_bonded(stats->last_cb_mode_rx);
505c8b78b5fSVladimir Kondratiev 	sinfo->rx_bytes = stats->rx_bytes;
506c8b78b5fSVladimir Kondratiev 	sinfo->rx_packets = stats->rx_packets;
507c8b78b5fSVladimir Kondratiev 	sinfo->rx_dropped_misc = stats->rx_dropped;
508c8b78b5fSVladimir Kondratiev 	sinfo->tx_bytes = stats->tx_bytes;
509c8b78b5fSVladimir Kondratiev 	sinfo->tx_packets = stats->tx_packets;
510c8b78b5fSVladimir Kondratiev 	sinfo->tx_failed = stats->tx_errors;
5112be7d22fSVladimir Kondratiev 
5125bd60982SLior David 	if (test_bit(wil_vif_fwconnected, vif->status)) {
51322d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
51430868f5dSDedy Lansky 		if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING,
51530868f5dSDedy Lansky 			     wil->fw_capabilities))
51630868f5dSDedy Lansky 			sinfo->signal = reply.evt.rssi;
51730868f5dSDedy Lansky 		else
518b8b33a3aSVladimir Kondratiev 			sinfo->signal = reply.evt.sqi;
5192be7d22fSVladimir Kondratiev 	}
5202be7d22fSVladimir Kondratiev 
521ef28afdbSVladimir Kondratiev 	return rc;
522ef28afdbSVladimir Kondratiev }
523ef28afdbSVladimir Kondratiev 
524ef28afdbSVladimir Kondratiev static int wil_cfg80211_get_station(struct wiphy *wiphy,
525ef28afdbSVladimir Kondratiev 				    struct net_device *ndev,
5263b3a0162SJohannes Berg 				    const u8 *mac, struct station_info *sinfo)
527ef28afdbSVladimir Kondratiev {
528e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
529ef28afdbSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
530ef28afdbSVladimir Kondratiev 	int rc;
531ef28afdbSVladimir Kondratiev 
532e00243faSLior David 	int cid = wil_find_cid(wil, vif->mid, mac);
533ef28afdbSVladimir Kondratiev 
534e00243faSLior David 	wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid,
535e00243faSLior David 		     vif->mid);
536c478ac9dSAlexei Avshalom Lazar 	if (!wil_cid_valid(wil, cid))
537c478ac9dSAlexei Avshalom Lazar 		return -ENOENT;
538ef28afdbSVladimir Kondratiev 
539e00243faSLior David 	rc = wil_cid_fill_sinfo(vif, cid, sinfo);
540ef28afdbSVladimir Kondratiev 
541ef28afdbSVladimir Kondratiev 	return rc;
542ef28afdbSVladimir Kondratiev }
543ef28afdbSVladimir Kondratiev 
544ef28afdbSVladimir Kondratiev /*
545e00243faSLior David  * Find @idx-th active STA for specific MID for station dump.
546ef28afdbSVladimir Kondratiev  */
54742fe1e51SAhmad Masri int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
548ef28afdbSVladimir Kondratiev {
549ef28afdbSVladimir Kondratiev 	int i;
550ef28afdbSVladimir Kondratiev 
551ddf7afddSAhmad Masri 	for (i = 0; i < wil->max_assoc_sta; i++) {
552ef28afdbSVladimir Kondratiev 		if (wil->sta[i].status == wil_sta_unused)
553ef28afdbSVladimir Kondratiev 			continue;
554e00243faSLior David 		if (wil->sta[i].mid != mid)
555e00243faSLior David 			continue;
556ef28afdbSVladimir Kondratiev 		if (idx == 0)
557ef28afdbSVladimir Kondratiev 			return i;
558ef28afdbSVladimir Kondratiev 		idx--;
559ef28afdbSVladimir Kondratiev 	}
560ef28afdbSVladimir Kondratiev 
561ef28afdbSVladimir Kondratiev 	return -ENOENT;
562ef28afdbSVladimir Kondratiev }
563ef28afdbSVladimir Kondratiev 
564ef28afdbSVladimir Kondratiev static int wil_cfg80211_dump_station(struct wiphy *wiphy,
565ef28afdbSVladimir Kondratiev 				     struct net_device *dev, int idx,
566ef28afdbSVladimir Kondratiev 				     u8 *mac, struct station_info *sinfo)
567ef28afdbSVladimir Kondratiev {
568e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
569ef28afdbSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
570ef28afdbSVladimir Kondratiev 	int rc;
571e00243faSLior David 	int cid = wil_find_cid_by_idx(wil, vif->mid, idx);
572ef28afdbSVladimir Kondratiev 
573c478ac9dSAlexei Avshalom Lazar 	if (!wil_cid_valid(wil, cid))
574ef28afdbSVladimir Kondratiev 		return -ENOENT;
575ef28afdbSVladimir Kondratiev 
576a82553bbSVladimir Kondratiev 	ether_addr_copy(mac, wil->sta[cid].addr);
577e00243faSLior David 	wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid,
578e00243faSLior David 		     vif->mid);
579ef28afdbSVladimir Kondratiev 
580e00243faSLior David 	rc = wil_cid_fill_sinfo(vif, cid, sinfo);
581ef28afdbSVladimir Kondratiev 
582ef28afdbSVladimir Kondratiev 	return rc;
5832be7d22fSVladimir Kondratiev }
5842be7d22fSVladimir Kondratiev 
58569fecf59SLior David static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
58669fecf59SLior David 					 struct wireless_dev *wdev)
58769fecf59SLior David {
58869fecf59SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
58969fecf59SLior David 
59069fecf59SLior David 	wil_dbg_misc(wil, "start_p2p_device: entered\n");
591e00243faSLior David 	wil->p2p_dev_started = 1;
59269fecf59SLior David 	return 0;
59369fecf59SLior David }
59469fecf59SLior David 
59569fecf59SLior David static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
59669fecf59SLior David 					 struct wireless_dev *wdev)
59769fecf59SLior David {
59869fecf59SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
59969fecf59SLior David 
600e00243faSLior David 	if (!wil->p2p_dev_started)
60169fecf59SLior David 		return;
60269fecf59SLior David 
60369fecf59SLior David 	wil_dbg_misc(wil, "stop_p2p_device: entered\n");
60469fecf59SLior David 	mutex_lock(&wil->mutex);
605404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
60669fecf59SLior David 	wil_p2p_stop_radio_operations(wil);
607e00243faSLior David 	wil->p2p_dev_started = 0;
608404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
60969fecf59SLior David 	mutex_unlock(&wil->mutex);
61069fecf59SLior David }
61169fecf59SLior David 
6124aebd3bdSLior David static int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil,
6134aebd3bdSLior David 					   enum nl80211_iftype new_type)
6144aebd3bdSLior David {
6154aebd3bdSLior David 	int i;
6164aebd3bdSLior David 	struct wireless_dev *wdev;
6174aebd3bdSLior David 	struct iface_combination_params params = {
6184aebd3bdSLior David 		.num_different_channels = 1,
6194aebd3bdSLior David 	};
6204aebd3bdSLior David 
621e4a29bddSAhmad Masri 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
6224aebd3bdSLior David 		if (wil->vifs[i]) {
6234aebd3bdSLior David 			wdev = vif_to_wdev(wil->vifs[i]);
6244aebd3bdSLior David 			params.iftype_num[wdev->iftype]++;
6254aebd3bdSLior David 		}
6264aebd3bdSLior David 	}
6274aebd3bdSLior David 	params.iftype_num[new_type]++;
6284aebd3bdSLior David 	return cfg80211_check_combinations(wil->wiphy, &params);
6294aebd3bdSLior David }
6304aebd3bdSLior David 
6314aebd3bdSLior David static int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil,
6324aebd3bdSLior David 					      struct wil6210_vif *vif,
6334aebd3bdSLior David 					      enum nl80211_iftype new_type)
6344aebd3bdSLior David {
6354aebd3bdSLior David 	int i, ret = 0;
6364aebd3bdSLior David 	struct wireless_dev *wdev;
6374aebd3bdSLior David 	struct iface_combination_params params = {
6384aebd3bdSLior David 		.num_different_channels = 1,
6394aebd3bdSLior David 	};
6404aebd3bdSLior David 	bool check_combos = false;
6414aebd3bdSLior David 
642e4a29bddSAhmad Masri 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
6434aebd3bdSLior David 		struct wil6210_vif *vif_pos = wil->vifs[i];
6444aebd3bdSLior David 
6454aebd3bdSLior David 		if (vif_pos && vif != vif_pos) {
6464aebd3bdSLior David 			wdev = vif_to_wdev(vif_pos);
6474aebd3bdSLior David 			params.iftype_num[wdev->iftype]++;
6484aebd3bdSLior David 			check_combos = true;
6494aebd3bdSLior David 		}
6504aebd3bdSLior David 	}
6514aebd3bdSLior David 
6524aebd3bdSLior David 	if (check_combos) {
6534aebd3bdSLior David 		params.iftype_num[new_type]++;
6544aebd3bdSLior David 		ret = cfg80211_check_combinations(wil->wiphy, &params);
6554aebd3bdSLior David 	}
6564aebd3bdSLior David 	return ret;
6574aebd3bdSLior David }
6584aebd3bdSLior David 
6594332cac1SLior David static struct wireless_dev *
6604332cac1SLior David wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
6614332cac1SLior David 		       unsigned char name_assign_type,
6624332cac1SLior David 		       enum nl80211_iftype type,
663818a986eSJohannes Berg 		       struct vif_params *params)
6644332cac1SLior David {
6654332cac1SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
6664aebd3bdSLior David 	struct net_device *ndev_main = wil->main_ndev, *ndev;
6679f38f286SLior David 	struct wil6210_vif *vif;
6684aebd3bdSLior David 	struct wireless_dev *p2p_wdev, *wdev;
6694aebd3bdSLior David 	int rc;
6704332cac1SLior David 
6714aebd3bdSLior David 	wil_dbg_misc(wil, "add_iface, type %d\n", type);
6724332cac1SLior David 
6734aebd3bdSLior David 	/* P2P device is not a real virtual interface, it is a management-only
6744aebd3bdSLior David 	 * interface that shares the main interface.
6754aebd3bdSLior David 	 * Skip concurrency checks here.
6764aebd3bdSLior David 	 */
6774aebd3bdSLior David 	if (type == NL80211_IFTYPE_P2P_DEVICE) {
6784332cac1SLior David 		if (wil->p2p_wdev) {
679af3db60aSLazar Alexei 			wil_err(wil, "P2P_DEVICE interface already created\n");
6804332cac1SLior David 			return ERR_PTR(-EINVAL);
6814332cac1SLior David 		}
6824332cac1SLior David 
6835bd60982SLior David 		p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
6845bd60982SLior David 		if (!p2p_wdev)
6854332cac1SLior David 			return ERR_PTR(-ENOMEM);
6864332cac1SLior David 
6874332cac1SLior David 		p2p_wdev->iftype = type;
6884332cac1SLior David 		p2p_wdev->wiphy = wiphy;
6894332cac1SLior David 		/* use our primary ethernet address */
6904aebd3bdSLior David 		ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr);
6914332cac1SLior David 
6924332cac1SLior David 		wil->p2p_wdev = p2p_wdev;
6934332cac1SLior David 
6944332cac1SLior David 		return p2p_wdev;
6954332cac1SLior David 	}
6964332cac1SLior David 
6974aebd3bdSLior David 	if (!wil->wiphy->n_iface_combinations) {
6984aebd3bdSLior David 		wil_err(wil, "virtual interfaces not supported\n");
6994aebd3bdSLior David 		return ERR_PTR(-EINVAL);
7004aebd3bdSLior David 	}
7014aebd3bdSLior David 
7024aebd3bdSLior David 	rc = wil_cfg80211_validate_add_iface(wil, type);
7034aebd3bdSLior David 	if (rc) {
7044aebd3bdSLior David 		wil_err(wil, "iface validation failed, err=%d\n", rc);
7054aebd3bdSLior David 		return ERR_PTR(rc);
7064aebd3bdSLior David 	}
7074aebd3bdSLior David 
7084aebd3bdSLior David 	vif = wil_vif_alloc(wil, name, name_assign_type, type);
7094aebd3bdSLior David 	if (IS_ERR(vif))
7104aebd3bdSLior David 		return ERR_CAST(vif);
7114aebd3bdSLior David 
7124aebd3bdSLior David 	ndev = vif_to_ndev(vif);
7134aebd3bdSLior David 	ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr);
7144aebd3bdSLior David 	if (is_valid_ether_addr(params->macaddr)) {
7154aebd3bdSLior David 		ether_addr_copy(ndev->dev_addr, params->macaddr);
7164aebd3bdSLior David 	} else {
7174aebd3bdSLior David 		ether_addr_copy(ndev->dev_addr, ndev_main->perm_addr);
7184aebd3bdSLior David 		ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << vif->mid)) |
7194aebd3bdSLior David 			0x2; /* locally administered */
7204aebd3bdSLior David 	}
7214aebd3bdSLior David 	wdev = vif_to_wdev(vif);
7224aebd3bdSLior David 	ether_addr_copy(wdev->address, ndev->dev_addr);
7234aebd3bdSLior David 
7244aebd3bdSLior David 	rc = wil_vif_add(wil, vif);
7254aebd3bdSLior David 	if (rc)
7264aebd3bdSLior David 		goto out;
7274aebd3bdSLior David 
7284aebd3bdSLior David 	wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n",
7294aebd3bdSLior David 		 vif->mid, type, wdev->address);
7304aebd3bdSLior David 	return wdev;
7314aebd3bdSLior David out:
7324aebd3bdSLior David 	wil_vif_free(vif);
7334aebd3bdSLior David 	return ERR_PTR(rc);
7344aebd3bdSLior David }
7354aebd3bdSLior David 
7363ada9314SLior David int wil_vif_prepare_stop(struct wil6210_vif *vif)
7374aebd3bdSLior David {
7383ada9314SLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
7394aebd3bdSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
7404aebd3bdSLior David 	struct net_device *ndev;
7414aebd3bdSLior David 	int rc;
7424aebd3bdSLior David 
7434aebd3bdSLior David 	if (wdev->iftype != NL80211_IFTYPE_AP)
7444aebd3bdSLior David 		return 0;
7454aebd3bdSLior David 
7464aebd3bdSLior David 	ndev = vif_to_ndev(vif);
7474aebd3bdSLior David 	if (netif_carrier_ok(ndev)) {
7484aebd3bdSLior David 		rc = wmi_pcp_stop(vif);
7494aebd3bdSLior David 		if (rc) {
7504aebd3bdSLior David 			wil_info(wil, "failed to stop AP, status %d\n",
7514aebd3bdSLior David 				 rc);
7524aebd3bdSLior David 			/* continue */
7534aebd3bdSLior David 		}
7543ada9314SLior David 		wil_bcast_fini(vif);
7554aebd3bdSLior David 		netif_carrier_off(ndev);
7564aebd3bdSLior David 	}
7574aebd3bdSLior David 
7584aebd3bdSLior David 	return 0;
7594aebd3bdSLior David }
7604aebd3bdSLior David 
7614332cac1SLior David static int wil_cfg80211_del_iface(struct wiphy *wiphy,
7624332cac1SLior David 				  struct wireless_dev *wdev)
7634332cac1SLior David {
7644332cac1SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
7654aebd3bdSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
7664aebd3bdSLior David 	int rc;
7674332cac1SLior David 
768af3db60aSLazar Alexei 	wil_dbg_misc(wil, "del_iface\n");
7694332cac1SLior David 
7704aebd3bdSLior David 	if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
7714332cac1SLior David 		if (wdev != wil->p2p_wdev) {
7724aebd3bdSLior David 			wil_err(wil, "delete of incorrect interface 0x%p\n",
7734aebd3bdSLior David 				wdev);
7744332cac1SLior David 			return -EINVAL;
7754332cac1SLior David 		}
7764332cac1SLior David 
77769fecf59SLior David 		wil_cfg80211_stop_p2p_device(wiphy, wdev);
7784332cac1SLior David 		wil_p2p_wdev_free(wil);
7794332cac1SLior David 		return 0;
7804332cac1SLior David 	}
7814332cac1SLior David 
7824aebd3bdSLior David 	if (vif->mid == 0) {
7834aebd3bdSLior David 		wil_err(wil, "cannot remove the main interface\n");
7844aebd3bdSLior David 		return -EINVAL;
7854aebd3bdSLior David 	}
7864aebd3bdSLior David 
7873ada9314SLior David 	rc = wil_vif_prepare_stop(vif);
7884aebd3bdSLior David 	if (rc)
7894aebd3bdSLior David 		goto out;
7904aebd3bdSLior David 
7914aebd3bdSLior David 	wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n",
7924aebd3bdSLior David 		 vif->mid, wdev->iftype, wdev->address);
7934aebd3bdSLior David 
7944aebd3bdSLior David 	wil_vif_remove(wil, vif->mid);
7954aebd3bdSLior David out:
7964aebd3bdSLior David 	return rc;
7974aebd3bdSLior David }
7984aebd3bdSLior David 
799b913e330SAlexei Avshalom Lazar static bool wil_is_safe_switch(enum nl80211_iftype from,
800b913e330SAlexei Avshalom Lazar 			       enum nl80211_iftype to)
801b913e330SAlexei Avshalom Lazar {
802b913e330SAlexei Avshalom Lazar 	if (from == NL80211_IFTYPE_STATION &&
803b913e330SAlexei Avshalom Lazar 	    to == NL80211_IFTYPE_P2P_CLIENT)
804b913e330SAlexei Avshalom Lazar 		return true;
805b913e330SAlexei Avshalom Lazar 
806b913e330SAlexei Avshalom Lazar 	return false;
807b913e330SAlexei Avshalom Lazar }
808b913e330SAlexei Avshalom Lazar 
8092be7d22fSVladimir Kondratiev static int wil_cfg80211_change_iface(struct wiphy *wiphy,
8102be7d22fSVladimir Kondratiev 				     struct net_device *ndev,
811818a986eSJohannes Berg 				     enum nl80211_iftype type,
8122be7d22fSVladimir Kondratiev 				     struct vif_params *params)
8132be7d22fSVladimir Kondratiev {
8142be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
815e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
816e00243faSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
817e6d68341SDedy Lansky 	int rc;
8183ada9314SLior David 	bool fw_reset = false;
819e6d68341SDedy Lansky 
820af3db60aSLazar Alexei 	wil_dbg_misc(wil, "change_iface: type=%d\n", type);
821e6d68341SDedy Lansky 
8224aebd3bdSLior David 	if (wiphy->n_iface_combinations) {
8234aebd3bdSLior David 		rc = wil_cfg80211_validate_change_iface(wil, vif, type);
8244aebd3bdSLior David 		if (rc) {
8254aebd3bdSLior David 			wil_err(wil, "iface validation failed, err=%d\n", rc);
8264aebd3bdSLior David 			return rc;
8274aebd3bdSLior David 		}
8284aebd3bdSLior David 	}
8294aebd3bdSLior David 
8304aebd3bdSLior David 	/* do not reset FW when there are active VIFs,
8314aebd3bdSLior David 	 * because it can cause significant disruption
8324aebd3bdSLior David 	 */
8333ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, true, false) &&
834b913e330SAlexei Avshalom Lazar 	    netif_running(ndev) && !wil_is_recovery_blocked(wil) &&
835b913e330SAlexei Avshalom Lazar 	    !wil_is_safe_switch(wdev->iftype, type)) {
836e6d68341SDedy Lansky 		wil_dbg_misc(wil, "interface is up. resetting...\n");
837e6d68341SDedy Lansky 		mutex_lock(&wil->mutex);
838e6d68341SDedy Lansky 		__wil_down(wil);
839e6d68341SDedy Lansky 		rc = __wil_up(wil);
840e6d68341SDedy Lansky 		mutex_unlock(&wil->mutex);
841e6d68341SDedy Lansky 
842e6d68341SDedy Lansky 		if (rc)
843e6d68341SDedy Lansky 			return rc;
8443ada9314SLior David 		fw_reset = true;
845e6d68341SDedy Lansky 	}
8462be7d22fSVladimir Kondratiev 
8472be7d22fSVladimir Kondratiev 	switch (type) {
8482be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
8492be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_AP:
8502be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
8512be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_GO:
8522be7d22fSVladimir Kondratiev 		break;
8532be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_MONITOR:
854818a986eSJohannes Berg 		if (params->flags)
855818a986eSJohannes Berg 			wil->monitor_flags = params->flags;
8562be7d22fSVladimir Kondratiev 		break;
8572be7d22fSVladimir Kondratiev 	default:
8582be7d22fSVladimir Kondratiev 		return -EOPNOTSUPP;
8592be7d22fSVladimir Kondratiev 	}
8602be7d22fSVladimir Kondratiev 
8613ada9314SLior David 	if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) {
8623ada9314SLior David 		if (!fw_reset)
8633ada9314SLior David 			wil_vif_prepare_stop(vif);
8644aebd3bdSLior David 		rc = wmi_port_delete(wil, vif->mid);
8654aebd3bdSLior David 		if (rc)
8664aebd3bdSLior David 			return rc;
8674aebd3bdSLior David 		rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type);
8684aebd3bdSLior David 		if (rc)
8694aebd3bdSLior David 			return rc;
8704aebd3bdSLior David 	}
8712be7d22fSVladimir Kondratiev 
8724aebd3bdSLior David 	wdev->iftype = type;
8732be7d22fSVladimir Kondratiev 	return 0;
8742be7d22fSVladimir Kondratiev }
8752be7d22fSVladimir Kondratiev 
8762be7d22fSVladimir Kondratiev static int wil_cfg80211_scan(struct wiphy *wiphy,
8772be7d22fSVladimir Kondratiev 			     struct cfg80211_scan_request *request)
8782be7d22fSVladimir Kondratiev {
8792be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
8804332cac1SLior David 	struct wireless_dev *wdev = request->wdev;
881e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
8822be7d22fSVladimir Kondratiev 	struct {
8832be7d22fSVladimir Kondratiev 		struct wmi_start_scan_cmd cmd;
8842be7d22fSVladimir Kondratiev 		u16 chnl[4];
8852be7d22fSVladimir Kondratiev 	} __packed cmd;
8862be7d22fSVladimir Kondratiev 	uint i, n;
887ed6f9dc6SVladimir Kondratiev 	int rc;
8882be7d22fSVladimir Kondratiev 
889af3db60aSLazar Alexei 	wil_dbg_misc(wil, "scan: wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
890e6d68341SDedy Lansky 
891af2cd85eSAhmad Masri 	/* scan is supported on client interfaces and on AP interface */
8922be7d22fSVladimir Kondratiev 	switch (wdev->iftype) {
8932be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
8942be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
8954332cac1SLior David 	case NL80211_IFTYPE_P2P_DEVICE:
896af2cd85eSAhmad Masri 	case NL80211_IFTYPE_AP:
8972be7d22fSVladimir Kondratiev 		break;
8982be7d22fSVladimir Kondratiev 	default:
8992be7d22fSVladimir Kondratiev 		return -EOPNOTSUPP;
9002be7d22fSVladimir Kondratiev 	}
9012be7d22fSVladimir Kondratiev 
9022be7d22fSVladimir Kondratiev 	/* FW don't support scan after connection attempt */
9039419b6a2SVladimir Kondratiev 	if (test_bit(wil_status_dontscan, wil->status)) {
904e83eb2fcSVladimir Kondratiev 		wil_err(wil, "Can't scan now\n");
9052be7d22fSVladimir Kondratiev 		return -EBUSY;
9062be7d22fSVladimir Kondratiev 	}
9072be7d22fSVladimir Kondratiev 
908bb6743f7SLior David 	mutex_lock(&wil->mutex);
909bb6743f7SLior David 
910404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
911e00243faSLior David 	if (vif->scan_request || vif->p2p.discovery_started) {
912bb6743f7SLior David 		wil_err(wil, "Already scanning\n");
913404bbb3cSLior David 		mutex_unlock(&wil->vif_mutex);
914bb6743f7SLior David 		rc = -EAGAIN;
915bb6743f7SLior David 		goto out;
916bb6743f7SLior David 	}
917404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
918bb6743f7SLior David 
9194a0e45a7SLior David 	if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
920e00243faSLior David 		if (!wil->p2p_dev_started) {
921eb57a5b3SLior David 			wil_err(wil, "P2P search requested on stopped P2P device\n");
922bb6743f7SLior David 			rc = -EIO;
923bb6743f7SLior David 			goto out;
924eb57a5b3SLior David 		}
9254a0e45a7SLior David 		/* social scan on P2P_DEVICE is handled as p2p search */
9264a0e45a7SLior David 		if (wil_p2p_is_social_scan(request)) {
927e00243faSLior David 			vif->scan_request = request;
928e00243faSLior David 			if (vif->mid == 0)
9294332cac1SLior David 				wil->radio_wdev = wdev;
930e00243faSLior David 			rc = wil_p2p_search(vif, request);
9314332cac1SLior David 			if (rc) {
932e00243faSLior David 				if (vif->mid == 0)
933e00243faSLior David 					wil->radio_wdev =
934e00243faSLior David 						wil->main_ndev->ieee80211_ptr;
935e00243faSLior David 				vif->scan_request = NULL;
9364332cac1SLior David 			}
937bb6743f7SLior David 			goto out;
938e6d68341SDedy Lansky 		}
9394a0e45a7SLior David 	}
940e6d68341SDedy Lansky 
941e00243faSLior David 	(void)wil_p2p_stop_discovery(vif);
942e6d68341SDedy Lansky 
9432a91d7d0SVladimir Kondratiev 	wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
9448e52fe30SHamad Kadmany 	wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
9458e52fe30SHamad Kadmany 
9468e52fe30SHamad Kadmany 	for (i = 0; i < request->n_ssids; i++) {
9478e52fe30SHamad Kadmany 		wil_dbg_misc(wil, "SSID[%d]", i);
9485eb443e9SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
9498e52fe30SHamad Kadmany 				  request->ssids[i].ssid,
9505eb443e9SDedy Lansky 				  request->ssids[i].ssid_len, true);
9518e52fe30SHamad Kadmany 	}
9528e52fe30SHamad Kadmany 
9538e52fe30SHamad Kadmany 	if (request->n_ssids)
954e00243faSLior David 		rc = wmi_set_ssid(vif, request->ssids[0].ssid_len,
9558e52fe30SHamad Kadmany 				  request->ssids[0].ssid);
9568e52fe30SHamad Kadmany 	else
957e00243faSLior David 		rc = wmi_set_ssid(vif, 0, NULL);
9588e52fe30SHamad Kadmany 
9598e52fe30SHamad Kadmany 	if (rc) {
9608e52fe30SHamad Kadmany 		wil_err(wil, "set SSID for scan request failed: %d\n", rc);
961bb6743f7SLior David 		goto out;
9628e52fe30SHamad Kadmany 	}
9638e52fe30SHamad Kadmany 
964e00243faSLior David 	vif->scan_request = request;
965e00243faSLior David 	mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO);
9662be7d22fSVladimir Kondratiev 
9672be7d22fSVladimir Kondratiev 	memset(&cmd, 0, sizeof(cmd));
96874997a53SLior David 	cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
9692be7d22fSVladimir Kondratiev 	cmd.cmd.num_channels = 0;
9702be7d22fSVladimir Kondratiev 	n = min(request->n_channels, 4U);
9712be7d22fSVladimir Kondratiev 	for (i = 0; i < n; i++) {
9722be7d22fSVladimir Kondratiev 		int ch = request->channels[i]->hw_value;
9738fe59627SVladimir Kondratiev 
9742be7d22fSVladimir Kondratiev 		if (ch == 0) {
9752be7d22fSVladimir Kondratiev 			wil_err(wil,
9762be7d22fSVladimir Kondratiev 				"Scan requested for unknown frequency %dMhz\n",
9772be7d22fSVladimir Kondratiev 				request->channels[i]->center_freq);
9782be7d22fSVladimir Kondratiev 			continue;
9792be7d22fSVladimir Kondratiev 		}
9802be7d22fSVladimir Kondratiev 		/* 0-based channel indexes */
9812be7d22fSVladimir Kondratiev 		cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
9827743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "Scan for ch %d  : %d MHz\n", ch,
9832be7d22fSVladimir Kondratiev 			     request->channels[i]->center_freq);
9842be7d22fSVladimir Kondratiev 	}
9852be7d22fSVladimir Kondratiev 
98677c91295SVladimir Kondratiev 	if (request->ie_len)
9875eb443e9SDedy Lansky 		wil_hex_dump_misc("Scan IE ", DUMP_PREFIX_OFFSET, 16, 1,
9885eb443e9SDedy Lansky 				  request->ie, request->ie_len, true);
98977c91295SVladimir Kondratiev 	else
99077c91295SVladimir Kondratiev 		wil_dbg_misc(wil, "Scan has no IE's\n");
99177c91295SVladimir Kondratiev 
992e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
993e00243faSLior David 			request->ie_len, request->ie);
9945421bf0cSVladimir Kondratiev 	if (rc)
995bb6743f7SLior David 		goto out_restore;
99677c91295SVladimir Kondratiev 
99774997a53SLior David 	if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) {
99874997a53SLior David 		cmd.cmd.discovery_mode = 1;
99974997a53SLior David 		wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
100074997a53SLior David 	}
100174997a53SLior David 
1002e00243faSLior David 	if (vif->mid == 0)
10034332cac1SLior David 		wil->radio_wdev = wdev;
1004e00243faSLior David 	rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid,
1005e00243faSLior David 		      &cmd, sizeof(cmd.cmd) +
10062be7d22fSVladimir Kondratiev 		      cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
1007ed6f9dc6SVladimir Kondratiev 
1008bb6743f7SLior David out_restore:
1009a2142086SVladimir Kondratiev 	if (rc) {
1010e00243faSLior David 		del_timer_sync(&vif->scan_timer);
1011e00243faSLior David 		if (vif->mid == 0)
1012e00243faSLior David 			wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
1013e00243faSLior David 		vif->scan_request = NULL;
1014a2142086SVladimir Kondratiev 	}
1015bb6743f7SLior David out:
1016bb6743f7SLior David 	mutex_unlock(&wil->mutex);
1017ed6f9dc6SVladimir Kondratiev 	return rc;
10182be7d22fSVladimir Kondratiev }
10192be7d22fSVladimir Kondratiev 
1020035859a5SMaya Erez static void wil_cfg80211_abort_scan(struct wiphy *wiphy,
1021035859a5SMaya Erez 				    struct wireless_dev *wdev)
1022035859a5SMaya Erez {
1023035859a5SMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1024e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
1025035859a5SMaya Erez 
1026035859a5SMaya Erez 	wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
1027035859a5SMaya Erez 
1028035859a5SMaya Erez 	mutex_lock(&wil->mutex);
1029404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
1030035859a5SMaya Erez 
1031e00243faSLior David 	if (!vif->scan_request)
1032035859a5SMaya Erez 		goto out;
1033035859a5SMaya Erez 
1034e00243faSLior David 	if (wdev != vif->scan_request->wdev) {
1035035859a5SMaya Erez 		wil_dbg_misc(wil, "abort scan was called on the wrong iface\n");
1036035859a5SMaya Erez 		goto out;
1037035859a5SMaya Erez 	}
1038035859a5SMaya Erez 
1039e00243faSLior David 	if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev)
1040035859a5SMaya Erez 		wil_p2p_stop_radio_operations(wil);
1041035859a5SMaya Erez 	else
1042e00243faSLior David 		wil_abort_scan(vif, true);
1043035859a5SMaya Erez 
1044035859a5SMaya Erez out:
1045404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
1046035859a5SMaya Erez 	mutex_unlock(&wil->mutex);
1047035859a5SMaya Erez }
1048035859a5SMaya Erez 
1049feeac225SVladimir Kondratiev static void wil_print_crypto(struct wil6210_priv *wil,
1050feeac225SVladimir Kondratiev 			     struct cfg80211_crypto_settings *c)
1051feeac225SVladimir Kondratiev {
1052feeac225SVladimir Kondratiev 	int i, n;
1053feeac225SVladimir Kondratiev 
1054feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
1055feeac225SVladimir Kondratiev 		     c->wpa_versions, c->cipher_group);
1056feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise);
1057feeac225SVladimir Kondratiev 	n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise));
1058feeac225SVladimir Kondratiev 	for (i = 0; i < n; i++)
1059feeac225SVladimir Kondratiev 		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
1060feeac225SVladimir Kondratiev 			     c->ciphers_pairwise[i]);
1061feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "}\n");
1062feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites);
1063feeac225SVladimir Kondratiev 	n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites));
1064feeac225SVladimir Kondratiev 	for (i = 0; i < n; i++)
1065feeac225SVladimir Kondratiev 		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
1066feeac225SVladimir Kondratiev 			     c->akm_suites[i]);
1067feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "}\n");
1068feeac225SVladimir Kondratiev 	wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
1069feeac225SVladimir Kondratiev 		     c->control_port, be16_to_cpu(c->control_port_ethertype),
1070feeac225SVladimir Kondratiev 		     c->control_port_no_encrypt);
1071feeac225SVladimir Kondratiev }
1072feeac225SVladimir Kondratiev 
1073b9010f10SAhmad Masri static const char *
1074b9010f10SAhmad Masri wil_get_auth_type_name(enum nl80211_auth_type auth_type)
1075b9010f10SAhmad Masri {
1076b9010f10SAhmad Masri 	switch (auth_type) {
1077b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_OPEN_SYSTEM:
1078b9010f10SAhmad Masri 		return "OPEN_SYSTEM";
1079b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_SHARED_KEY:
1080b9010f10SAhmad Masri 		return "SHARED_KEY";
1081b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_FT:
1082b9010f10SAhmad Masri 		return "FT";
1083b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_NETWORK_EAP:
1084b9010f10SAhmad Masri 		return "NETWORK_EAP";
1085b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_SAE:
1086b9010f10SAhmad Masri 		return "SAE";
1087b9010f10SAhmad Masri 	case NL80211_AUTHTYPE_AUTOMATIC:
1088b9010f10SAhmad Masri 		return "AUTOMATIC";
1089b9010f10SAhmad Masri 	default:
1090b9010f10SAhmad Masri 		return "unknown";
1091b9010f10SAhmad Masri 	}
1092b9010f10SAhmad Masri }
10938ca26163SVladimir Kondratiev static void wil_print_connect_params(struct wil6210_priv *wil,
10948ca26163SVladimir Kondratiev 				     struct cfg80211_connect_params *sme)
10958ca26163SVladimir Kondratiev {
10968ca26163SVladimir Kondratiev 	wil_info(wil, "Connecting to:\n");
10978ca26163SVladimir Kondratiev 	if (sme->channel) {
10988ca26163SVladimir Kondratiev 		wil_info(wil, "  Channel: %d freq %d\n",
10998ca26163SVladimir Kondratiev 			 sme->channel->hw_value, sme->channel->center_freq);
11008ca26163SVladimir Kondratiev 	}
11018ca26163SVladimir Kondratiev 	if (sme->bssid)
11028ca26163SVladimir Kondratiev 		wil_info(wil, "  BSSID: %pM\n", sme->bssid);
11038ca26163SVladimir Kondratiev 	if (sme->ssid)
11048ca26163SVladimir Kondratiev 		print_hex_dump(KERN_INFO, "  SSID: ", DUMP_PREFIX_OFFSET,
11058ca26163SVladimir Kondratiev 			       16, 1, sme->ssid, sme->ssid_len, true);
1106b9010f10SAhmad Masri 	if (sme->prev_bssid)
1107b9010f10SAhmad Masri 		wil_info(wil, "  Previous BSSID=%pM\n", sme->prev_bssid);
1108b9010f10SAhmad Masri 	wil_info(wil, "  Auth Type: %s\n",
1109b9010f10SAhmad Masri 		 wil_get_auth_type_name(sme->auth_type));
11108ca26163SVladimir Kondratiev 	wil_info(wil, "  Privacy: %s\n", sme->privacy ? "secure" : "open");
1111eabb03b4SLior David 	wil_info(wil, "  PBSS: %d\n", sme->pbss);
1112feeac225SVladimir Kondratiev 	wil_print_crypto(wil, &sme->crypto);
11138ca26163SVladimir Kondratiev }
11148ca26163SVladimir Kondratiev 
1115b9010f10SAhmad Masri static int wil_ft_connect(struct wiphy *wiphy,
1116b9010f10SAhmad Masri 			  struct net_device *ndev,
1117b9010f10SAhmad Masri 			  struct cfg80211_connect_params *sme)
1118b9010f10SAhmad Masri {
1119b9010f10SAhmad Masri 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1120b9010f10SAhmad Masri 	struct wil6210_vif *vif = ndev_to_vif(ndev);
1121b9010f10SAhmad Masri 	struct wmi_ft_auth_cmd auth_cmd;
1122b9010f10SAhmad Masri 	int rc;
1123b9010f10SAhmad Masri 
1124b9010f10SAhmad Masri 	if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
1125b9010f10SAhmad Masri 		wil_err(wil, "FT: FW does not support FT roaming\n");
1126b9010f10SAhmad Masri 		return -EOPNOTSUPP;
1127b9010f10SAhmad Masri 	}
1128b9010f10SAhmad Masri 
1129b9010f10SAhmad Masri 	if (!sme->prev_bssid) {
1130b9010f10SAhmad Masri 		wil_err(wil, "FT: prev_bssid was not set\n");
1131b9010f10SAhmad Masri 		return -EINVAL;
1132b9010f10SAhmad Masri 	}
1133b9010f10SAhmad Masri 
1134b9010f10SAhmad Masri 	if (ether_addr_equal(sme->prev_bssid, sme->bssid)) {
1135b9010f10SAhmad Masri 		wil_err(wil, "FT: can not roam to same AP\n");
1136b9010f10SAhmad Masri 		return -EINVAL;
1137b9010f10SAhmad Masri 	}
1138b9010f10SAhmad Masri 
1139b9010f10SAhmad Masri 	if (!test_bit(wil_vif_fwconnected, vif->status)) {
1140b9010f10SAhmad Masri 		wil_err(wil, "FT: roam while not connected\n");
1141b9010f10SAhmad Masri 		return -EINVAL;
1142b9010f10SAhmad Masri 	}
1143b9010f10SAhmad Masri 
1144b9010f10SAhmad Masri 	if (vif->privacy != sme->privacy) {
1145b9010f10SAhmad Masri 		wil_err(wil, "FT: privacy mismatch, current (%d) roam (%d)\n",
1146b9010f10SAhmad Masri 			vif->privacy, sme->privacy);
1147b9010f10SAhmad Masri 		return -EINVAL;
1148b9010f10SAhmad Masri 	}
1149b9010f10SAhmad Masri 
1150b9010f10SAhmad Masri 	if (sme->pbss) {
1151b9010f10SAhmad Masri 		wil_err(wil, "FT: roam is not valid for PBSS\n");
1152b9010f10SAhmad Masri 		return -EINVAL;
1153b9010f10SAhmad Masri 	}
1154b9010f10SAhmad Masri 
1155b9010f10SAhmad Masri 	memset(&auth_cmd, 0, sizeof(auth_cmd));
1156b9010f10SAhmad Masri 	auth_cmd.channel = sme->channel->hw_value - 1;
1157b9010f10SAhmad Masri 	ether_addr_copy(auth_cmd.bssid, sme->bssid);
1158b9010f10SAhmad Masri 
1159b9010f10SAhmad Masri 	wil_info(wil, "FT: roaming\n");
1160b9010f10SAhmad Masri 
1161b9010f10SAhmad Masri 	set_bit(wil_vif_ft_roam, vif->status);
1162b9010f10SAhmad Masri 	rc = wmi_send(wil, WMI_FT_AUTH_CMDID, vif->mid,
1163b9010f10SAhmad Masri 		      &auth_cmd, sizeof(auth_cmd));
1164b9010f10SAhmad Masri 	if (rc == 0)
1165b9010f10SAhmad Masri 		mod_timer(&vif->connect_timer,
1166b9010f10SAhmad Masri 			  jiffies + msecs_to_jiffies(5000));
1167b9010f10SAhmad Masri 	else
1168b9010f10SAhmad Masri 		clear_bit(wil_vif_ft_roam, vif->status);
1169b9010f10SAhmad Masri 
1170b9010f10SAhmad Masri 	return rc;
1171b9010f10SAhmad Masri }
1172b9010f10SAhmad Masri 
11739abe3e30SAlexei Avshalom Lazar static int wil_get_wmi_edmg_channel(struct wil6210_priv *wil, u8 edmg_bw_config,
11749abe3e30SAlexei Avshalom Lazar 				    u8 edmg_channels, u8 *wmi_ch)
11759abe3e30SAlexei Avshalom Lazar {
11769abe3e30SAlexei Avshalom Lazar 	if (!edmg_bw_config) {
11779abe3e30SAlexei Avshalom Lazar 		*wmi_ch = 0;
11789abe3e30SAlexei Avshalom Lazar 		return 0;
11799abe3e30SAlexei Avshalom Lazar 	} else if (edmg_bw_config == WIL_EDMG_BW_CONFIGURATION) {
11809abe3e30SAlexei Avshalom Lazar 		/* convert from edmg channel bitmap into edmg channel number */
11819abe3e30SAlexei Avshalom Lazar 		switch (edmg_channels) {
11829abe3e30SAlexei Avshalom Lazar 		case WIL_EDMG_CHANNEL_9_SUBCHANNELS:
11839abe3e30SAlexei Avshalom Lazar 			return wil_spec2wmi_ch(9, wmi_ch);
11849abe3e30SAlexei Avshalom Lazar 		case WIL_EDMG_CHANNEL_10_SUBCHANNELS:
11859abe3e30SAlexei Avshalom Lazar 			return wil_spec2wmi_ch(10, wmi_ch);
11869abe3e30SAlexei Avshalom Lazar 		case WIL_EDMG_CHANNEL_11_SUBCHANNELS:
11879abe3e30SAlexei Avshalom Lazar 			return wil_spec2wmi_ch(11, wmi_ch);
11889abe3e30SAlexei Avshalom Lazar 		default:
11899abe3e30SAlexei Avshalom Lazar 			wil_err(wil, "Unsupported edmg channel bitmap 0x%x\n",
11909abe3e30SAlexei Avshalom Lazar 				edmg_channels);
11919abe3e30SAlexei Avshalom Lazar 			return -EINVAL;
11929abe3e30SAlexei Avshalom Lazar 		}
11939abe3e30SAlexei Avshalom Lazar 	} else {
11949abe3e30SAlexei Avshalom Lazar 		wil_err(wil, "Unsupported EDMG BW configuration %d\n",
11959abe3e30SAlexei Avshalom Lazar 			edmg_bw_config);
11969abe3e30SAlexei Avshalom Lazar 		return -EINVAL;
11979abe3e30SAlexei Avshalom Lazar 	}
11989abe3e30SAlexei Avshalom Lazar }
11999abe3e30SAlexei Avshalom Lazar 
12002be7d22fSVladimir Kondratiev static int wil_cfg80211_connect(struct wiphy *wiphy,
12012be7d22fSVladimir Kondratiev 				struct net_device *ndev,
12022be7d22fSVladimir Kondratiev 				struct cfg80211_connect_params *sme)
12032be7d22fSVladimir Kondratiev {
12042be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1205e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
12062be7d22fSVladimir Kondratiev 	struct cfg80211_bss *bss;
12072be7d22fSVladimir Kondratiev 	struct wmi_connect_cmd conn;
12082be7d22fSVladimir Kondratiev 	const u8 *ssid_eid;
12092be7d22fSVladimir Kondratiev 	const u8 *rsn_eid;
12102be7d22fSVladimir Kondratiev 	int ch;
12112be7d22fSVladimir Kondratiev 	int rc = 0;
1212b9010f10SAhmad Masri 	bool is_ft_roam = false;
1213b9010f10SAhmad Masri 	u8 network_type;
1214eabb03b4SLior David 	enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
12152be7d22fSVladimir Kondratiev 
1216e00243faSLior David 	wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
1217344a7024SVladimir Kondratiev 	wil_print_connect_params(wil, sme);
1218344a7024SVladimir Kondratiev 
1219b9010f10SAhmad Masri 	if (sme->auth_type == NL80211_AUTHTYPE_FT)
1220b9010f10SAhmad Masri 		is_ft_roam = true;
1221b9010f10SAhmad Masri 	if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC &&
1222b9010f10SAhmad Masri 	    test_bit(wil_vif_fwconnected, vif->status))
1223b9010f10SAhmad Masri 		is_ft_roam = true;
1224b9010f10SAhmad Masri 
1225b9010f10SAhmad Masri 	if (!is_ft_roam)
12265bd60982SLior David 		if (test_bit(wil_vif_fwconnecting, vif->status) ||
12275bd60982SLior David 		    test_bit(wil_vif_fwconnected, vif->status))
12284cd9e837SVladimir Kondratiev 			return -EALREADY;
12294cd9e837SVladimir Kondratiev 
1230344a7024SVladimir Kondratiev 	if (sme->ie_len > WMI_MAX_IE_LEN) {
1231344a7024SVladimir Kondratiev 		wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len);
1232344a7024SVladimir Kondratiev 		return -ERANGE;
1233344a7024SVladimir Kondratiev 	}
1234344a7024SVladimir Kondratiev 
1235344a7024SVladimir Kondratiev 	rsn_eid = sme->ie ?
1236344a7024SVladimir Kondratiev 			cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
1237344a7024SVladimir Kondratiev 			NULL;
1238b9010f10SAhmad Masri 	if (sme->privacy && !rsn_eid) {
123927aa6b71SVladimir Kondratiev 		wil_info(wil, "WSC connection\n");
1240b9010f10SAhmad Masri 		if (is_ft_roam) {
1241b9010f10SAhmad Masri 			wil_err(wil, "No WSC with FT roam\n");
1242b9010f10SAhmad Masri 			return -EINVAL;
1243b9010f10SAhmad Masri 		}
1244b9010f10SAhmad Masri 	}
12458ca26163SVladimir Kondratiev 
1246eabb03b4SLior David 	if (sme->pbss)
1247eabb03b4SLior David 		bss_type = IEEE80211_BSS_TYPE_PBSS;
124834d50519SLior David 
12492be7d22fSVladimir Kondratiev 	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
12502be7d22fSVladimir Kondratiev 			       sme->ssid, sme->ssid_len,
1251eabb03b4SLior David 			       bss_type, IEEE80211_PRIVACY_ANY);
12522be7d22fSVladimir Kondratiev 	if (!bss) {
12532be7d22fSVladimir Kondratiev 		wil_err(wil, "Unable to find BSS\n");
12542be7d22fSVladimir Kondratiev 		return -ENOENT;
12552be7d22fSVladimir Kondratiev 	}
12562be7d22fSVladimir Kondratiev 
12572be7d22fSVladimir Kondratiev 	ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
12582be7d22fSVladimir Kondratiev 	if (!ssid_eid) {
12592be7d22fSVladimir Kondratiev 		wil_err(wil, "No SSID\n");
12602be7d22fSVladimir Kondratiev 		rc = -ENOENT;
12612be7d22fSVladimir Kondratiev 		goto out;
12622be7d22fSVladimir Kondratiev 	}
1263e00243faSLior David 	vif->privacy = sme->privacy;
1264e00243faSLior David 	vif->pbss = sme->pbss;
12652be7d22fSVladimir Kondratiev 
1266b9010f10SAhmad Masri 	rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
1267b9010f10SAhmad Masri 	if (rc)
1268b9010f10SAhmad Masri 		goto out;
1269b9010f10SAhmad Masri 
1270b9010f10SAhmad Masri 	switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
1271b9010f10SAhmad Masri 	case WLAN_CAPABILITY_DMG_TYPE_AP:
1272b9010f10SAhmad Masri 		network_type = WMI_NETTYPE_INFRA;
1273b9010f10SAhmad Masri 		break;
1274b9010f10SAhmad Masri 	case WLAN_CAPABILITY_DMG_TYPE_PBSS:
1275b9010f10SAhmad Masri 		network_type = WMI_NETTYPE_P2P;
1276b9010f10SAhmad Masri 		break;
1277b9010f10SAhmad Masri 	default:
1278b9010f10SAhmad Masri 		wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
1279b9010f10SAhmad Masri 			bss->capability);
1280b9010f10SAhmad Masri 		rc = -EINVAL;
1281b9010f10SAhmad Masri 		goto out;
1282b9010f10SAhmad Masri 	}
1283b9010f10SAhmad Masri 
1284b9010f10SAhmad Masri 	ch = bss->channel->hw_value;
1285b9010f10SAhmad Masri 	if (ch == 0) {
1286b9010f10SAhmad Masri 		wil_err(wil, "BSS at unknown frequency %dMhz\n",
1287b9010f10SAhmad Masri 			bss->channel->center_freq);
1288b9010f10SAhmad Masri 		rc = -EOPNOTSUPP;
1289b9010f10SAhmad Masri 		goto out;
1290b9010f10SAhmad Masri 	}
1291b9010f10SAhmad Masri 
1292b9010f10SAhmad Masri 	if (is_ft_roam) {
1293b9010f10SAhmad Masri 		if (network_type != WMI_NETTYPE_INFRA) {
1294b9010f10SAhmad Masri 			wil_err(wil, "FT: Unsupported BSS type, capability= 0x%04x\n",
1295b9010f10SAhmad Masri 				bss->capability);
1296b9010f10SAhmad Masri 			rc = -EINVAL;
1297b9010f10SAhmad Masri 			goto out;
1298b9010f10SAhmad Masri 		}
1299b9010f10SAhmad Masri 		rc = wil_ft_connect(wiphy, ndev, sme);
1300b9010f10SAhmad Masri 		if (rc == 0)
1301b9010f10SAhmad Masri 			vif->bss = bss;
1302b9010f10SAhmad Masri 		goto out;
1303b9010f10SAhmad Masri 	}
1304b9010f10SAhmad Masri 
1305e00243faSLior David 	if (vif->privacy) {
1306230d8442SVladimir Kondratiev 		/* For secure assoc, remove old keys */
1307e00243faSLior David 		rc = wmi_del_cipher_key(vif, 0, bss->bssid,
1308230d8442SVladimir Kondratiev 					WMI_KEY_USE_PAIRWISE);
13092be7d22fSVladimir Kondratiev 		if (rc) {
1310230d8442SVladimir Kondratiev 			wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
1311230d8442SVladimir Kondratiev 			goto out;
1312230d8442SVladimir Kondratiev 		}
1313e00243faSLior David 		rc = wmi_del_cipher_key(vif, 0, bss->bssid,
1314230d8442SVladimir Kondratiev 					WMI_KEY_USE_RX_GROUP);
1315230d8442SVladimir Kondratiev 		if (rc) {
1316230d8442SVladimir Kondratiev 			wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
13172be7d22fSVladimir Kondratiev 			goto out;
13182be7d22fSVladimir Kondratiev 		}
1319ac4acdb7SVladimir Kondratiev 	}
1320ac4acdb7SVladimir Kondratiev 
13212be7d22fSVladimir Kondratiev 	/* WMI_CONNECT_CMD */
13222be7d22fSVladimir Kondratiev 	memset(&conn, 0, sizeof(conn));
1323b9010f10SAhmad Masri 	conn.network_type = network_type;
1324e00243faSLior David 	if (vif->privacy) {
132527aa6b71SVladimir Kondratiev 		if (rsn_eid) { /* regular secure connection */
13262be7d22fSVladimir Kondratiev 			conn.dot11_auth_mode = WMI_AUTH11_SHARED;
13272be7d22fSVladimir Kondratiev 			conn.auth_mode = WMI_AUTH_WPA2_PSK;
13282be7d22fSVladimir Kondratiev 			conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
13292be7d22fSVladimir Kondratiev 			conn.pairwise_crypto_len = 16;
1330230d8442SVladimir Kondratiev 			conn.group_crypto_type = WMI_CRYPT_AES_GCMP;
1331230d8442SVladimir Kondratiev 			conn.group_crypto_len = 16;
133227aa6b71SVladimir Kondratiev 		} else { /* WSC */
133327aa6b71SVladimir Kondratiev 			conn.dot11_auth_mode = WMI_AUTH11_WSC;
133427aa6b71SVladimir Kondratiev 			conn.auth_mode = WMI_AUTH_NONE;
133527aa6b71SVladimir Kondratiev 		}
133627aa6b71SVladimir Kondratiev 	} else { /* insecure connection */
13372be7d22fSVladimir Kondratiev 		conn.dot11_auth_mode = WMI_AUTH11_OPEN;
13382be7d22fSVladimir Kondratiev 		conn.auth_mode = WMI_AUTH_NONE;
13392be7d22fSVladimir Kondratiev 	}
13402be7d22fSVladimir Kondratiev 
13412be7d22fSVladimir Kondratiev 	conn.ssid_len = min_t(u8, ssid_eid[1], 32);
13422be7d22fSVladimir Kondratiev 	memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
13432be7d22fSVladimir Kondratiev 	conn.channel = ch - 1;
13442be7d22fSVladimir Kondratiev 
13459abe3e30SAlexei Avshalom Lazar 	rc = wil_get_wmi_edmg_channel(wil, sme->edmg.bw_config,
13469abe3e30SAlexei Avshalom Lazar 				      sme->edmg.channels, &conn.edmg_channel);
13479abe3e30SAlexei Avshalom Lazar 	if (rc < 0)
13489abe3e30SAlexei Avshalom Lazar 		return rc;
13499abe3e30SAlexei Avshalom Lazar 
1350a82553bbSVladimir Kondratiev 	ether_addr_copy(conn.bssid, bss->bssid);
1351a82553bbSVladimir Kondratiev 	ether_addr_copy(conn.dst_mac, bss->bssid);
1352e83eb2fcSVladimir Kondratiev 
13535bd60982SLior David 	set_bit(wil_vif_fwconnecting, vif->status);
13542be7d22fSVladimir Kondratiev 
1355e00243faSLior David 	rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn));
13562be7d22fSVladimir Kondratiev 	if (rc == 0) {
1357c5e96c91SDedy Lansky 		netif_carrier_on(ndev);
13585bd60982SLior David 		if (!wil_has_other_active_ifaces(wil, ndev, false, true))
13599953a782SLior David 			wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
1360e00243faSLior David 		vif->bss = bss;
13612be7d22fSVladimir Kondratiev 		/* Connect can take lots of time */
1362e00243faSLior David 		mod_timer(&vif->connect_timer,
1363d83ad4c4SLior David 			  jiffies + msecs_to_jiffies(5000));
1364b338f74eSVladimir Kondratiev 	} else {
13655bd60982SLior David 		clear_bit(wil_vif_fwconnecting, vif->status);
13662be7d22fSVladimir Kondratiev 	}
13672be7d22fSVladimir Kondratiev 
13682be7d22fSVladimir Kondratiev  out:
13695b112d3dSJohannes Berg 	cfg80211_put_bss(wiphy, bss);
13702be7d22fSVladimir Kondratiev 
13712be7d22fSVladimir Kondratiev 	return rc;
13722be7d22fSVladimir Kondratiev }
13732be7d22fSVladimir Kondratiev 
13742be7d22fSVladimir Kondratiev static int wil_cfg80211_disconnect(struct wiphy *wiphy,
13752be7d22fSVladimir Kondratiev 				   struct net_device *ndev,
13762be7d22fSVladimir Kondratiev 				   u16 reason_code)
13772be7d22fSVladimir Kondratiev {
13782be7d22fSVladimir Kondratiev 	int rc;
13792be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1380e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
13812be7d22fSVladimir Kondratiev 
1382e00243faSLior David 	wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n",
1383e00243faSLior David 		     reason_code, vif->mid);
1384de9084efSVladimir Kondratiev 
13855bd60982SLior David 	if (!(test_bit(wil_vif_fwconnecting, vif->status) ||
13865bd60982SLior David 	      test_bit(wil_vif_fwconnected, vif->status))) {
1387af3db60aSLazar Alexei 		wil_err(wil, "Disconnect was called while disconnected\n");
138878771d76SVladimir Kondratiev 		return 0;
138978771d76SVladimir Kondratiev 	}
139078771d76SVladimir Kondratiev 
1391e00243faSLior David 	vif->locally_generated_disc = true;
1392e00243faSLior David 	rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0,
139378771d76SVladimir Kondratiev 		      WMI_DISCONNECT_EVENTID, NULL, 0,
139478771d76SVladimir Kondratiev 		      WIL6210_DISCONNECT_TO_MS);
139578771d76SVladimir Kondratiev 	if (rc)
1396af3db60aSLazar Alexei 		wil_err(wil, "disconnect error %d\n", rc);
13972be7d22fSVladimir Kondratiev 
13982be7d22fSVladimir Kondratiev 	return rc;
13992be7d22fSVladimir Kondratiev }
14002be7d22fSVladimir Kondratiev 
14013fea18d0SLior David static int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
14023fea18d0SLior David {
14033fea18d0SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
14043fea18d0SLior David 	int rc;
14053fea18d0SLior David 
14063fea18d0SLior David 	/* these parameters are explicitly not supported */
14073fea18d0SLior David 	if (changed & (WIPHY_PARAM_RETRY_LONG |
14083fea18d0SLior David 		       WIPHY_PARAM_FRAG_THRESHOLD |
14093fea18d0SLior David 		       WIPHY_PARAM_RTS_THRESHOLD))
14103fea18d0SLior David 		return -ENOTSUPP;
14113fea18d0SLior David 
14123fea18d0SLior David 	if (changed & WIPHY_PARAM_RETRY_SHORT) {
14133fea18d0SLior David 		rc = wmi_set_mgmt_retry(wil, wiphy->retry_short);
14143fea18d0SLior David 		if (rc)
14153fea18d0SLior David 			return rc;
14163fea18d0SLior David 	}
14173fea18d0SLior David 
14183fea18d0SLior David 	return 0;
14193fea18d0SLior David }
14203fea18d0SLior David 
14210b39aaf2SVladimir Kondratiev int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
14221647f12fSVladimir Kondratiev 			 struct cfg80211_mgmt_tx_params *params,
14231647f12fSVladimir Kondratiev 			 u64 *cookie)
14241647f12fSVladimir Kondratiev {
14251647f12fSVladimir Kondratiev 	const u8 *buf = params->buf;
14261c21cc5fSDedy Lansky 	size_t len = params->len;
14271647f12fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1428e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
14291647f12fSVladimir Kondratiev 	int rc;
14301c21cc5fSDedy Lansky 	bool tx_status;
14311647f12fSVladimir Kondratiev 
1432b698e2dfSAhmad Masri 	wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
1433b698e2dfSAhmad Masri 		     params->chan ? params->chan->hw_value : -1,
1434b698e2dfSAhmad Masri 		     params->offchan,
1435b698e2dfSAhmad Masri 		     params->wait);
1436b698e2dfSAhmad Masri 
1437b698e2dfSAhmad Masri 	/* Note, currently we support the "wait" parameter only on AP mode.
1438b698e2dfSAhmad Masri 	 * In other modes, user-space must call remain_on_channel before
1439b698e2dfSAhmad Masri 	 * mgmt_tx or listen on a channel other than active one.
1440e6d68341SDedy Lansky 	 */
1441e6d68341SDedy Lansky 
1442b698e2dfSAhmad Masri 	if (params->chan && params->chan->hw_value == 0) {
1443b698e2dfSAhmad Masri 		wil_err(wil, "invalid channel\n");
1444b698e2dfSAhmad Masri 		return -EINVAL;
1445b698e2dfSAhmad Masri 	}
1446e6d68341SDedy Lansky 
1447b698e2dfSAhmad Masri 	if (wdev->iftype != NL80211_IFTYPE_AP) {
1448b698e2dfSAhmad Masri 		wil_dbg_misc(wil,
1449b698e2dfSAhmad Masri 			     "send WMI_SW_TX_REQ_CMDID on non-AP interfaces\n");
1450b698e2dfSAhmad Masri 		rc = wmi_mgmt_tx(vif, buf, len);
1451b698e2dfSAhmad Masri 		goto out;
1452b698e2dfSAhmad Masri 	}
1453b698e2dfSAhmad Masri 
1454b698e2dfSAhmad Masri 	if (!params->chan || params->chan->hw_value == vif->channel) {
1455b698e2dfSAhmad Masri 		wil_dbg_misc(wil,
1456b698e2dfSAhmad Masri 			     "send WMI_SW_TX_REQ_CMDID for on-channel\n");
1457b698e2dfSAhmad Masri 		rc = wmi_mgmt_tx(vif, buf, len);
1458b698e2dfSAhmad Masri 		goto out;
1459b698e2dfSAhmad Masri 	}
1460b698e2dfSAhmad Masri 
1461b698e2dfSAhmad Masri 	if (params->offchan == 0) {
1462b698e2dfSAhmad Masri 		wil_err(wil,
1463b698e2dfSAhmad Masri 			"invalid channel params: current %d requested %d, off-channel not allowed\n",
1464b698e2dfSAhmad Masri 			vif->channel, params->chan->hw_value);
1465b698e2dfSAhmad Masri 		return -EBUSY;
1466b698e2dfSAhmad Masri 	}
1467b698e2dfSAhmad Masri 
1468b698e2dfSAhmad Masri 	/* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
1469b698e2dfSAhmad Masri 	rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
1470b698e2dfSAhmad Masri 			     params->wait);
1471b698e2dfSAhmad Masri 
1472b698e2dfSAhmad Masri out:
147349122ec4SLior David 	/* when the sent packet was not acked by receiver(ACK=0), rc will
147449122ec4SLior David 	 * be -EAGAIN. In this case this function needs to return success,
147549122ec4SLior David 	 * the ACK=0 will be reflected in tx_status.
147649122ec4SLior David 	 */
1477b698e2dfSAhmad Masri 	tx_status = (rc == 0);
147849122ec4SLior David 	rc = (rc == -EAGAIN) ? 0 : rc;
1479304464f4SVladimir Kondratiev 	cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
1480304464f4SVladimir Kondratiev 				tx_status, GFP_KERNEL);
1481b698e2dfSAhmad Masri 
14821647f12fSVladimir Kondratiev 	return rc;
14831647f12fSVladimir Kondratiev }
14841647f12fSVladimir Kondratiev 
14852be7d22fSVladimir Kondratiev static int wil_cfg80211_set_channel(struct wiphy *wiphy,
14862be7d22fSVladimir Kondratiev 				    struct cfg80211_chan_def *chandef)
14872be7d22fSVladimir Kondratiev {
14882be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
14892be7d22fSVladimir Kondratiev 
14907d3e4dbeSLior David 	wil->monitor_chandef = *chandef;
14912be7d22fSVladimir Kondratiev 
14922be7d22fSVladimir Kondratiev 	return 0;
14932be7d22fSVladimir Kondratiev }
14942be7d22fSVladimir Kondratiev 
1495e00243faSLior David static enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev,
1496230d8442SVladimir Kondratiev 					       bool pairwise)
1497230d8442SVladimir Kondratiev {
1498e00243faSLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
1499230d8442SVladimir Kondratiev 	enum wmi_key_usage rc;
1500230d8442SVladimir Kondratiev 
1501230d8442SVladimir Kondratiev 	if (pairwise) {
1502230d8442SVladimir Kondratiev 		rc = WMI_KEY_USE_PAIRWISE;
1503230d8442SVladimir Kondratiev 	} else {
1504230d8442SVladimir Kondratiev 		switch (wdev->iftype) {
1505230d8442SVladimir Kondratiev 		case NL80211_IFTYPE_STATION:
1506e6d68341SDedy Lansky 		case NL80211_IFTYPE_P2P_CLIENT:
1507230d8442SVladimir Kondratiev 			rc = WMI_KEY_USE_RX_GROUP;
1508230d8442SVladimir Kondratiev 			break;
1509230d8442SVladimir Kondratiev 		case NL80211_IFTYPE_AP:
1510e6d68341SDedy Lansky 		case NL80211_IFTYPE_P2P_GO:
1511230d8442SVladimir Kondratiev 			rc = WMI_KEY_USE_TX_GROUP;
1512230d8442SVladimir Kondratiev 			break;
1513230d8442SVladimir Kondratiev 		default:
1514230d8442SVladimir Kondratiev 			/* TODO: Rx GTK or Tx GTK? */
1515230d8442SVladimir Kondratiev 			wil_err(wil, "Can't determine GTK type\n");
1516230d8442SVladimir Kondratiev 			rc = WMI_KEY_USE_RX_GROUP;
1517230d8442SVladimir Kondratiev 			break;
1518230d8442SVladimir Kondratiev 		}
1519230d8442SVladimir Kondratiev 	}
1520af3db60aSLazar Alexei 	wil_dbg_misc(wil, "detect_key_usage: -> %s\n", key_usage_str[rc]);
1521230d8442SVladimir Kondratiev 
1522230d8442SVladimir Kondratiev 	return rc;
1523230d8442SVladimir Kondratiev }
1524230d8442SVladimir Kondratiev 
152574b6ac58SMaya Erez static struct wil_sta_info *
1526e00243faSLior David wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid,
152758527421SVladimir Kondratiev 			  enum wmi_key_usage key_usage, const u8 *mac_addr)
152858527421SVladimir Kondratiev {
152958527421SVladimir Kondratiev 	int cid = -EINVAL;
153058527421SVladimir Kondratiev 
153158527421SVladimir Kondratiev 	if (key_usage == WMI_KEY_USE_TX_GROUP)
153258527421SVladimir Kondratiev 		return NULL; /* not needed */
153358527421SVladimir Kondratiev 
153458527421SVladimir Kondratiev 	/* supplicant provides Rx group key in STA mode with NULL MAC address */
153558527421SVladimir Kondratiev 	if (mac_addr)
1536e00243faSLior David 		cid = wil_find_cid(wil, mid, mac_addr);
153758527421SVladimir Kondratiev 	else if (key_usage == WMI_KEY_USE_RX_GROUP)
1538e00243faSLior David 		cid = wil_find_cid_by_idx(wil, mid, 0);
153958527421SVladimir Kondratiev 	if (cid < 0) {
154074b6ac58SMaya Erez 		wil_err(wil, "No CID for %pM %s\n", mac_addr,
154174b6ac58SMaya Erez 			key_usage_str[key_usage]);
154258527421SVladimir Kondratiev 		return ERR_PTR(cid);
154358527421SVladimir Kondratiev 	}
154458527421SVladimir Kondratiev 
154574b6ac58SMaya Erez 	return &wil->sta[cid];
154674b6ac58SMaya Erez }
154758527421SVladimir Kondratiev 
1548b9010f10SAhmad Masri void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
154974b6ac58SMaya Erez 		       struct wil_sta_info *cs,
155074b6ac58SMaya Erez 		       struct key_params *params)
155174b6ac58SMaya Erez {
155274b6ac58SMaya Erez 	struct wil_tid_crypto_rx_single *cc;
155374b6ac58SMaya Erez 	int tid;
155474b6ac58SMaya Erez 
155574b6ac58SMaya Erez 	if (!cs)
155674b6ac58SMaya Erez 		return;
155774b6ac58SMaya Erez 
155874b6ac58SMaya Erez 	switch (key_usage) {
155942fe1e51SAhmad Masri 	case WMI_KEY_USE_STORE_PTK:
156074b6ac58SMaya Erez 	case WMI_KEY_USE_PAIRWISE:
156174b6ac58SMaya Erez 		for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
156274b6ac58SMaya Erez 			cc = &cs->tid_crypto_rx[tid].key_id[key_index];
156374b6ac58SMaya Erez 			if (params->seq)
156474b6ac58SMaya Erez 				memcpy(cc->pn, params->seq,
156574b6ac58SMaya Erez 				       IEEE80211_GCMP_PN_LEN);
156674b6ac58SMaya Erez 			else
156774b6ac58SMaya Erez 				memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
156874b6ac58SMaya Erez 			cc->key_set = true;
156974b6ac58SMaya Erez 		}
157074b6ac58SMaya Erez 		break;
157174b6ac58SMaya Erez 	case WMI_KEY_USE_RX_GROUP:
157274b6ac58SMaya Erez 		cc = &cs->group_crypto_rx.key_id[key_index];
157374b6ac58SMaya Erez 		if (params->seq)
157474b6ac58SMaya Erez 			memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
157574b6ac58SMaya Erez 		else
157674b6ac58SMaya Erez 			memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
157774b6ac58SMaya Erez 		cc->key_set = true;
157874b6ac58SMaya Erez 		break;
157974b6ac58SMaya Erez 	default:
158074b6ac58SMaya Erez 		break;
158174b6ac58SMaya Erez 	}
158274b6ac58SMaya Erez }
158374b6ac58SMaya Erez 
158474b6ac58SMaya Erez static void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage,
158574b6ac58SMaya Erez 			   struct wil_sta_info *cs)
158674b6ac58SMaya Erez {
158774b6ac58SMaya Erez 	struct wil_tid_crypto_rx_single *cc;
158874b6ac58SMaya Erez 	int tid;
158974b6ac58SMaya Erez 
159074b6ac58SMaya Erez 	if (!cs)
159174b6ac58SMaya Erez 		return;
159274b6ac58SMaya Erez 
159374b6ac58SMaya Erez 	switch (key_usage) {
159474b6ac58SMaya Erez 	case WMI_KEY_USE_PAIRWISE:
159574b6ac58SMaya Erez 		for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
159674b6ac58SMaya Erez 			cc = &cs->tid_crypto_rx[tid].key_id[key_index];
159774b6ac58SMaya Erez 			cc->key_set = false;
159874b6ac58SMaya Erez 		}
159974b6ac58SMaya Erez 		break;
160074b6ac58SMaya Erez 	case WMI_KEY_USE_RX_GROUP:
160174b6ac58SMaya Erez 		cc = &cs->group_crypto_rx.key_id[key_index];
160274b6ac58SMaya Erez 		cc->key_set = false;
160374b6ac58SMaya Erez 		break;
160474b6ac58SMaya Erez 	default:
160574b6ac58SMaya Erez 		break;
160674b6ac58SMaya Erez 	}
160758527421SVladimir Kondratiev }
160858527421SVladimir Kondratiev 
16092be7d22fSVladimir Kondratiev static int wil_cfg80211_add_key(struct wiphy *wiphy,
16102be7d22fSVladimir Kondratiev 				struct net_device *ndev,
16112be7d22fSVladimir Kondratiev 				u8 key_index, bool pairwise,
16122be7d22fSVladimir Kondratiev 				const u8 *mac_addr,
16132be7d22fSVladimir Kondratiev 				struct key_params *params)
16142be7d22fSVladimir Kondratiev {
161558527421SVladimir Kondratiev 	int rc;
1616e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
16172be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1618e00243faSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
1619e00243faSLior David 	enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
1620e00243faSLior David 	struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
1621e00243faSLior David 							    key_usage,
162258527421SVladimir Kondratiev 							    mac_addr);
16232be7d22fSVladimir Kondratiev 
162474b6ac58SMaya Erez 	if (!params) {
162574b6ac58SMaya Erez 		wil_err(wil, "NULL params\n");
162674b6ac58SMaya Erez 		return -EINVAL;
162774b6ac58SMaya Erez 	}
162874b6ac58SMaya Erez 
1629af3db60aSLazar Alexei 	wil_dbg_misc(wil, "add_key: %pM %s[%d] PN %*phN\n",
163058527421SVladimir Kondratiev 		     mac_addr, key_usage_str[key_usage], key_index,
163158527421SVladimir Kondratiev 		     params->seq_len, params->seq);
1632db8adcbfSVladimir Kondratiev 
163374b6ac58SMaya Erez 	if (IS_ERR(cs)) {
1634b9010f10SAhmad Masri 		/* in FT, sta info may not be available as add_key may be
1635b9010f10SAhmad Masri 		 * sent by host before FW sends WMI_CONNECT_EVENT
1636b9010f10SAhmad Masri 		 */
1637b9010f10SAhmad Masri 		if (!test_bit(wil_vif_ft_roam, vif->status)) {
1638af3db60aSLazar Alexei 			wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n",
1639af3db60aSLazar Alexei 				mac_addr, key_usage_str[key_usage], key_index,
164058527421SVladimir Kondratiev 				params->seq_len, params->seq);
164158527421SVladimir Kondratiev 			return -EINVAL;
164258527421SVladimir Kondratiev 		}
1643b9010f10SAhmad Masri 	}
164458527421SVladimir Kondratiev 
1645b9010f10SAhmad Masri 	if (!IS_ERR(cs))
164674b6ac58SMaya Erez 		wil_del_rx_key(key_index, key_usage, cs);
164758527421SVladimir Kondratiev 
164858527421SVladimir Kondratiev 	if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
164958527421SVladimir Kondratiev 		wil_err(wil,
1650af3db60aSLazar Alexei 			"Wrong PN len %d, %pM %s[%d] PN %*phN\n",
1651af3db60aSLazar Alexei 			params->seq_len, mac_addr,
165258527421SVladimir Kondratiev 			key_usage_str[key_usage], key_index,
165358527421SVladimir Kondratiev 			params->seq_len, params->seq);
165458527421SVladimir Kondratiev 		return -EINVAL;
165558527421SVladimir Kondratiev 	}
165658527421SVladimir Kondratiev 
165742fe1e51SAhmad Masri 	spin_lock_bh(&wil->eap_lock);
165842fe1e51SAhmad Masri 	if (pairwise && wdev->iftype == NL80211_IFTYPE_STATION &&
165942fe1e51SAhmad Masri 	    (vif->ptk_rekey_state == WIL_REKEY_M3_RECEIVED ||
166042fe1e51SAhmad Masri 	     vif->ptk_rekey_state == WIL_REKEY_WAIT_M4_SENT)) {
166142fe1e51SAhmad Masri 		key_usage = WMI_KEY_USE_STORE_PTK;
166242fe1e51SAhmad Masri 		vif->ptk_rekey_state = WIL_REKEY_WAIT_M4_SENT;
166342fe1e51SAhmad Masri 		wil_dbg_misc(wil, "Store EAPOL key\n");
166442fe1e51SAhmad Masri 	}
166542fe1e51SAhmad Masri 	spin_unlock_bh(&wil->eap_lock);
166642fe1e51SAhmad Masri 
1667e00243faSLior David 	rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
1668230d8442SVladimir Kondratiev 				params->key, key_usage);
1669e41ab937SDedy Lansky 	if (!rc && !IS_ERR(cs)) {
1670e41ab937SDedy Lansky 		/* update local storage used for AP recovery */
1671e41ab937SDedy Lansky 		if (key_usage == WMI_KEY_USE_TX_GROUP && params->key &&
1672e41ab937SDedy Lansky 		    params->key_len <= WMI_MAX_KEY_LEN) {
1673e41ab937SDedy Lansky 			vif->gtk_index = key_index;
1674e41ab937SDedy Lansky 			memcpy(vif->gtk, params->key, params->key_len);
1675e41ab937SDedy Lansky 			vif->gtk_len = params->key_len;
1676e41ab937SDedy Lansky 		}
1677b9010f10SAhmad Masri 		/* in FT set crypto will take place upon receiving
1678b9010f10SAhmad Masri 		 * WMI_RING_EN_EVENTID event
1679b9010f10SAhmad Masri 		 */
168074b6ac58SMaya Erez 		wil_set_crypto_rx(key_index, key_usage, cs, params);
1681e41ab937SDedy Lansky 	}
168258527421SVladimir Kondratiev 
168358527421SVladimir Kondratiev 	return rc;
16842be7d22fSVladimir Kondratiev }
16852be7d22fSVladimir Kondratiev 
16862be7d22fSVladimir Kondratiev static int wil_cfg80211_del_key(struct wiphy *wiphy,
16872be7d22fSVladimir Kondratiev 				struct net_device *ndev,
16882be7d22fSVladimir Kondratiev 				u8 key_index, bool pairwise,
16892be7d22fSVladimir Kondratiev 				const u8 *mac_addr)
16902be7d22fSVladimir Kondratiev {
1691e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
16922be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1693e00243faSLior David 	struct wireless_dev *wdev = vif_to_wdev(vif);
1694e00243faSLior David 	enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
1695e00243faSLior David 	struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
1696e00243faSLior David 							    key_usage,
169758527421SVladimir Kondratiev 							    mac_addr);
16982be7d22fSVladimir Kondratiev 
1699af3db60aSLazar Alexei 	wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr,
170058527421SVladimir Kondratiev 		     key_usage_str[key_usage], key_index);
170158527421SVladimir Kondratiev 
170274b6ac58SMaya Erez 	if (IS_ERR(cs))
1703af3db60aSLazar Alexei 		wil_info(wil, "Not connected, %pM %s[%d]\n",
170458527421SVladimir Kondratiev 			 mac_addr, key_usage_str[key_usage], key_index);
170558527421SVladimir Kondratiev 
170674b6ac58SMaya Erez 	if (!IS_ERR_OR_NULL(cs))
170774b6ac58SMaya Erez 		wil_del_rx_key(key_index, key_usage, cs);
1708db8adcbfSVladimir Kondratiev 
1709e00243faSLior David 	return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage);
17102be7d22fSVladimir Kondratiev }
17112be7d22fSVladimir Kondratiev 
17122be7d22fSVladimir Kondratiev /* Need to be present or wiphy_new() will WARN */
17132be7d22fSVladimir Kondratiev static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
17142be7d22fSVladimir Kondratiev 					struct net_device *ndev,
17152be7d22fSVladimir Kondratiev 					u8 key_index, bool unicast,
17162be7d22fSVladimir Kondratiev 					bool multicast)
17172be7d22fSVladimir Kondratiev {
1718280ab987SLior David 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1719280ab987SLior David 
1720af3db60aSLazar Alexei 	wil_dbg_misc(wil, "set_default_key: entered\n");
17212be7d22fSVladimir Kondratiev 	return 0;
17222be7d22fSVladimir Kondratiev }
17232be7d22fSVladimir Kondratiev 
17241647f12fSVladimir Kondratiev static int wil_remain_on_channel(struct wiphy *wiphy,
17251647f12fSVladimir Kondratiev 				 struct wireless_dev *wdev,
17261647f12fSVladimir Kondratiev 				 struct ieee80211_channel *chan,
17271647f12fSVladimir Kondratiev 				 unsigned int duration,
17281647f12fSVladimir Kondratiev 				 u64 *cookie)
17291647f12fSVladimir Kondratiev {
17301647f12fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
17311647f12fSVladimir Kondratiev 	int rc;
17321647f12fSVladimir Kondratiev 
1733af3db60aSLazar Alexei 	wil_dbg_misc(wil,
1734af3db60aSLazar Alexei 		     "remain_on_channel: center_freq=%d, duration=%d iftype=%d\n",
1735af3db60aSLazar Alexei 		     chan->center_freq, duration, wdev->iftype);
17361647f12fSVladimir Kondratiev 
1737bb6743f7SLior David 	rc = wil_p2p_listen(wil, wdev, duration, chan, cookie);
17381647f12fSVladimir Kondratiev 	return rc;
17391647f12fSVladimir Kondratiev }
17401647f12fSVladimir Kondratiev 
17411647f12fSVladimir Kondratiev static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
17421647f12fSVladimir Kondratiev 					struct wireless_dev *wdev,
17431647f12fSVladimir Kondratiev 					u64 cookie)
17441647f12fSVladimir Kondratiev {
17451647f12fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1746e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
17471647f12fSVladimir Kondratiev 
1748af3db60aSLazar Alexei 	wil_dbg_misc(wil, "cancel_remain_on_channel\n");
17491647f12fSVladimir Kondratiev 
1750e00243faSLior David 	return wil_p2p_cancel_listen(vif, cookie);
17511647f12fSVladimir Kondratiev }
17521647f12fSVladimir Kondratiev 
1753c100c883SLior David /**
1754c100c883SLior David  * find a specific IE in a list of IEs
1755c100c883SLior David  * return a pointer to the beginning of IE in the list
1756c100c883SLior David  * or NULL if not found
1757c100c883SLior David  */
1758c100c883SLior David static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie,
1759c100c883SLior David 				       u16 ie_len)
1760c100c883SLior David {
1761c100c883SLior David 	struct ieee80211_vendor_ie *vie;
1762c100c883SLior David 	u32 oui;
1763c100c883SLior David 
1764c100c883SLior David 	/* IE tag at offset 0, length at offset 1 */
1765c100c883SLior David 	if (ie_len < 2 || 2 + ie[1] > ie_len)
1766c100c883SLior David 		return NULL;
1767c100c883SLior David 
1768c100c883SLior David 	if (ie[0] != WLAN_EID_VENDOR_SPECIFIC)
1769c100c883SLior David 		return cfg80211_find_ie(ie[0], ies, ies_len);
1770c100c883SLior David 
1771c100c883SLior David 	/* make sure there is room for 3 bytes OUI + 1 byte OUI type */
1772c100c883SLior David 	if (ie[1] < 4)
1773c100c883SLior David 		return NULL;
1774c100c883SLior David 	vie = (struct ieee80211_vendor_ie *)ie;
1775c100c883SLior David 	oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2];
1776c100c883SLior David 	return cfg80211_find_vendor_ie(oui, vie->oui_type, ies,
1777c100c883SLior David 				       ies_len);
1778c100c883SLior David }
1779c100c883SLior David 
1780c100c883SLior David /**
1781c100c883SLior David  * merge the IEs in two lists into a single list.
1782c100c883SLior David  * do not include IEs from the second list which exist in the first list.
1783c100c883SLior David  * add only vendor specific IEs from second list to keep
1784c100c883SLior David  * the merged list sorted (since vendor-specific IE has the
1785c100c883SLior David  * highest tag number)
1786c100c883SLior David  * caller must free the allocated memory for merged IEs
1787c100c883SLior David  */
1788c100c883SLior David static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len,
1789c100c883SLior David 					 const u8 *ies2, u16 ies2_len,
1790c100c883SLior David 					 u8 **merged_ies, u16 *merged_len)
1791c100c883SLior David {
1792c100c883SLior David 	u8 *buf, *dpos;
1793c100c883SLior David 	const u8 *spos;
1794c100c883SLior David 
1795de77a53cSAlexei Avshalom Lazar 	if (!ies1)
1796de77a53cSAlexei Avshalom Lazar 		ies1_len = 0;
1797de77a53cSAlexei Avshalom Lazar 
1798de77a53cSAlexei Avshalom Lazar 	if (!ies2)
1799de77a53cSAlexei Avshalom Lazar 		ies2_len = 0;
1800de77a53cSAlexei Avshalom Lazar 
1801c100c883SLior David 	if (ies1_len == 0 && ies2_len == 0) {
1802c100c883SLior David 		*merged_ies = NULL;
1803c100c883SLior David 		*merged_len = 0;
1804c100c883SLior David 		return 0;
1805c100c883SLior David 	}
1806c100c883SLior David 
1807c100c883SLior David 	buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL);
1808c100c883SLior David 	if (!buf)
1809c100c883SLior David 		return -ENOMEM;
1810de77a53cSAlexei Avshalom Lazar 	if (ies1)
1811c100c883SLior David 		memcpy(buf, ies1, ies1_len);
1812c100c883SLior David 	dpos = buf + ies1_len;
1813c100c883SLior David 	spos = ies2;
1814de77a53cSAlexei Avshalom Lazar 	while (spos && (spos + 1 < ies2 + ies2_len)) {
1815c100c883SLior David 		/* IE tag at offset 0, length at offset 1 */
1816c100c883SLior David 		u16 ielen = 2 + spos[1];
1817c100c883SLior David 
1818c100c883SLior David 		if (spos + ielen > ies2 + ies2_len)
1819c100c883SLior David 			break;
1820c100c883SLior David 		if (spos[0] == WLAN_EID_VENDOR_SPECIFIC &&
1821de77a53cSAlexei Avshalom Lazar 		    (!ies1 || !_wil_cfg80211_find_ie(ies1, ies1_len,
1822de77a53cSAlexei Avshalom Lazar 						     spos, ielen))) {
1823c100c883SLior David 			memcpy(dpos, spos, ielen);
1824c100c883SLior David 			dpos += ielen;
1825c100c883SLior David 		}
1826c100c883SLior David 		spos += ielen;
1827c100c883SLior David 	}
1828c100c883SLior David 
1829c100c883SLior David 	*merged_ies = buf;
1830c100c883SLior David 	*merged_len = dpos - buf;
1831c100c883SLior David 	return 0;
1832c100c883SLior David }
1833c100c883SLior David 
1834ca959773SVladimir Kondratiev static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
1835ca959773SVladimir Kondratiev {
18365eb443e9SDedy Lansky 	wil_hex_dump_misc("head     ", DUMP_PREFIX_OFFSET, 16, 1,
18375eb443e9SDedy Lansky 			  b->head, b->head_len, true);
18385eb443e9SDedy Lansky 	wil_hex_dump_misc("tail     ", DUMP_PREFIX_OFFSET, 16, 1,
18395eb443e9SDedy Lansky 			  b->tail, b->tail_len, true);
18405eb443e9SDedy Lansky 	wil_hex_dump_misc("BCON IE  ", DUMP_PREFIX_OFFSET, 16, 1,
18415eb443e9SDedy Lansky 			  b->beacon_ies, b->beacon_ies_len, true);
18425eb443e9SDedy Lansky 	wil_hex_dump_misc("PROBE    ", DUMP_PREFIX_OFFSET, 16, 1,
18435eb443e9SDedy Lansky 			  b->probe_resp, b->probe_resp_len, true);
18445eb443e9SDedy Lansky 	wil_hex_dump_misc("PROBE IE ", DUMP_PREFIX_OFFSET, 16, 1,
18455eb443e9SDedy Lansky 			  b->proberesp_ies, b->proberesp_ies_len, true);
18465eb443e9SDedy Lansky 	wil_hex_dump_misc("ASSOC IE ", DUMP_PREFIX_OFFSET, 16, 1,
18475eb443e9SDedy Lansky 			  b->assocresp_ies, b->assocresp_ies_len, true);
1848ca959773SVladimir Kondratiev }
1849ca959773SVladimir Kondratiev 
185033190ebfSVladimir Kondratiev /* internal functions for device reset and starting AP */
1851b9010f10SAhmad Masri static u8 *
1852b9010f10SAhmad Masri _wil_cfg80211_get_proberesp_ies(const u8 *proberesp, u16 proberesp_len,
1853b9010f10SAhmad Masri 				u16 *ies_len)
1854b9010f10SAhmad Masri {
1855b9010f10SAhmad Masri 	u8 *ies = NULL;
1856b9010f10SAhmad Masri 
1857b9010f10SAhmad Masri 	if (proberesp) {
1858b9010f10SAhmad Masri 		struct ieee80211_mgmt *f =
1859b9010f10SAhmad Masri 			(struct ieee80211_mgmt *)proberesp;
1860b9010f10SAhmad Masri 		size_t hlen = offsetof(struct ieee80211_mgmt,
1861b9010f10SAhmad Masri 				       u.probe_resp.variable);
1862b9010f10SAhmad Masri 
1863b9010f10SAhmad Masri 		ies = f->u.probe_resp.variable;
1864b9010f10SAhmad Masri 		if (ies_len)
1865b9010f10SAhmad Masri 			*ies_len = proberesp_len - hlen;
1866b9010f10SAhmad Masri 	}
1867b9010f10SAhmad Masri 
1868b9010f10SAhmad Masri 	return ies;
1869b9010f10SAhmad Masri }
1870b9010f10SAhmad Masri 
1871e00243faSLior David static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
1872cab5abbfSVladimir Kondratiev 				 struct cfg80211_beacon_data *bcon)
187333190ebfSVladimir Kondratiev {
187433190ebfSVladimir Kondratiev 	int rc;
1875c100c883SLior David 	u16 len = 0, proberesp_len = 0;
1876b9010f10SAhmad Masri 	u8 *ies = NULL, *proberesp;
187733190ebfSVladimir Kondratiev 
1878e41ab937SDedy Lansky 	/* update local storage used for AP recovery */
1879e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, bcon->probe_resp,
1880e41ab937SDedy Lansky 		      bcon->probe_resp_len);
1881e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len,
1882e41ab937SDedy Lansky 		      bcon->proberesp_ies, bcon->proberesp_ies_len);
1883e41ab937SDedy Lansky 	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len,
1884e41ab937SDedy Lansky 		      bcon->assocresp_ies, bcon->assocresp_ies_len);
1885e41ab937SDedy Lansky 
1886b9010f10SAhmad Masri 	proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
1887b9010f10SAhmad Masri 						    bcon->probe_resp_len,
1888b9010f10SAhmad Masri 						    &proberesp_len);
1889c100c883SLior David 	rc = _wil_cfg80211_merge_extra_ies(proberesp,
1890c100c883SLior David 					   proberesp_len,
1891c100c883SLior David 					   bcon->proberesp_ies,
1892c100c883SLior David 					   bcon->proberesp_ies_len,
1893c100c883SLior David 					   &ies, &len);
1894c100c883SLior David 
18955421bf0cSVladimir Kondratiev 	if (rc)
1896c100c883SLior David 		goto out;
189733190ebfSVladimir Kondratiev 
1898e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies);
1899c100c883SLior David 	if (rc)
1900c100c883SLior David 		goto out;
1901c100c883SLior David 
1902c100c883SLior David 	if (bcon->assocresp_ies)
1903e00243faSLior David 		rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP,
1904c100c883SLior David 				bcon->assocresp_ies_len, bcon->assocresp_ies);
1905c100c883SLior David 	else
1906e00243faSLior David 		rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies);
1907cab5abbfSVladimir Kondratiev #if 0 /* to use beacon IE's, remove this #if 0 */
19085421bf0cSVladimir Kondratiev 	if (rc)
1909c100c883SLior David 		goto out;
19105421bf0cSVladimir Kondratiev 
1911e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_BEACON,
1912e00243faSLior David 			bcon->tail_len, bcon->tail);
1913cab5abbfSVladimir Kondratiev #endif
1914c100c883SLior David out:
1915c100c883SLior David 	kfree(ies);
19165421bf0cSVladimir Kondratiev 	return rc;
191733190ebfSVladimir Kondratiev }
191833190ebfSVladimir Kondratiev 
191933190ebfSVladimir Kondratiev static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
192033190ebfSVladimir Kondratiev 				  struct net_device *ndev,
192133190ebfSVladimir Kondratiev 				  const u8 *ssid, size_t ssid_len, u32 privacy,
19229abe3e30SAlexei Avshalom Lazar 				  int bi, u8 chan, u8 wmi_edmg_channel,
1923cab5abbfSVladimir Kondratiev 				  struct cfg80211_beacon_data *bcon,
1924eabb03b4SLior David 				  u8 hidden_ssid, u32 pbss)
192533190ebfSVladimir Kondratiev {
192633190ebfSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1927e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
192833190ebfSVladimir Kondratiev 	int rc;
192933190ebfSVladimir Kondratiev 	struct wireless_dev *wdev = ndev->ieee80211_ptr;
193033190ebfSVladimir Kondratiev 	u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
1931b4944f2cSLior David 	u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO);
1932b9010f10SAhmad Masri 	u16 proberesp_len = 0;
1933b9010f10SAhmad Masri 	u8 *proberesp;
1934b9010f10SAhmad Masri 	bool ft = false;
193533190ebfSVladimir Kondratiev 
1936eabb03b4SLior David 	if (pbss)
1937eabb03b4SLior David 		wmi_nettype = WMI_NETTYPE_P2P;
1938eabb03b4SLior David 
1939e00243faSLior David 	wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go);
1940b4944f2cSLior David 	if (is_go && !pbss) {
1941af3db60aSLazar Alexei 		wil_err(wil, "P2P GO must be in PBSS\n");
1942b4944f2cSLior David 		return -ENOTSUPP;
1943b4944f2cSLior David 	}
1944b4944f2cSLior David 
194533190ebfSVladimir Kondratiev 	wil_set_recovery_state(wil, fw_recovery_idle);
194633190ebfSVladimir Kondratiev 
1947b9010f10SAhmad Masri 	proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
1948b9010f10SAhmad Masri 						    bcon->probe_resp_len,
1949b9010f10SAhmad Masri 						    &proberesp_len);
1950b9010f10SAhmad Masri 	/* check that the probe response IEs has a MDE */
1951b9010f10SAhmad Masri 	if ((proberesp && proberesp_len > 0 &&
1952b9010f10SAhmad Masri 	     cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN,
1953b9010f10SAhmad Masri 			      proberesp,
1954b9010f10SAhmad Masri 			      proberesp_len)))
1955b9010f10SAhmad Masri 		ft = true;
1956b9010f10SAhmad Masri 
1957b9010f10SAhmad Masri 	if (ft) {
1958b9010f10SAhmad Masri 		if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING,
1959b9010f10SAhmad Masri 			      wil->fw_capabilities)) {
1960b9010f10SAhmad Masri 			wil_err(wil, "FW does not support FT roaming\n");
1961b9010f10SAhmad Masri 			return -ENOTSUPP;
1962b9010f10SAhmad Masri 		}
1963b9010f10SAhmad Masri 		set_bit(wil_vif_ft_roam, vif->status);
1964b9010f10SAhmad Masri 	}
1965b9010f10SAhmad Masri 
196633190ebfSVladimir Kondratiev 	mutex_lock(&wil->mutex);
196733190ebfSVladimir Kondratiev 
19683ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
196933190ebfSVladimir Kondratiev 		__wil_down(wil);
197033190ebfSVladimir Kondratiev 		rc = __wil_up(wil);
197133190ebfSVladimir Kondratiev 		if (rc)
197233190ebfSVladimir Kondratiev 			goto out;
19733ada9314SLior David 	}
197433190ebfSVladimir Kondratiev 
1975e00243faSLior David 	rc = wmi_set_ssid(vif, ssid_len, ssid);
197633190ebfSVladimir Kondratiev 	if (rc)
197733190ebfSVladimir Kondratiev 		goto out;
197833190ebfSVladimir Kondratiev 
1979e00243faSLior David 	rc = _wil_cfg80211_set_ies(vif, bcon);
198033190ebfSVladimir Kondratiev 	if (rc)
198133190ebfSVladimir Kondratiev 		goto out;
198233190ebfSVladimir Kondratiev 
1983e00243faSLior David 	vif->privacy = privacy;
1984e00243faSLior David 	vif->channel = chan;
19859abe3e30SAlexei Avshalom Lazar 	vif->wmi_edmg_channel = wmi_edmg_channel;
1986e00243faSLior David 	vif->hidden_ssid = hidden_ssid;
1987e00243faSLior David 	vif->pbss = pbss;
1988e41ab937SDedy Lansky 	vif->bi = bi;
1989e41ab937SDedy Lansky 	memcpy(vif->ssid, ssid, ssid_len);
1990e41ab937SDedy Lansky 	vif->ssid_len = ssid_len;
199133190ebfSVladimir Kondratiev 
199233190ebfSVladimir Kondratiev 	netif_carrier_on(ndev);
19933ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
19949953a782SLior David 		wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
199533190ebfSVladimir Kondratiev 
19969abe3e30SAlexei Avshalom Lazar 	rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, wmi_edmg_channel,
19979abe3e30SAlexei Avshalom Lazar 			   hidden_ssid, is_go);
199833190ebfSVladimir Kondratiev 	if (rc)
199933190ebfSVladimir Kondratiev 		goto err_pcp_start;
200033190ebfSVladimir Kondratiev 
2001e00243faSLior David 	rc = wil_bcast_init(vif);
200233190ebfSVladimir Kondratiev 	if (rc)
200333190ebfSVladimir Kondratiev 		goto err_bcast;
200433190ebfSVladimir Kondratiev 
200533190ebfSVladimir Kondratiev 	goto out; /* success */
200633190ebfSVladimir Kondratiev 
200733190ebfSVladimir Kondratiev err_bcast:
2008e00243faSLior David 	wmi_pcp_stop(vif);
200933190ebfSVladimir Kondratiev err_pcp_start:
201033190ebfSVladimir Kondratiev 	netif_carrier_off(ndev);
20113ada9314SLior David 	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
20129953a782SLior David 		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
201333190ebfSVladimir Kondratiev out:
201433190ebfSVladimir Kondratiev 	mutex_unlock(&wil->mutex);
201533190ebfSVladimir Kondratiev 	return rc;
201633190ebfSVladimir Kondratiev }
201733190ebfSVladimir Kondratiev 
2018e41ab937SDedy Lansky void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
2019e41ab937SDedy Lansky {
2020e41ab937SDedy Lansky 	int rc, i;
2021e41ab937SDedy Lansky 	struct wiphy *wiphy = wil_to_wiphy(wil);
2022e41ab937SDedy Lansky 
2023e4a29bddSAhmad Masri 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
2024e41ab937SDedy Lansky 		struct wil6210_vif *vif = wil->vifs[i];
2025e41ab937SDedy Lansky 		struct net_device *ndev;
2026e41ab937SDedy Lansky 		struct cfg80211_beacon_data bcon = {};
2027e41ab937SDedy Lansky 		struct key_params key_params = {};
2028e41ab937SDedy Lansky 
2029e41ab937SDedy Lansky 		if (!vif || vif->ssid_len == 0)
2030e41ab937SDedy Lansky 			continue;
2031e41ab937SDedy Lansky 
2032e41ab937SDedy Lansky 		ndev = vif_to_ndev(vif);
2033e41ab937SDedy Lansky 		bcon.proberesp_ies = vif->proberesp_ies;
2034e41ab937SDedy Lansky 		bcon.assocresp_ies = vif->assocresp_ies;
2035e41ab937SDedy Lansky 		bcon.probe_resp = vif->proberesp;
2036e41ab937SDedy Lansky 		bcon.proberesp_ies_len = vif->proberesp_ies_len;
2037e41ab937SDedy Lansky 		bcon.assocresp_ies_len = vif->assocresp_ies_len;
2038e41ab937SDedy Lansky 		bcon.probe_resp_len = vif->proberesp_len;
2039e41ab937SDedy Lansky 
2040e41ab937SDedy Lansky 		wil_info(wil,
2041e41ab937SDedy Lansky 			 "AP (vif %d) recovery: privacy %d, bi %d, channel %d, hidden %d, pbss %d\n",
2042e41ab937SDedy Lansky 			 i, vif->privacy, vif->bi, vif->channel,
2043e41ab937SDedy Lansky 			 vif->hidden_ssid, vif->pbss);
2044e41ab937SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2045e41ab937SDedy Lansky 				  vif->ssid, vif->ssid_len, true);
2046e41ab937SDedy Lansky 		rc = _wil_cfg80211_start_ap(wiphy, ndev,
2047e41ab937SDedy Lansky 					    vif->ssid, vif->ssid_len,
2048e41ab937SDedy Lansky 					    vif->privacy, vif->bi,
20499abe3e30SAlexei Avshalom Lazar 					    vif->channel,
20509abe3e30SAlexei Avshalom Lazar 					    vif->wmi_edmg_channel, &bcon,
2051e41ab937SDedy Lansky 					    vif->hidden_ssid, vif->pbss);
2052e41ab937SDedy Lansky 		if (rc) {
2053e41ab937SDedy Lansky 			wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
2054e41ab937SDedy Lansky 			continue;
2055e41ab937SDedy Lansky 		}
2056e41ab937SDedy Lansky 
2057e41ab937SDedy Lansky 		if (!vif->privacy || vif->gtk_len == 0)
2058e41ab937SDedy Lansky 			continue;
2059e41ab937SDedy Lansky 
2060e41ab937SDedy Lansky 		key_params.key = vif->gtk;
2061e41ab937SDedy Lansky 		key_params.key_len = vif->gtk_len;
2062e41ab937SDedy Lansky 		key_params.seq_len = IEEE80211_GCMP_PN_LEN;
2063e41ab937SDedy Lansky 		rc = wil_cfg80211_add_key(wiphy, ndev, vif->gtk_index, false,
2064e41ab937SDedy Lansky 					  NULL, &key_params);
2065e41ab937SDedy Lansky 		if (rc)
2066e41ab937SDedy Lansky 			wil_err(wil, "vif %d recovery add key failed (%d)\n",
2067e41ab937SDedy Lansky 				i, rc);
2068e41ab937SDedy Lansky 	}
2069e41ab937SDedy Lansky }
2070e41ab937SDedy Lansky 
20711bd922fcSVladimir Kondratiev static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
20721bd922fcSVladimir Kondratiev 				      struct net_device *ndev,
20731bd922fcSVladimir Kondratiev 				      struct cfg80211_beacon_data *bcon)
20741bd922fcSVladimir Kondratiev {
20751bd922fcSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2076e41ab937SDedy Lansky 	struct wireless_dev *wdev = ndev->ieee80211_ptr;
2077e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
20781bd922fcSVladimir Kondratiev 	int rc;
207933190ebfSVladimir Kondratiev 	u32 privacy = 0;
20801bd922fcSVladimir Kondratiev 
2081e00243faSLior David 	wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid);
20821e7e5a0dSVladimir Kondratiev 	wil_print_bcon_data(bcon);
20831e7e5a0dSVladimir Kondratiev 
2084c5a157e4SLior David 	if (bcon->tail &&
2085c5a157e4SLior David 	    cfg80211_find_ie(WLAN_EID_RSN, bcon->tail,
2086c5a157e4SLior David 			     bcon->tail_len))
208733190ebfSVladimir Kondratiev 		privacy = 1;
20881bd922fcSVladimir Kondratiev 
2089e41ab937SDedy Lansky 	memcpy(vif->ssid, wdev->ssid, wdev->ssid_len);
2090e41ab937SDedy Lansky 	vif->ssid_len = wdev->ssid_len;
2091e41ab937SDedy Lansky 
209233190ebfSVladimir Kondratiev 	/* in case privacy has changed, need to restart the AP */
2093e00243faSLior David 	if (vif->privacy != privacy) {
209433190ebfSVladimir Kondratiev 		wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
2095e00243faSLior David 			     vif->privacy, privacy);
209633190ebfSVladimir Kondratiev 
2097e41ab937SDedy Lansky 		rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
2098e41ab937SDedy Lansky 					    vif->ssid_len, privacy,
209933190ebfSVladimir Kondratiev 					    wdev->beacon_interval,
21009abe3e30SAlexei Avshalom Lazar 					    vif->channel,
21019abe3e30SAlexei Avshalom Lazar 					    vif->wmi_edmg_channel, bcon,
2102e00243faSLior David 					    vif->hidden_ssid,
2103e00243faSLior David 					    vif->pbss);
210433190ebfSVladimir Kondratiev 	} else {
2105e00243faSLior David 		rc = _wil_cfg80211_set_ies(vif, bcon);
21061bd922fcSVladimir Kondratiev 	}
21071bd922fcSVladimir Kondratiev 
210833190ebfSVladimir Kondratiev 	return rc;
21091bd922fcSVladimir Kondratiev }
21101bd922fcSVladimir Kondratiev 
21112be7d22fSVladimir Kondratiev static int wil_cfg80211_start_ap(struct wiphy *wiphy,
21122be7d22fSVladimir Kondratiev 				 struct net_device *ndev,
21132be7d22fSVladimir Kondratiev 				 struct cfg80211_ap_settings *info)
21142be7d22fSVladimir Kondratiev {
211533190ebfSVladimir Kondratiev 	int rc;
21162be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
21172be7d22fSVladimir Kondratiev 	struct ieee80211_channel *channel = info->chandef.chan;
21182be7d22fSVladimir Kondratiev 	struct cfg80211_beacon_data *bcon = &info->beacon;
2119ca959773SVladimir Kondratiev 	struct cfg80211_crypto_settings *crypto = &info->crypto;
21209abe3e30SAlexei Avshalom Lazar 	u8 wmi_edmg_channel;
21218e52fe30SHamad Kadmany 	u8 hidden_ssid;
21222be7d22fSVladimir Kondratiev 
2123af3db60aSLazar Alexei 	wil_dbg_misc(wil, "start_ap\n");
2124ca959773SVladimir Kondratiev 
21259abe3e30SAlexei Avshalom Lazar 	rc = wil_get_wmi_edmg_channel(wil, info->chandef.edmg.bw_config,
21269abe3e30SAlexei Avshalom Lazar 				      info->chandef.edmg.channels,
21279abe3e30SAlexei Avshalom Lazar 				      &wmi_edmg_channel);
21289abe3e30SAlexei Avshalom Lazar 	if (rc < 0)
21299abe3e30SAlexei Avshalom Lazar 		return rc;
21309abe3e30SAlexei Avshalom Lazar 
21312be7d22fSVladimir Kondratiev 	if (!channel) {
21322be7d22fSVladimir Kondratiev 		wil_err(wil, "AP: No channel???\n");
21332be7d22fSVladimir Kondratiev 		return -EINVAL;
21342be7d22fSVladimir Kondratiev 	}
21352be7d22fSVladimir Kondratiev 
213633190ebfSVladimir Kondratiev 	switch (info->hidden_ssid) {
213733190ebfSVladimir Kondratiev 	case NL80211_HIDDEN_SSID_NOT_IN_USE:
213833190ebfSVladimir Kondratiev 		hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
213933190ebfSVladimir Kondratiev 		break;
214033190ebfSVladimir Kondratiev 
214133190ebfSVladimir Kondratiev 	case NL80211_HIDDEN_SSID_ZERO_LEN:
214233190ebfSVladimir Kondratiev 		hidden_ssid = WMI_HIDDEN_SSID_SEND_EMPTY;
214333190ebfSVladimir Kondratiev 		break;
214433190ebfSVladimir Kondratiev 
214533190ebfSVladimir Kondratiev 	case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
214633190ebfSVladimir Kondratiev 		hidden_ssid = WMI_HIDDEN_SSID_CLEAR;
214733190ebfSVladimir Kondratiev 		break;
214833190ebfSVladimir Kondratiev 
214933190ebfSVladimir Kondratiev 	default:
215033190ebfSVladimir Kondratiev 		wil_err(wil, "AP: Invalid hidden SSID %d\n", info->hidden_ssid);
215133190ebfSVladimir Kondratiev 		return -EOPNOTSUPP;
215233190ebfSVladimir Kondratiev 	}
21537743882dSVladimir Kondratiev 	wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
21542be7d22fSVladimir Kondratiev 		     channel->center_freq, info->privacy ? "secure" : "open");
2155ca959773SVladimir Kondratiev 	wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
2156ca959773SVladimir Kondratiev 		     info->privacy, info->auth_type);
21578e52fe30SHamad Kadmany 	wil_dbg_misc(wil, "Hidden SSID mode: %d\n",
21588e52fe30SHamad Kadmany 		     info->hidden_ssid);
2159ca959773SVladimir Kondratiev 	wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
2160ca959773SVladimir Kondratiev 		     info->dtim_period);
2161eabb03b4SLior David 	wil_dbg_misc(wil, "PBSS %d\n", info->pbss);
21625eb443e9SDedy Lansky 	wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
21635eb443e9SDedy Lansky 			  info->ssid, info->ssid_len, true);
2164ca959773SVladimir Kondratiev 	wil_print_bcon_data(bcon);
2165ca959773SVladimir Kondratiev 	wil_print_crypto(wil, crypto);
21662be7d22fSVladimir Kondratiev 
216733190ebfSVladimir Kondratiev 	rc = _wil_cfg80211_start_ap(wiphy, ndev,
216833190ebfSVladimir Kondratiev 				    info->ssid, info->ssid_len, info->privacy,
216933190ebfSVladimir Kondratiev 				    info->beacon_interval, channel->hw_value,
21709abe3e30SAlexei Avshalom Lazar 				    wmi_edmg_channel, bcon, hidden_ssid,
21719abe3e30SAlexei Avshalom Lazar 				    info->pbss);
2172c33407a8SVladimir Kondratiev 
21732be7d22fSVladimir Kondratiev 	return rc;
21742be7d22fSVladimir Kondratiev }
21752be7d22fSVladimir Kondratiev 
21762be7d22fSVladimir Kondratiev static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
21772be7d22fSVladimir Kondratiev 				struct net_device *ndev)
21782be7d22fSVladimir Kondratiev {
21792be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2180e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(ndev);
21813ada9314SLior David 	bool last;
21822be7d22fSVladimir Kondratiev 
21833ada9314SLior David 	wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid);
2184ca959773SVladimir Kondratiev 
2185c5e96c91SDedy Lansky 	netif_carrier_off(ndev);
21863ada9314SLior David 	last = !wil_has_other_active_ifaces(wil, ndev, false, true);
21873ada9314SLior David 	if (last) {
21889953a782SLior David 		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
2189c33407a8SVladimir Kondratiev 		wil_set_recovery_state(wil, fw_recovery_idle);
2190646d402dSHamad Kadmany 		set_bit(wil_status_resetting, wil->status);
21913ada9314SLior David 	}
2192646d402dSHamad Kadmany 
21939c3bde56SVladimir Kondratiev 	mutex_lock(&wil->mutex);
21949c3bde56SVladimir Kondratiev 
2195e00243faSLior David 	wmi_pcp_stop(vif);
2196b9010f10SAhmad Masri 	clear_bit(wil_vif_ft_roam, vif->status);
2197e41ab937SDedy Lansky 	vif->ssid_len = 0;
2198e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, NULL, 0);
2199e41ab937SDedy Lansky 	wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len, NULL, 0);
2200e41ab937SDedy Lansky 	wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len, NULL, 0);
2201e41ab937SDedy Lansky 	memset(vif->gtk, 0, WMI_MAX_KEY_LEN);
2202e41ab937SDedy Lansky 	vif->gtk_len = 0;
22032be7d22fSVladimir Kondratiev 
22043ada9314SLior David 	if (last)
220573d839aeSVladimir Kondratiev 		__wil_down(wil);
22063ada9314SLior David 	else
22073ada9314SLior David 		wil_bcast_fini(vif);
220873d839aeSVladimir Kondratiev 
22099c3bde56SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
221073d839aeSVladimir Kondratiev 
221132a20d46SDedy Lansky 	return 0;
22122be7d22fSVladimir Kondratiev }
22132be7d22fSVladimir Kondratiev 
2214849a564bSDedy Lansky static int wil_cfg80211_add_station(struct wiphy *wiphy,
2215849a564bSDedy Lansky 				    struct net_device *dev,
2216849a564bSDedy Lansky 				    const u8 *mac,
2217849a564bSDedy Lansky 				    struct station_parameters *params)
2218849a564bSDedy Lansky {
2219e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2220849a564bSDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2221849a564bSDedy Lansky 
2222b9010f10SAhmad Masri 	wil_dbg_misc(wil, "add station %pM aid %d mid %d mask 0x%x set 0x%x\n",
2223b9010f10SAhmad Masri 		     mac, params->aid, vif->mid,
2224b9010f10SAhmad Masri 		     params->sta_flags_mask, params->sta_flags_set);
2225849a564bSDedy Lansky 
2226849a564bSDedy Lansky 	if (!disable_ap_sme) {
2227849a564bSDedy Lansky 		wil_err(wil, "not supported with AP SME enabled\n");
2228849a564bSDedy Lansky 		return -EOPNOTSUPP;
2229849a564bSDedy Lansky 	}
2230849a564bSDedy Lansky 
2231849a564bSDedy Lansky 	if (params->aid > WIL_MAX_DMG_AID) {
2232849a564bSDedy Lansky 		wil_err(wil, "invalid aid\n");
2233849a564bSDedy Lansky 		return -EINVAL;
2234849a564bSDedy Lansky 	}
2235849a564bSDedy Lansky 
2236e00243faSLior David 	return wmi_new_sta(vif, mac, params->aid);
2237849a564bSDedy Lansky }
2238849a564bSDedy Lansky 
22394d55a0a1SVladimir Kondratiev static int wil_cfg80211_del_station(struct wiphy *wiphy,
224089c771e5SJouni Malinen 				    struct net_device *dev,
224189c771e5SJouni Malinen 				    struct station_del_parameters *params)
22424d55a0a1SVladimir Kondratiev {
2243e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
22444d55a0a1SVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2245097638a0SVladimir Kondratiev 
2246e00243faSLior David 	wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n",
2247e00243faSLior David 		     params->mac, params->reason_code, vif->mid);
2248de9084efSVladimir Kondratiev 
2249097638a0SVladimir Kondratiev 	mutex_lock(&wil->mutex);
2250e1b43407SAhmad Masri 	wil6210_disconnect(vif, params->mac, params->reason_code);
2251097638a0SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
2252097638a0SVladimir Kondratiev 
22534d55a0a1SVladimir Kondratiev 	return 0;
22544d55a0a1SVladimir Kondratiev }
22554d55a0a1SVladimir Kondratiev 
2256849a564bSDedy Lansky static int wil_cfg80211_change_station(struct wiphy *wiphy,
2257849a564bSDedy Lansky 				       struct net_device *dev,
2258849a564bSDedy Lansky 				       const u8 *mac,
2259849a564bSDedy Lansky 				       struct station_parameters *params)
2260849a564bSDedy Lansky {
2261e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2262849a564bSDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2263849a564bSDedy Lansky 	int authorize;
2264849a564bSDedy Lansky 	int cid, i;
226510590c6aSGidon Studinski 	struct wil_ring_tx_data *txdata = NULL;
2266849a564bSDedy Lansky 
2267e00243faSLior David 	wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n",
2268e00243faSLior David 		     mac, params->sta_flags_mask, params->sta_flags_set,
2269e00243faSLior David 		     vif->mid);
2270849a564bSDedy Lansky 
2271849a564bSDedy Lansky 	if (!disable_ap_sme) {
2272849a564bSDedy Lansky 		wil_dbg_misc(wil, "not supported with AP SME enabled\n");
2273849a564bSDedy Lansky 		return -EOPNOTSUPP;
2274849a564bSDedy Lansky 	}
2275849a564bSDedy Lansky 
2276849a564bSDedy Lansky 	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2277849a564bSDedy Lansky 		return 0;
2278849a564bSDedy Lansky 
2279e00243faSLior David 	cid = wil_find_cid(wil, vif->mid, mac);
2280849a564bSDedy Lansky 	if (cid < 0) {
2281849a564bSDedy Lansky 		wil_err(wil, "station not found\n");
2282849a564bSDedy Lansky 		return -ENOLINK;
2283849a564bSDedy Lansky 	}
2284849a564bSDedy Lansky 
228510590c6aSGidon Studinski 	for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++)
228610590c6aSGidon Studinski 		if (wil->ring2cid_tid[i][0] == cid) {
228710590c6aSGidon Studinski 			txdata = &wil->ring_tx_data[i];
2288849a564bSDedy Lansky 			break;
2289849a564bSDedy Lansky 		}
2290849a564bSDedy Lansky 
2291849a564bSDedy Lansky 	if (!txdata) {
229210590c6aSGidon Studinski 		wil_err(wil, "ring data not found\n");
2293849a564bSDedy Lansky 		return -ENOLINK;
2294849a564bSDedy Lansky 	}
2295849a564bSDedy Lansky 
2296849a564bSDedy Lansky 	authorize = params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED);
2297849a564bSDedy Lansky 	txdata->dot1x_open = authorize ? 1 : 0;
229810590c6aSGidon Studinski 	wil_dbg_misc(wil, "cid %d ring %d authorize %d\n", cid, i,
2299849a564bSDedy Lansky 		     txdata->dot1x_open);
2300849a564bSDedy Lansky 
2301849a564bSDedy Lansky 	return 0;
2302849a564bSDedy Lansky }
2303849a564bSDedy Lansky 
230440822a90SVladimir Kondratiev /* probe_client handling */
230540822a90SVladimir Kondratiev static void wil_probe_client_handle(struct wil6210_priv *wil,
2306e00243faSLior David 				    struct wil6210_vif *vif,
230740822a90SVladimir Kondratiev 				    struct wil_probe_client_req *req)
230840822a90SVladimir Kondratiev {
2309e00243faSLior David 	struct net_device *ndev = vif_to_ndev(vif);
231040822a90SVladimir Kondratiev 	struct wil_sta_info *sta = &wil->sta[req->cid];
231140822a90SVladimir Kondratiev 	/* assume STA is alive if it is still connected,
231240822a90SVladimir Kondratiev 	 * else FW will disconnect it
231340822a90SVladimir Kondratiev 	 */
231440822a90SVladimir Kondratiev 	bool alive = (sta->status == wil_sta_connected);
231540822a90SVladimir Kondratiev 
2316c4b50cd3SVenkateswara Naralasetty 	cfg80211_probe_status(ndev, sta->addr, req->cookie, alive,
2317c4b50cd3SVenkateswara Naralasetty 			      0, false, GFP_KERNEL);
231840822a90SVladimir Kondratiev }
231940822a90SVladimir Kondratiev 
2320e00243faSLior David static struct list_head *next_probe_client(struct wil6210_vif *vif)
232140822a90SVladimir Kondratiev {
232240822a90SVladimir Kondratiev 	struct list_head *ret = NULL;
232340822a90SVladimir Kondratiev 
2324e00243faSLior David 	mutex_lock(&vif->probe_client_mutex);
232540822a90SVladimir Kondratiev 
2326e00243faSLior David 	if (!list_empty(&vif->probe_client_pending)) {
2327e00243faSLior David 		ret = vif->probe_client_pending.next;
232840822a90SVladimir Kondratiev 		list_del(ret);
232940822a90SVladimir Kondratiev 	}
233040822a90SVladimir Kondratiev 
2331e00243faSLior David 	mutex_unlock(&vif->probe_client_mutex);
233240822a90SVladimir Kondratiev 
233340822a90SVladimir Kondratiev 	return ret;
233440822a90SVladimir Kondratiev }
233540822a90SVladimir Kondratiev 
233640822a90SVladimir Kondratiev void wil_probe_client_worker(struct work_struct *work)
233740822a90SVladimir Kondratiev {
2338e00243faSLior David 	struct wil6210_vif *vif = container_of(work, struct wil6210_vif,
233940822a90SVladimir Kondratiev 					       probe_client_worker);
2340e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
234140822a90SVladimir Kondratiev 	struct wil_probe_client_req *req;
234240822a90SVladimir Kondratiev 	struct list_head *lh;
234340822a90SVladimir Kondratiev 
2344e00243faSLior David 	while ((lh = next_probe_client(vif)) != NULL) {
234540822a90SVladimir Kondratiev 		req = list_entry(lh, struct wil_probe_client_req, list);
234640822a90SVladimir Kondratiev 
2347e00243faSLior David 		wil_probe_client_handle(wil, vif, req);
234840822a90SVladimir Kondratiev 		kfree(req);
234940822a90SVladimir Kondratiev 	}
235040822a90SVladimir Kondratiev }
235140822a90SVladimir Kondratiev 
2352e00243faSLior David void wil_probe_client_flush(struct wil6210_vif *vif)
235340822a90SVladimir Kondratiev {
235440822a90SVladimir Kondratiev 	struct wil_probe_client_req *req, *t;
2355e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
235640822a90SVladimir Kondratiev 
2357af3db60aSLazar Alexei 	wil_dbg_misc(wil, "probe_client_flush\n");
235840822a90SVladimir Kondratiev 
2359e00243faSLior David 	mutex_lock(&vif->probe_client_mutex);
236040822a90SVladimir Kondratiev 
2361e00243faSLior David 	list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) {
236240822a90SVladimir Kondratiev 		list_del(&req->list);
236340822a90SVladimir Kondratiev 		kfree(req);
236440822a90SVladimir Kondratiev 	}
236540822a90SVladimir Kondratiev 
2366e00243faSLior David 	mutex_unlock(&vif->probe_client_mutex);
236740822a90SVladimir Kondratiev }
236840822a90SVladimir Kondratiev 
236940822a90SVladimir Kondratiev static int wil_cfg80211_probe_client(struct wiphy *wiphy,
237040822a90SVladimir Kondratiev 				     struct net_device *dev,
237140822a90SVladimir Kondratiev 				     const u8 *peer, u64 *cookie)
237240822a90SVladimir Kondratiev {
237340822a90SVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2374e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
237540822a90SVladimir Kondratiev 	struct wil_probe_client_req *req;
2376e00243faSLior David 	int cid = wil_find_cid(wil, vif->mid, peer);
237740822a90SVladimir Kondratiev 
2378e00243faSLior David 	wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n",
2379e00243faSLior David 		     peer, cid, vif->mid);
238040822a90SVladimir Kondratiev 
238140822a90SVladimir Kondratiev 	if (cid < 0)
238240822a90SVladimir Kondratiev 		return -ENOLINK;
238340822a90SVladimir Kondratiev 
238440822a90SVladimir Kondratiev 	req = kzalloc(sizeof(*req), GFP_KERNEL);
238540822a90SVladimir Kondratiev 	if (!req)
238640822a90SVladimir Kondratiev 		return -ENOMEM;
238740822a90SVladimir Kondratiev 
238840822a90SVladimir Kondratiev 	req->cid = cid;
238940822a90SVladimir Kondratiev 	req->cookie = cid;
239040822a90SVladimir Kondratiev 
2391e00243faSLior David 	mutex_lock(&vif->probe_client_mutex);
2392e00243faSLior David 	list_add_tail(&req->list, &vif->probe_client_pending);
2393e00243faSLior David 	mutex_unlock(&vif->probe_client_mutex);
239440822a90SVladimir Kondratiev 
239540822a90SVladimir Kondratiev 	*cookie = req->cookie;
2396e00243faSLior David 	queue_work(wil->wq_service, &vif->probe_client_worker);
239740822a90SVladimir Kondratiev 	return 0;
239840822a90SVladimir Kondratiev }
239940822a90SVladimir Kondratiev 
240002beaf1aSVladimir Kondratiev static int wil_cfg80211_change_bss(struct wiphy *wiphy,
240102beaf1aSVladimir Kondratiev 				   struct net_device *dev,
240202beaf1aSVladimir Kondratiev 				   struct bss_parameters *params)
240302beaf1aSVladimir Kondratiev {
240402beaf1aSVladimir Kondratiev 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2405e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
240602beaf1aSVladimir Kondratiev 
240702beaf1aSVladimir Kondratiev 	if (params->ap_isolate >= 0) {
2408e00243faSLior David 		wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n",
2409e00243faSLior David 			     vif->mid, vif->ap_isolate, params->ap_isolate);
2410e00243faSLior David 		vif->ap_isolate = params->ap_isolate;
241102beaf1aSVladimir Kondratiev 	}
241202beaf1aSVladimir Kondratiev 
241302beaf1aSVladimir Kondratiev 	return 0;
241402beaf1aSVladimir Kondratiev }
241502beaf1aSVladimir Kondratiev 
24162c207eb8SMaya Erez static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
24172c207eb8SMaya Erez 				       struct net_device *dev,
24182c207eb8SMaya Erez 				       bool enabled, int timeout)
24192c207eb8SMaya Erez {
24202c207eb8SMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
24212c207eb8SMaya Erez 	enum wmi_ps_profile_type ps_profile;
24222c207eb8SMaya Erez 
24232c207eb8SMaya Erez 	wil_dbg_misc(wil, "enabled=%d, timeout=%d\n",
24242c207eb8SMaya Erez 		     enabled, timeout);
24252c207eb8SMaya Erez 
24262c207eb8SMaya Erez 	if (enabled)
24272c207eb8SMaya Erez 		ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT;
24282c207eb8SMaya Erez 	else
24292c207eb8SMaya Erez 		ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED;
24302c207eb8SMaya Erez 
24318b068c03SLazar Alexei 	return wil_ps_update(wil, ps_profile);
24322c207eb8SMaya Erez }
24332c207eb8SMaya Erez 
2434fe9ee51eSMaya Erez static int wil_cfg80211_suspend(struct wiphy *wiphy,
2435fe9ee51eSMaya Erez 				struct cfg80211_wowlan *wow)
2436fe9ee51eSMaya Erez {
2437fe9ee51eSMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2438fe9ee51eSMaya Erez 	int rc;
2439fe9ee51eSMaya Erez 
2440fe9ee51eSMaya Erez 	/* Setting the wakeup trigger based on wow is TBD */
2441fe9ee51eSMaya Erez 
2442fe9ee51eSMaya Erez 	if (test_bit(wil_status_suspended, wil->status)) {
2443fe9ee51eSMaya Erez 		wil_dbg_pm(wil, "trying to suspend while suspended\n");
2444fe9ee51eSMaya Erez 		return 0;
2445fe9ee51eSMaya Erez 	}
2446fe9ee51eSMaya Erez 
2447fe9ee51eSMaya Erez 	rc = wil_can_suspend(wil, false);
2448fe9ee51eSMaya Erez 	if (rc)
2449fe9ee51eSMaya Erez 		goto out;
2450fe9ee51eSMaya Erez 
2451fe9ee51eSMaya Erez 	wil_dbg_pm(wil, "suspending\n");
2452fe9ee51eSMaya Erez 
2453144a12a6SHamad Kadmany 	mutex_lock(&wil->mutex);
2454404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
2455144a12a6SHamad Kadmany 	wil_p2p_stop_radio_operations(wil);
24565bd60982SLior David 	wil_abort_scan_all_vifs(wil, true);
2457404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
2458144a12a6SHamad Kadmany 	mutex_unlock(&wil->mutex);
2459fe9ee51eSMaya Erez 
2460fe9ee51eSMaya Erez out:
2461fe9ee51eSMaya Erez 	return rc;
2462fe9ee51eSMaya Erez }
2463fe9ee51eSMaya Erez 
2464fe9ee51eSMaya Erez static int wil_cfg80211_resume(struct wiphy *wiphy)
2465fe9ee51eSMaya Erez {
2466fe9ee51eSMaya Erez 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2467fe9ee51eSMaya Erez 
2468fe9ee51eSMaya Erez 	wil_dbg_pm(wil, "resuming\n");
2469fe9ee51eSMaya Erez 
2470fe9ee51eSMaya Erez 	return 0;
2471fe9ee51eSMaya Erez }
2472fe9ee51eSMaya Erez 
2473a5dc6883SDedy Lansky static int
2474a5dc6883SDedy Lansky wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
2475a5dc6883SDedy Lansky 			      struct net_device *dev,
2476a5dc6883SDedy Lansky 			      struct cfg80211_sched_scan_request *request)
2477a5dc6883SDedy Lansky {
2478a5dc6883SDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2479e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2480a5dc6883SDedy Lansky 	int i, rc;
2481a5dc6883SDedy Lansky 
2482e00243faSLior David 	if (vif->mid != 0)
2483e00243faSLior David 		return -EOPNOTSUPP;
2484e00243faSLior David 
2485a5dc6883SDedy Lansky 	wil_dbg_misc(wil,
2486a5dc6883SDedy Lansky 		     "sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
2487a5dc6883SDedy Lansky 		     request->n_ssids, request->ie_len, request->flags);
2488a5dc6883SDedy Lansky 	for (i = 0; i < request->n_ssids; i++) {
2489a5dc6883SDedy Lansky 		wil_dbg_misc(wil, "SSID[%d]:", i);
2490a5dc6883SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2491a5dc6883SDedy Lansky 				  request->ssids[i].ssid,
2492a5dc6883SDedy Lansky 				  request->ssids[i].ssid_len, true);
2493a5dc6883SDedy Lansky 	}
2494a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "channels:");
2495a5dc6883SDedy Lansky 	for (i = 0; i < request->n_channels; i++)
2496a5dc6883SDedy Lansky 		wil_dbg_misc(wil, " %d%s", request->channels[i]->hw_value,
2497a5dc6883SDedy Lansky 			     i == request->n_channels - 1 ? "\n" : "");
2498a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "n_match_sets %d, min_rssi_thold %d, delay %d\n",
2499a5dc6883SDedy Lansky 		     request->n_match_sets, request->min_rssi_thold,
2500a5dc6883SDedy Lansky 		     request->delay);
2501a5dc6883SDedy Lansky 	for (i = 0; i < request->n_match_sets; i++) {
2502a5dc6883SDedy Lansky 		struct cfg80211_match_set *ms = &request->match_sets[i];
2503a5dc6883SDedy Lansky 
2504a5dc6883SDedy Lansky 		wil_dbg_misc(wil, "MATCHSET[%d]: rssi_thold %d\n",
2505a5dc6883SDedy Lansky 			     i, ms->rssi_thold);
2506a5dc6883SDedy Lansky 		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2507a5dc6883SDedy Lansky 				  ms->ssid.ssid,
2508a5dc6883SDedy Lansky 				  ms->ssid.ssid_len, true);
2509a5dc6883SDedy Lansky 	}
2510a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "n_scan_plans %d\n", request->n_scan_plans);
2511a5dc6883SDedy Lansky 	for (i = 0; i < request->n_scan_plans; i++) {
2512a5dc6883SDedy Lansky 		struct cfg80211_sched_scan_plan *sp = &request->scan_plans[i];
2513a5dc6883SDedy Lansky 
2514a5dc6883SDedy Lansky 		wil_dbg_misc(wil, "SCAN PLAN[%d]: interval %d iterations %d\n",
2515a5dc6883SDedy Lansky 			     i, sp->interval, sp->iterations);
2516a5dc6883SDedy Lansky 	}
2517a5dc6883SDedy Lansky 
2518e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
2519e00243faSLior David 			request->ie_len, request->ie);
2520a5dc6883SDedy Lansky 	if (rc)
2521a5dc6883SDedy Lansky 		return rc;
2522a5dc6883SDedy Lansky 	return wmi_start_sched_scan(wil, request);
2523a5dc6883SDedy Lansky }
2524a5dc6883SDedy Lansky 
2525a5dc6883SDedy Lansky static int
2526a5dc6883SDedy Lansky wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
2527a5dc6883SDedy Lansky 			     u64 reqid)
2528a5dc6883SDedy Lansky {
2529a5dc6883SDedy Lansky 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2530e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(dev);
2531a5dc6883SDedy Lansky 	int rc;
2532a5dc6883SDedy Lansky 
2533e00243faSLior David 	if (vif->mid != 0)
2534e00243faSLior David 		return -EOPNOTSUPP;
2535e00243faSLior David 
2536a5dc6883SDedy Lansky 	rc = wmi_stop_sched_scan(wil);
2537a5dc6883SDedy Lansky 	/* device would return error if it thinks PNO is already stopped.
2538a5dc6883SDedy Lansky 	 * ignore the return code so user space and driver gets back in-sync
2539a5dc6883SDedy Lansky 	 */
2540a5dc6883SDedy Lansky 	wil_dbg_misc(wil, "sched scan stopped (%d)\n", rc);
2541a5dc6883SDedy Lansky 
2542a5dc6883SDedy Lansky 	return 0;
2543a5dc6883SDedy Lansky }
2544a5dc6883SDedy Lansky 
2545b9010f10SAhmad Masri static int
2546b9010f10SAhmad Masri wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
2547b9010f10SAhmad Masri 			   struct cfg80211_update_ft_ies_params *ftie)
2548b9010f10SAhmad Masri {
2549b9010f10SAhmad Masri 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2550b9010f10SAhmad Masri 	struct wil6210_vif *vif = ndev_to_vif(dev);
2551b9010f10SAhmad Masri 	struct cfg80211_bss *bss;
2552b9010f10SAhmad Masri 	struct wmi_ft_reassoc_cmd reassoc;
2553b9010f10SAhmad Masri 	int rc = 0;
2554b9010f10SAhmad Masri 
2555b9010f10SAhmad Masri 	wil_dbg_misc(wil, "update ft ies, mid=%d\n", vif->mid);
2556b9010f10SAhmad Masri 	wil_hex_dump_misc("FT IE ", DUMP_PREFIX_OFFSET, 16, 1,
2557b9010f10SAhmad Masri 			  ftie->ie, ftie->ie_len, true);
2558b9010f10SAhmad Masri 
2559b9010f10SAhmad Masri 	if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
2560b9010f10SAhmad Masri 		wil_err(wil, "FW does not support FT roaming\n");
2561b9010f10SAhmad Masri 		return -EOPNOTSUPP;
2562b9010f10SAhmad Masri 	}
2563b9010f10SAhmad Masri 
2564b9010f10SAhmad Masri 	rc = wmi_update_ft_ies(vif, ftie->ie_len, ftie->ie);
2565b9010f10SAhmad Masri 	if (rc)
2566b9010f10SAhmad Masri 		return rc;
2567b9010f10SAhmad Masri 
2568b9010f10SAhmad Masri 	if (!test_bit(wil_vif_ft_roam, vif->status))
2569b9010f10SAhmad Masri 		/* vif is not roaming */
2570b9010f10SAhmad Masri 		return 0;
2571b9010f10SAhmad Masri 
2572b9010f10SAhmad Masri 	/* wil_vif_ft_roam is set. wil_cfg80211_update_ft_ies is used as
2573b9010f10SAhmad Masri 	 * a trigger for reassoc
2574b9010f10SAhmad Masri 	 */
2575b9010f10SAhmad Masri 
2576b9010f10SAhmad Masri 	bss = vif->bss;
2577b9010f10SAhmad Masri 	if (!bss) {
2578b9010f10SAhmad Masri 		wil_err(wil, "FT: bss is NULL\n");
2579b9010f10SAhmad Masri 		return -EINVAL;
2580b9010f10SAhmad Masri 	}
2581b9010f10SAhmad Masri 
2582b9010f10SAhmad Masri 	memset(&reassoc, 0, sizeof(reassoc));
2583b9010f10SAhmad Masri 	ether_addr_copy(reassoc.bssid, bss->bssid);
2584b9010f10SAhmad Masri 
2585b9010f10SAhmad Masri 	rc = wmi_send(wil, WMI_FT_REASSOC_CMDID, vif->mid,
2586b9010f10SAhmad Masri 		      &reassoc, sizeof(reassoc));
2587b9010f10SAhmad Masri 	if (rc)
2588b9010f10SAhmad Masri 		wil_err(wil, "FT: reassoc failed (%d)\n", rc);
2589b9010f10SAhmad Masri 
2590b9010f10SAhmad Masri 	return rc;
2591b9010f10SAhmad Masri }
2592b9010f10SAhmad Masri 
2593b59eb961SBhumika Goyal static const struct cfg80211_ops wil_cfg80211_ops = {
25944332cac1SLior David 	.add_virtual_intf = wil_cfg80211_add_iface,
25954332cac1SLior David 	.del_virtual_intf = wil_cfg80211_del_iface,
25962be7d22fSVladimir Kondratiev 	.scan = wil_cfg80211_scan,
2597035859a5SMaya Erez 	.abort_scan = wil_cfg80211_abort_scan,
25982be7d22fSVladimir Kondratiev 	.connect = wil_cfg80211_connect,
25992be7d22fSVladimir Kondratiev 	.disconnect = wil_cfg80211_disconnect,
26003fea18d0SLior David 	.set_wiphy_params = wil_cfg80211_set_wiphy_params,
26012be7d22fSVladimir Kondratiev 	.change_virtual_intf = wil_cfg80211_change_iface,
26022be7d22fSVladimir Kondratiev 	.get_station = wil_cfg80211_get_station,
2603ef28afdbSVladimir Kondratiev 	.dump_station = wil_cfg80211_dump_station,
26041647f12fSVladimir Kondratiev 	.remain_on_channel = wil_remain_on_channel,
26051647f12fSVladimir Kondratiev 	.cancel_remain_on_channel = wil_cancel_remain_on_channel,
26061647f12fSVladimir Kondratiev 	.mgmt_tx = wil_cfg80211_mgmt_tx,
26072be7d22fSVladimir Kondratiev 	.set_monitor_channel = wil_cfg80211_set_channel,
26082be7d22fSVladimir Kondratiev 	.add_key = wil_cfg80211_add_key,
26092be7d22fSVladimir Kondratiev 	.del_key = wil_cfg80211_del_key,
26102be7d22fSVladimir Kondratiev 	.set_default_key = wil_cfg80211_set_default_key,
26112be7d22fSVladimir Kondratiev 	/* AP mode */
26121bd922fcSVladimir Kondratiev 	.change_beacon = wil_cfg80211_change_beacon,
26132be7d22fSVladimir Kondratiev 	.start_ap = wil_cfg80211_start_ap,
26142be7d22fSVladimir Kondratiev 	.stop_ap = wil_cfg80211_stop_ap,
2615849a564bSDedy Lansky 	.add_station = wil_cfg80211_add_station,
26164d55a0a1SVladimir Kondratiev 	.del_station = wil_cfg80211_del_station,
2617849a564bSDedy Lansky 	.change_station = wil_cfg80211_change_station,
261840822a90SVladimir Kondratiev 	.probe_client = wil_cfg80211_probe_client,
261902beaf1aSVladimir Kondratiev 	.change_bss = wil_cfg80211_change_bss,
26204332cac1SLior David 	/* P2P device */
26214332cac1SLior David 	.start_p2p_device = wil_cfg80211_start_p2p_device,
26224332cac1SLior David 	.stop_p2p_device = wil_cfg80211_stop_p2p_device,
26232c207eb8SMaya Erez 	.set_power_mgmt = wil_cfg80211_set_power_mgmt,
2624fe9ee51eSMaya Erez 	.suspend = wil_cfg80211_suspend,
2625fe9ee51eSMaya Erez 	.resume = wil_cfg80211_resume,
2626a5dc6883SDedy Lansky 	.sched_scan_start = wil_cfg80211_sched_scan_start,
2627a5dc6883SDedy Lansky 	.sched_scan_stop = wil_cfg80211_sched_scan_stop,
2628b9010f10SAhmad Masri 	.update_ft_ies = wil_cfg80211_update_ft_ies,
26292be7d22fSVladimir Kondratiev };
26302be7d22fSVladimir Kondratiev 
26312be7d22fSVladimir Kondratiev static void wil_wiphy_init(struct wiphy *wiphy)
26322be7d22fSVladimir Kondratiev {
26338e52fe30SHamad Kadmany 	wiphy->max_scan_ssids = 1;
263477c91295SVladimir Kondratiev 	wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
2635e6d68341SDedy Lansky 	wiphy->max_remain_on_channel_duration = WIL_MAX_ROC_DURATION_MS;
26362be7d22fSVladimir Kondratiev 	wiphy->max_num_pmkids = 0 /* TODO: */;
26372be7d22fSVladimir Kondratiev 	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
26382be7d22fSVladimir Kondratiev 				 BIT(NL80211_IFTYPE_AP) |
2639e6d68341SDedy Lansky 				 BIT(NL80211_IFTYPE_P2P_CLIENT) |
2640e6d68341SDedy Lansky 				 BIT(NL80211_IFTYPE_P2P_GO) |
26414332cac1SLior David 				 BIT(NL80211_IFTYPE_P2P_DEVICE) |
2642e6d68341SDedy Lansky 				 BIT(NL80211_IFTYPE_MONITOR);
2643849a564bSDedy Lansky 	wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
26442c207eb8SMaya Erez 			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
26452c207eb8SMaya Erez 			WIPHY_FLAG_PS_ON_BY_DEFAULT;
2646849a564bSDedy Lansky 	if (!disable_ap_sme)
2647849a564bSDedy Lansky 		wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
26489cf10d62SVladimir Kondratiev 	dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
26492be7d22fSVladimir Kondratiev 		__func__, wiphy->flags);
26502be7d22fSVladimir Kondratiev 	wiphy->probe_resp_offload =
26512be7d22fSVladimir Kondratiev 		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
26522be7d22fSVladimir Kondratiev 		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
26532be7d22fSVladimir Kondratiev 		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
26542be7d22fSVladimir Kondratiev 
265557fbcce3SJohannes Berg 	wiphy->bands[NL80211_BAND_60GHZ] = &wil_band_60ghz;
26562be7d22fSVladimir Kondratiev 
265730868f5dSDedy Lansky 	/* may change after reading FW capabilities */
2658b8b33a3aSVladimir Kondratiev 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
26592be7d22fSVladimir Kondratiev 
26602be7d22fSVladimir Kondratiev 	wiphy->cipher_suites = wil_cipher_suites;
26612be7d22fSVladimir Kondratiev 	wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
26622be7d22fSVladimir Kondratiev 	wiphy->mgmt_stypes = wil_mgmt_stypes;
2663713c8a29SVladimir Kondratiev 	wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
26640216a895SLior David 
26650216a895SLior David 	wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
26660216a895SLior David 	wiphy->vendor_commands = wil_nl80211_vendor_commands;
2667d1fbf075SMaya Erez 
2668d1fbf075SMaya Erez #ifdef CONFIG_PM
2669d1fbf075SMaya Erez 	wiphy->wowlan = &wil_wowlan_support;
2670d1fbf075SMaya Erez #endif
26712be7d22fSVladimir Kondratiev }
26722be7d22fSVladimir Kondratiev 
26737bfe9e22SLior David int wil_cfg80211_iface_combinations_from_fw(
26747bfe9e22SLior David 	struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc)
26757bfe9e22SLior David {
26767bfe9e22SLior David 	struct wiphy *wiphy = wil_to_wiphy(wil);
26777bfe9e22SLior David 	u32 total_limits = 0;
26787bfe9e22SLior David 	u16 n_combos;
26797bfe9e22SLior David 	const struct wil_fw_concurrency_combo *combo;
26807bfe9e22SLior David 	const struct wil_fw_concurrency_limit *limit;
26817bfe9e22SLior David 	struct ieee80211_iface_combination *iface_combinations;
26827bfe9e22SLior David 	struct ieee80211_iface_limit *iface_limit;
26837bfe9e22SLior David 	int i, j;
26847bfe9e22SLior David 
26857bfe9e22SLior David 	if (wiphy->iface_combinations) {
26867bfe9e22SLior David 		wil_dbg_misc(wil, "iface_combinations already set, skipping\n");
26877bfe9e22SLior David 		return 0;
26887bfe9e22SLior David 	}
26897bfe9e22SLior David 
26907bfe9e22SLior David 	combo = conc->combos;
26917bfe9e22SLior David 	n_combos = le16_to_cpu(conc->n_combos);
26927bfe9e22SLior David 	for (i = 0; i < n_combos; i++) {
26937bfe9e22SLior David 		total_limits += combo->n_limits;
26947bfe9e22SLior David 		limit = combo->limits + combo->n_limits;
26957bfe9e22SLior David 		combo = (struct wil_fw_concurrency_combo *)limit;
26967bfe9e22SLior David 	}
26977bfe9e22SLior David 
26987bfe9e22SLior David 	iface_combinations =
26997bfe9e22SLior David 		kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) +
27007bfe9e22SLior David 			total_limits * sizeof(struct ieee80211_iface_limit),
27017bfe9e22SLior David 			GFP_KERNEL);
27027bfe9e22SLior David 	if (!iface_combinations)
27037bfe9e22SLior David 		return -ENOMEM;
27047bfe9e22SLior David 	iface_limit = (struct ieee80211_iface_limit *)(iface_combinations +
27057bfe9e22SLior David 						       n_combos);
27067bfe9e22SLior David 	combo = conc->combos;
27077bfe9e22SLior David 	for (i = 0; i < n_combos; i++) {
27087bfe9e22SLior David 		iface_combinations[i].max_interfaces = combo->max_interfaces;
27097bfe9e22SLior David 		iface_combinations[i].num_different_channels =
27107bfe9e22SLior David 			combo->n_diff_channels;
27117bfe9e22SLior David 		iface_combinations[i].beacon_int_infra_match =
27127bfe9e22SLior David 			combo->same_bi;
27137bfe9e22SLior David 		iface_combinations[i].n_limits = combo->n_limits;
27147bfe9e22SLior David 		wil_dbg_misc(wil,
27157bfe9e22SLior David 			     "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n",
27167bfe9e22SLior David 			     i, iface_combinations[i].max_interfaces,
27177bfe9e22SLior David 			     iface_combinations[i].num_different_channels,
27187bfe9e22SLior David 			     iface_combinations[i].beacon_int_infra_match);
27197bfe9e22SLior David 		limit = combo->limits;
27207bfe9e22SLior David 		for (j = 0; j < combo->n_limits; j++) {
27217bfe9e22SLior David 			iface_limit[j].max = le16_to_cpu(limit[j].max);
27227bfe9e22SLior David 			iface_limit[j].types = le16_to_cpu(limit[j].types);
27237bfe9e22SLior David 			wil_dbg_misc(wil,
27247bfe9e22SLior David 				     "limit %d: max %d types 0x%x\n", j,
27257bfe9e22SLior David 				     iface_limit[j].max, iface_limit[j].types);
27267bfe9e22SLior David 		}
27277bfe9e22SLior David 		iface_combinations[i].limits = iface_limit;
27287bfe9e22SLior David 		iface_limit += combo->n_limits;
27297bfe9e22SLior David 		limit += combo->n_limits;
27307bfe9e22SLior David 		combo = (struct wil_fw_concurrency_combo *)limit;
27317bfe9e22SLior David 	}
27327bfe9e22SLior David 
27334aebd3bdSLior David 	wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids);
27344aebd3bdSLior David 	wil->max_vifs = conc->n_mids + 1; /* including main interface */
27354aebd3bdSLior David 	if (wil->max_vifs > WIL_MAX_VIFS) {
27364aebd3bdSLior David 		wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n",
27374aebd3bdSLior David 			 WIL_MAX_VIFS, wil->max_vifs);
27384aebd3bdSLior David 		wil->max_vifs = WIL_MAX_VIFS;
27394aebd3bdSLior David 	}
27407bfe9e22SLior David 	wiphy->n_iface_combinations = n_combos;
27417bfe9e22SLior David 	wiphy->iface_combinations = iface_combinations;
27427bfe9e22SLior David 	return 0;
27437bfe9e22SLior David }
27447bfe9e22SLior David 
27459f38f286SLior David struct wil6210_priv *wil_cfg80211_init(struct device *dev)
27462be7d22fSVladimir Kondratiev {
27479f38f286SLior David 	struct wiphy *wiphy;
27489f38f286SLior David 	struct wil6210_priv *wil;
27499f38f286SLior David 	struct ieee80211_channel *ch;
27502be7d22fSVladimir Kondratiev 
27519cf10d62SVladimir Kondratiev 	dev_dbg(dev, "%s()\n", __func__);
27529cf10d62SVladimir Kondratiev 
27539f38f286SLior David 	/* Note: the wireless_dev structure is no longer allocated here.
27549f38f286SLior David 	 * Instead, it is allocated as part of the net_device structure
27559f38f286SLior David 	 * for main interface and each VIF.
27569f38f286SLior David 	 */
27579f38f286SLior David 	wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv));
27589f38f286SLior David 	if (!wiphy)
27592be7d22fSVladimir Kondratiev 		return ERR_PTR(-ENOMEM);
27602be7d22fSVladimir Kondratiev 
27619f38f286SLior David 	set_wiphy_dev(wiphy, dev);
27629f38f286SLior David 	wil_wiphy_init(wiphy);
27639f38f286SLior David 
27649f38f286SLior David 	wil = wiphy_to_wil(wiphy);
27659f38f286SLior David 	wil->wiphy = wiphy;
27669f38f286SLior David 
27679f38f286SLior David 	/* default monitor channel */
27689f38f286SLior David 	ch = wiphy->bands[NL80211_BAND_60GHZ]->channels;
27699f38f286SLior David 	cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT);
27709f38f286SLior David 
27719f38f286SLior David 	return wil;
27722be7d22fSVladimir Kondratiev }
27732be7d22fSVladimir Kondratiev 
27749f38f286SLior David void wil_cfg80211_deinit(struct wil6210_priv *wil)
27752be7d22fSVladimir Kondratiev {
27769f38f286SLior David 	struct wiphy *wiphy = wil_to_wiphy(wil);
27772be7d22fSVladimir Kondratiev 
27789cf10d62SVladimir Kondratiev 	dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
27799cf10d62SVladimir Kondratiev 
27809f38f286SLior David 	if (!wiphy)
27812be7d22fSVladimir Kondratiev 		return;
27822be7d22fSVladimir Kondratiev 
27837bfe9e22SLior David 	kfree(wiphy->iface_combinations);
27847bfe9e22SLior David 	wiphy->iface_combinations = NULL;
27857bfe9e22SLior David 
27869f38f286SLior David 	wiphy_free(wiphy);
27879f38f286SLior David 	/* do not access wil6210_priv after returning from here */
27882be7d22fSVladimir Kondratiev }
27894332cac1SLior David 
27904332cac1SLior David void wil_p2p_wdev_free(struct wil6210_priv *wil)
27914332cac1SLior David {
27924332cac1SLior David 	struct wireless_dev *p2p_wdev;
27934332cac1SLior David 
2794404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
27954332cac1SLior David 	p2p_wdev = wil->p2p_wdev;
27964332cac1SLior David 	wil->p2p_wdev = NULL;
2797e00243faSLior David 	wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
2798404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
2799d35c2b6fSMaya Erez 	if (p2p_wdev) {
28004332cac1SLior David 		cfg80211_unregister_wdev(p2p_wdev);
28015bd60982SLior David 		kfree(p2p_wdev);
28024332cac1SLior David 	}
28034332cac1SLior David }
28040216a895SLior David 
28050216a895SLior David static int wil_rf_sector_status_to_rc(u8 status)
28060216a895SLior David {
28070216a895SLior David 	switch (status) {
28080216a895SLior David 	case WMI_RF_SECTOR_STATUS_SUCCESS:
28090216a895SLior David 		return 0;
28100216a895SLior David 	case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR:
28110216a895SLior David 		return -EINVAL;
28120216a895SLior David 	case WMI_RF_SECTOR_STATUS_BUSY_ERROR:
28130216a895SLior David 		return -EAGAIN;
28140216a895SLior David 	case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR:
28150216a895SLior David 		return -EOPNOTSUPP;
28160216a895SLior David 	default:
28170216a895SLior David 		return -EINVAL;
28180216a895SLior David 	}
28190216a895SLior David }
28200216a895SLior David 
28210216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
28220216a895SLior David 				 struct wireless_dev *wdev,
28230216a895SLior David 				 const void *data, int data_len)
28240216a895SLior David {
28250216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
2826e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
28270216a895SLior David 	int rc;
28280216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
28290216a895SLior David 	u16 sector_index;
28300216a895SLior David 	u8 sector_type;
28310216a895SLior David 	u32 rf_modules_vec;
28320216a895SLior David 	struct wmi_get_rf_sector_params_cmd cmd;
28330216a895SLior David 	struct {
28340216a895SLior David 		struct wmi_cmd_hdr wmi;
28350216a895SLior David 		struct wmi_get_rf_sector_params_done_event evt;
2836807b0860SAlexei Avshalom Lazar 	} __packed reply = {
2837807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
2838807b0860SAlexei Avshalom Lazar 	};
28390216a895SLior David 	struct sk_buff *msg;
28400216a895SLior David 	struct nlattr *nl_cfgs, *nl_cfg;
28410216a895SLior David 	u32 i;
28420216a895SLior David 	struct wmi_rf_sector_info *si;
28430216a895SLior David 
28440216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
28450216a895SLior David 		return -EOPNOTSUPP;
28460216a895SLior David 
28478cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
28488cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
28490216a895SLior David 	if (rc) {
28500216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
28510216a895SLior David 		return rc;
28520216a895SLior David 	}
28530216a895SLior David 
28540216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
28550216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
28560216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) {
28570216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
28580216a895SLior David 		return -EINVAL;
28590216a895SLior David 	}
28600216a895SLior David 
28610216a895SLior David 	sector_index = nla_get_u16(
28620216a895SLior David 		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
28630216a895SLior David 	if (sector_index >= WIL_MAX_RF_SECTORS) {
28640216a895SLior David 		wil_err(wil, "Invalid sector index %d\n", sector_index);
28650216a895SLior David 		return -EINVAL;
28660216a895SLior David 	}
28670216a895SLior David 
28680216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
28690216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
28700216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
28710216a895SLior David 		return -EINVAL;
28720216a895SLior David 	}
28730216a895SLior David 
28740216a895SLior David 	rf_modules_vec = nla_get_u32(
28750216a895SLior David 		tb[QCA_ATTR_DMG_RF_MODULE_MASK]);
28760216a895SLior David 	if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) {
28770216a895SLior David 		wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec);
28780216a895SLior David 		return -EINVAL;
28790216a895SLior David 	}
28800216a895SLior David 
28810216a895SLior David 	cmd.sector_idx = cpu_to_le16(sector_index);
28820216a895SLior David 	cmd.sector_type = sector_type;
28830216a895SLior David 	cmd.rf_modules_vec = rf_modules_vec & 0xFF;
2884e00243faSLior David 	rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid,
2885e00243faSLior David 		      &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
28860216a895SLior David 		      &reply, sizeof(reply),
28870216a895SLior David 		      500);
28880216a895SLior David 	if (rc)
28890216a895SLior David 		return rc;
28900216a895SLior David 	if (reply.evt.status) {
28910216a895SLior David 		wil_err(wil, "get rf sector cfg failed with status %d\n",
28920216a895SLior David 			reply.evt.status);
28930216a895SLior David 		return wil_rf_sector_status_to_rc(reply.evt.status);
28940216a895SLior David 	}
28950216a895SLior David 
28960216a895SLior David 	msg = cfg80211_vendor_cmd_alloc_reply_skb(
28970216a895SLior David 		wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
28980216a895SLior David 	if (!msg)
28990216a895SLior David 		return -ENOMEM;
29000216a895SLior David 
29010216a895SLior David 	if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
29020216a895SLior David 			      le64_to_cpu(reply.evt.tsf),
29030216a895SLior David 			      QCA_ATTR_PAD))
29040216a895SLior David 		goto nla_put_failure;
29050216a895SLior David 
2906ae0be8deSMichal Kubecek 	nl_cfgs = nla_nest_start_noflag(msg, QCA_ATTR_DMG_RF_SECTOR_CFG);
29070216a895SLior David 	if (!nl_cfgs)
29080216a895SLior David 		goto nla_put_failure;
29090216a895SLior David 	for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) {
29100216a895SLior David 		if (!(rf_modules_vec & BIT(i)))
29110216a895SLior David 			continue;
2912ae0be8deSMichal Kubecek 		nl_cfg = nla_nest_start_noflag(msg, i);
29130216a895SLior David 		if (!nl_cfg)
29140216a895SLior David 			goto nla_put_failure;
29150216a895SLior David 		si = &reply.evt.sectors_info[i];
29160216a895SLior David 		if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
29170216a895SLior David 			       i) ||
29180216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
29190216a895SLior David 				le32_to_cpu(si->etype0)) ||
29200216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
29210216a895SLior David 				le32_to_cpu(si->etype1)) ||
29220216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
29230216a895SLior David 				le32_to_cpu(si->etype2)) ||
29240216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
29250216a895SLior David 				le32_to_cpu(si->psh_hi)) ||
29260216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
29270216a895SLior David 				le32_to_cpu(si->psh_lo)) ||
29280216a895SLior David 		    nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
29290216a895SLior David 				le32_to_cpu(si->dtype_swch_off)))
29300216a895SLior David 			goto nla_put_failure;
29310216a895SLior David 		nla_nest_end(msg, nl_cfg);
29320216a895SLior David 	}
29330216a895SLior David 
29340216a895SLior David 	nla_nest_end(msg, nl_cfgs);
29350216a895SLior David 	rc = cfg80211_vendor_cmd_reply(msg);
29360216a895SLior David 	return rc;
29370216a895SLior David nla_put_failure:
29380216a895SLior David 	kfree_skb(msg);
29390216a895SLior David 	return -ENOBUFS;
29400216a895SLior David }
29410216a895SLior David 
29420216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
29430216a895SLior David 				 struct wireless_dev *wdev,
29440216a895SLior David 				 const void *data, int data_len)
29450216a895SLior David {
29460216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
2947e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
29480216a895SLior David 	int rc, tmp;
29490216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
29500216a895SLior David 	struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
29510216a895SLior David 	u16 sector_index, rf_module_index;
29520216a895SLior David 	u8 sector_type;
29530216a895SLior David 	u32 rf_modules_vec = 0;
29540216a895SLior David 	struct wmi_set_rf_sector_params_cmd cmd;
29550216a895SLior David 	struct {
29560216a895SLior David 		struct wmi_cmd_hdr wmi;
29570216a895SLior David 		struct wmi_set_rf_sector_params_done_event evt;
2958807b0860SAlexei Avshalom Lazar 	} __packed reply = {
2959807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
2960807b0860SAlexei Avshalom Lazar 	};
29610216a895SLior David 	struct nlattr *nl_cfg;
29620216a895SLior David 	struct wmi_rf_sector_info *si;
29630216a895SLior David 
29640216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
29650216a895SLior David 		return -EOPNOTSUPP;
29660216a895SLior David 
29678cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
29688cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
29690216a895SLior David 	if (rc) {
29700216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
29710216a895SLior David 		return rc;
29720216a895SLior David 	}
29730216a895SLior David 
29740216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
29750216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
29760216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) {
29770216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
29780216a895SLior David 		return -EINVAL;
29790216a895SLior David 	}
29800216a895SLior David 
29810216a895SLior David 	sector_index = nla_get_u16(
29820216a895SLior David 		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
29830216a895SLior David 	if (sector_index >= WIL_MAX_RF_SECTORS) {
29840216a895SLior David 		wil_err(wil, "Invalid sector index %d\n", sector_index);
29850216a895SLior David 		return -EINVAL;
29860216a895SLior David 	}
29870216a895SLior David 
29880216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
29890216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
29900216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
29910216a895SLior David 		return -EINVAL;
29920216a895SLior David 	}
29930216a895SLior David 
29940216a895SLior David 	memset(&cmd, 0, sizeof(cmd));
29950216a895SLior David 
29960216a895SLior David 	cmd.sector_idx = cpu_to_le16(sector_index);
29970216a895SLior David 	cmd.sector_type = sector_type;
29980216a895SLior David 	nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG],
29990216a895SLior David 			    tmp) {
30008cb08174SJohannes Berg 		rc = nla_parse_nested_deprecated(tb2,
30018cb08174SJohannes Berg 						 QCA_ATTR_DMG_RF_SECTOR_CFG_MAX,
30028cb08174SJohannes Berg 						 nl_cfg,
30038cb08174SJohannes Berg 						 wil_rf_sector_cfg_policy,
30040216a895SLior David 						 NULL);
30050216a895SLior David 		if (rc) {
30060216a895SLior David 			wil_err(wil, "invalid sector cfg\n");
30070216a895SLior David 			return -EINVAL;
30080216a895SLior David 		}
30090216a895SLior David 
30100216a895SLior David 		if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] ||
30110216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] ||
30120216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] ||
30130216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] ||
30140216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] ||
30150216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] ||
30160216a895SLior David 		    !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) {
30170216a895SLior David 			wil_err(wil, "missing cfg params\n");
30180216a895SLior David 			return -EINVAL;
30190216a895SLior David 		}
30200216a895SLior David 
30210216a895SLior David 		rf_module_index = nla_get_u8(
30220216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]);
30230216a895SLior David 		if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) {
30240216a895SLior David 			wil_err(wil, "invalid RF module index %d\n",
30250216a895SLior David 				rf_module_index);
30260216a895SLior David 			return -EINVAL;
30270216a895SLior David 		}
30280216a895SLior David 		rf_modules_vec |= BIT(rf_module_index);
30290216a895SLior David 		si = &cmd.sectors_info[rf_module_index];
30300216a895SLior David 		si->etype0 = cpu_to_le32(nla_get_u32(
30310216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0]));
30320216a895SLior David 		si->etype1 = cpu_to_le32(nla_get_u32(
30330216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1]));
30340216a895SLior David 		si->etype2 = cpu_to_le32(nla_get_u32(
30350216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2]));
30360216a895SLior David 		si->psh_hi = cpu_to_le32(nla_get_u32(
30370216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI]));
30380216a895SLior David 		si->psh_lo = cpu_to_le32(nla_get_u32(
30390216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO]));
30400216a895SLior David 		si->dtype_swch_off = cpu_to_le32(nla_get_u32(
30410216a895SLior David 			tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]));
30420216a895SLior David 	}
30430216a895SLior David 
30440216a895SLior David 	cmd.rf_modules_vec = rf_modules_vec & 0xFF;
3045e00243faSLior David 	rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid,
3046e00243faSLior David 		      &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
30470216a895SLior David 		      &reply, sizeof(reply),
30480216a895SLior David 		      500);
30490216a895SLior David 	if (rc)
30500216a895SLior David 		return rc;
30510216a895SLior David 	return wil_rf_sector_status_to_rc(reply.evt.status);
30520216a895SLior David }
30530216a895SLior David 
30540216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy,
30550216a895SLior David 				      struct wireless_dev *wdev,
30560216a895SLior David 				      const void *data, int data_len)
30570216a895SLior David {
30580216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
3059e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
30600216a895SLior David 	int rc;
30610216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
30620216a895SLior David 	u8 sector_type, mac_addr[ETH_ALEN];
30630216a895SLior David 	int cid = 0;
30640216a895SLior David 	struct wmi_get_selected_rf_sector_index_cmd cmd;
30650216a895SLior David 	struct {
30660216a895SLior David 		struct wmi_cmd_hdr wmi;
30670216a895SLior David 		struct wmi_get_selected_rf_sector_index_done_event evt;
3068807b0860SAlexei Avshalom Lazar 	} __packed reply = {
3069807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3070807b0860SAlexei Avshalom Lazar 	};
30710216a895SLior David 	struct sk_buff *msg;
30720216a895SLior David 
30730216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
30740216a895SLior David 		return -EOPNOTSUPP;
30750216a895SLior David 
30768cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
30778cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
30780216a895SLior David 	if (rc) {
30790216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
30800216a895SLior David 		return rc;
30810216a895SLior David 	}
30820216a895SLior David 
30830216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
30840216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
30850216a895SLior David 		return -EINVAL;
30860216a895SLior David 	}
30870216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
30880216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
30890216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
30900216a895SLior David 		return -EINVAL;
30910216a895SLior David 	}
30920216a895SLior David 
30930216a895SLior David 	if (tb[QCA_ATTR_MAC_ADDR]) {
30940216a895SLior David 		ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
3095e00243faSLior David 		cid = wil_find_cid(wil, vif->mid, mac_addr);
30960216a895SLior David 		if (cid < 0) {
30970216a895SLior David 			wil_err(wil, "invalid MAC address %pM\n", mac_addr);
30980216a895SLior David 			return -ENOENT;
30990216a895SLior David 		}
31000216a895SLior David 	} else {
31015bd60982SLior David 		if (test_bit(wil_vif_fwconnected, vif->status)) {
31020216a895SLior David 			wil_err(wil, "must specify MAC address when connected\n");
31030216a895SLior David 			return -EINVAL;
31040216a895SLior David 		}
31050216a895SLior David 	}
31060216a895SLior David 
31070216a895SLior David 	memset(&cmd, 0, sizeof(cmd));
31080216a895SLior David 	cmd.cid = (u8)cid;
31090216a895SLior David 	cmd.sector_type = sector_type;
3110e00243faSLior David 	rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid,
31110216a895SLior David 		      &cmd, sizeof(cmd),
31120216a895SLior David 		      WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
31130216a895SLior David 		      &reply, sizeof(reply),
31140216a895SLior David 		      500);
31150216a895SLior David 	if (rc)
31160216a895SLior David 		return rc;
31170216a895SLior David 	if (reply.evt.status) {
31180216a895SLior David 		wil_err(wil, "get rf selected sector cfg failed with status %d\n",
31190216a895SLior David 			reply.evt.status);
31200216a895SLior David 		return wil_rf_sector_status_to_rc(reply.evt.status);
31210216a895SLior David 	}
31220216a895SLior David 
31230216a895SLior David 	msg = cfg80211_vendor_cmd_alloc_reply_skb(
31240216a895SLior David 		wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
31250216a895SLior David 	if (!msg)
31260216a895SLior David 		return -ENOMEM;
31270216a895SLior David 
31280216a895SLior David 	if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
31290216a895SLior David 			      le64_to_cpu(reply.evt.tsf),
31300216a895SLior David 			      QCA_ATTR_PAD) ||
31310216a895SLior David 	    nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX,
31320216a895SLior David 			le16_to_cpu(reply.evt.sector_idx)))
31330216a895SLior David 		goto nla_put_failure;
31340216a895SLior David 
31350216a895SLior David 	rc = cfg80211_vendor_cmd_reply(msg);
31360216a895SLior David 	return rc;
31370216a895SLior David nla_put_failure:
31380216a895SLior David 	kfree_skb(msg);
31390216a895SLior David 	return -ENOBUFS;
31400216a895SLior David }
31410216a895SLior David 
31420216a895SLior David static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
3143e00243faSLior David 					  u8 mid, u16 sector_index,
31440216a895SLior David 					  u8 sector_type, u8 cid)
31450216a895SLior David {
31460216a895SLior David 	struct wmi_set_selected_rf_sector_index_cmd cmd;
31470216a895SLior David 	struct {
31480216a895SLior David 		struct wmi_cmd_hdr wmi;
31490216a895SLior David 		struct wmi_set_selected_rf_sector_index_done_event evt;
3150807b0860SAlexei Avshalom Lazar 	} __packed reply = {
3151807b0860SAlexei Avshalom Lazar 		.evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3152807b0860SAlexei Avshalom Lazar 	};
31530216a895SLior David 	int rc;
31540216a895SLior David 
31550216a895SLior David 	memset(&cmd, 0, sizeof(cmd));
31560216a895SLior David 	cmd.sector_idx = cpu_to_le16(sector_index);
31570216a895SLior David 	cmd.sector_type = sector_type;
31580216a895SLior David 	cmd.cid = (u8)cid;
3159e00243faSLior David 	rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid,
31600216a895SLior David 		      &cmd, sizeof(cmd),
31610216a895SLior David 		      WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
31620216a895SLior David 		      &reply, sizeof(reply),
31630216a895SLior David 		      500);
31640216a895SLior David 	if (rc)
31650216a895SLior David 		return rc;
31660216a895SLior David 	return wil_rf_sector_status_to_rc(reply.evt.status);
31670216a895SLior David }
31680216a895SLior David 
31690216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy,
31700216a895SLior David 				      struct wireless_dev *wdev,
31710216a895SLior David 				      const void *data, int data_len)
31720216a895SLior David {
31730216a895SLior David 	struct wil6210_priv *wil = wdev_to_wil(wdev);
3174e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
31750216a895SLior David 	int rc;
31760216a895SLior David 	struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
31770216a895SLior David 	u16 sector_index;
31780216a895SLior David 	u8 sector_type, mac_addr[ETH_ALEN], i;
31790216a895SLior David 	int cid = 0;
31800216a895SLior David 
31810216a895SLior David 	if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
31820216a895SLior David 		return -EOPNOTSUPP;
31830216a895SLior David 
31848cb08174SJohannes Berg 	rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
31858cb08174SJohannes Berg 				  data_len, wil_rf_sector_policy, NULL);
31860216a895SLior David 	if (rc) {
31870216a895SLior David 		wil_err(wil, "Invalid rf sector ATTR\n");
31880216a895SLior David 		return rc;
31890216a895SLior David 	}
31900216a895SLior David 
31910216a895SLior David 	if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
31920216a895SLior David 	    !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
31930216a895SLior David 		wil_err(wil, "Invalid rf sector spec\n");
31940216a895SLior David 		return -EINVAL;
31950216a895SLior David 	}
31960216a895SLior David 
31970216a895SLior David 	sector_index = nla_get_u16(
31980216a895SLior David 		tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
31990216a895SLior David 	if (sector_index >= WIL_MAX_RF_SECTORS &&
32000216a895SLior David 	    sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
32010216a895SLior David 		wil_err(wil, "Invalid sector index %d\n", sector_index);
32020216a895SLior David 		return -EINVAL;
32030216a895SLior David 	}
32040216a895SLior David 
32050216a895SLior David 	sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
32060216a895SLior David 	if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
32070216a895SLior David 		wil_err(wil, "Invalid sector type %d\n", sector_type);
32080216a895SLior David 		return -EINVAL;
32090216a895SLior David 	}
32100216a895SLior David 
32110216a895SLior David 	if (tb[QCA_ATTR_MAC_ADDR]) {
32120216a895SLior David 		ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
32130216a895SLior David 		if (!is_broadcast_ether_addr(mac_addr)) {
3214e00243faSLior David 			cid = wil_find_cid(wil, vif->mid, mac_addr);
32150216a895SLior David 			if (cid < 0) {
32160216a895SLior David 				wil_err(wil, "invalid MAC address %pM\n",
32170216a895SLior David 					mac_addr);
32180216a895SLior David 				return -ENOENT;
32190216a895SLior David 			}
32200216a895SLior David 		} else {
32210216a895SLior David 			if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
32220216a895SLior David 				wil_err(wil, "broadcast MAC valid only with unlocking\n");
32230216a895SLior David 				return -EINVAL;
32240216a895SLior David 			}
32250216a895SLior David 			cid = -1;
32260216a895SLior David 		}
32270216a895SLior David 	} else {
32285bd60982SLior David 		if (test_bit(wil_vif_fwconnected, vif->status)) {
32290216a895SLior David 			wil_err(wil, "must specify MAC address when connected\n");
32300216a895SLior David 			return -EINVAL;
32310216a895SLior David 		}
32320216a895SLior David 		/* otherwise, using cid=0 for unassociated station */
32330216a895SLior David 	}
32340216a895SLior David 
32350216a895SLior David 	if (cid >= 0) {
3236e00243faSLior David 		rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index,
32370216a895SLior David 						    sector_type, cid);
32380216a895SLior David 	} else {
32390216a895SLior David 		/* unlock all cids */
32400216a895SLior David 		rc = wil_rf_sector_wmi_set_selected(
3241e00243faSLior David 			wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX,
3242e00243faSLior David 			sector_type, WIL_CID_ALL);
32430216a895SLior David 		if (rc == -EINVAL) {
3244ddf7afddSAhmad Masri 			for (i = 0; i < wil->max_assoc_sta; i++) {
3245e00243faSLior David 				if (wil->sta[i].mid != vif->mid)
3246e00243faSLior David 					continue;
32470216a895SLior David 				rc = wil_rf_sector_wmi_set_selected(
3248e00243faSLior David 					wil, vif->mid,
3249e00243faSLior David 					WMI_INVALID_RF_SECTOR_INDEX,
32500216a895SLior David 					sector_type, i);
32510216a895SLior David 				/* the FW will silently ignore and return
32520216a895SLior David 				 * success for unused cid, so abort the loop
32530216a895SLior David 				 * on any other error
32540216a895SLior David 				 */
32550216a895SLior David 				if (rc) {
32560216a895SLior David 					wil_err(wil, "unlock cid %d failed with status %d\n",
32570216a895SLior David 						i, rc);
32580216a895SLior David 					break;
32590216a895SLior David 				}
32600216a895SLior David 			}
32610216a895SLior David 		}
32620216a895SLior David 	}
32630216a895SLior David 
32640216a895SLior David 	return rc;
32650216a895SLior David }
3266